#pragma once #include #include 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 void forEachActor(Fn&& fn) const; std::vector actors_; AdjacentActors adjacent_{}; };