Files
projecte_2026/source/game/gameplay/solid_actor_manager.hpp

92 lines
3.8 KiB
C++

#pragma once
#include <SDL3/SDL.h>
#include <vector>
class SolidActor;
/**
* @brief Gestor de actores sólidos dinámicos (AABB) de una habitación.
*
* Paralelo al TileCollider: proporciona sweeps axis-aligned del Player
* contra una lista de AABBs de entidades (puertas, plataformas móviles,
* bloques empujables, ascensores, etc). El manager NO es propietario de
* los actores — sus entidades concretas viven en sus respectivos managers
* (DoorManager, PlatformManager, ...). Este manager solo mantiene una
* lista de raw pointers para consultas de colisión.
*
* Cross-room: igual que CollisionMap::updateBorders, este manager acepta
* punteros a los managers de las rooms adyacentes (setAdjacent). Cuando
* el Player está cerca del borde, los sweeps iteran también sobre los
* actores vecinos, trasladando sus AABBs por ±PlayArea::WIDTH/HEIGHT
* para ponerlos en el sistema de coordenadas de la room actual.
*/
class SolidActorManager {
public:
struct FloorHit {
float y{-1};
SolidActor* carrier{nullptr};
};
struct AdjacentActors {
SolidActorManager* upper{nullptr};
SolidActorManager* lower{nullptr};
SolidActorManager* left{nullptr};
SolidActorManager* right{nullptr};
};
SolidActorManager() = default;
~SolidActorManager() = default;
SolidActorManager(const SolidActorManager&) = delete;
auto operator=(const SolidActorManager&) -> SolidActorManager& = delete;
SolidActorManager(SolidActorManager&&) = delete;
auto operator=(SolidActorManager&&) -> SolidActorManager& = delete;
// Registro de actores (el manager no es propietario)
void registerActor(SolidActor* actor);
void unregisterActor(SolidActor* actor);
void clear();
// Cross-room: punteros a los managers de las rooms adyacentes
void setAdjacent(const AdjacentActors& adjacent);
void clearAdjacent();
// Sweeps del Player (paralelos a TileCollider). Todas las coordenadas
// están en el sistema de la room actual (room-local pixel space).
/// Devuelve el x del borde derecho del primer actor bloqueante cuyo AABB
/// esté "a la izquierda" del Player y solape su rect (px, py, pw, ph).
/// Collision::NONE si no hay colisión.
[[nodiscard]] auto checkWallLeft(float px, float py, float pw, float ph) const -> float;
/// Devuelve el x del borde izquierdo del primer actor bloqueante cuyo AABB
/// esté "a la derecha" del Player y solape su rect. Collision::NONE si no.
[[nodiscard]] auto checkWallRight(float px, float py, float pw, float ph) const -> float;
/// Devuelve el y del borde inferior del primer techo bloqueante que solape
/// la parte superior del Player a (px, py, pw). Collision::NONE si no.
[[nodiscard]] auto checkCeiling(float px, float py, float pw) const -> float;
/// Devuelve el suelo más cercano debajo de los pies del Player dentro del
/// rango vertical [foot_y_cur, foot_y_new]. Los actores ONEWAY_TOP solo
/// colisionan si foot_y_cur <= r.y (el Player venía desde arriba).
/// Si el actor tiene CARRY_ON_TOP, FloorHit.carrier apunta a él.
[[nodiscard]] auto checkFloor(float px, float foot_y_cur, float pw, float foot_y_new) const -> FloorHit;
/// Devuelve true si hay algún actor con top justo debajo de los pies
/// (dentro de 1 px). Usado por checkFalling para no desengancharse de
/// plataformas móviles.
[[nodiscard]] auto hasGroundBelow(float px, float foot_y, float pw) const -> bool;
private:
// Itera sobre todos los actores relevantes (locales + vecinos) aplicando
// el offset correcto a los AABBs vecinos. La lambda recibe (actor, aabb_world).
template <typename Fn>
void forEachActor(Fn&& fn) const;
std::vector<SolidActor*> actors_;
AdjacentActors adjacent_{};
};