migrades portes i plataformes a solidActor
This commit is contained in:
@@ -2,32 +2,31 @@
|
||||
|
||||
#include <utility> // Para std::move
|
||||
|
||||
#include "game/entities/door.hpp" // Para Door
|
||||
#include "game/gameplay/collision_map.hpp" // Para CollisionMap
|
||||
#include "game/gameplay/door_tracker.hpp" // Para DoorTracker
|
||||
#include "game/gameplay/inventory.hpp" // Para Inventory
|
||||
#include "game/gameplay/tile_collider.hpp" // Para TileCollider::Tile
|
||||
#include "utils/defines.hpp" // Para Map::WIDTH, Tile::SIZE
|
||||
#include "utils/utils.hpp" // Para checkCollision
|
||||
#include "game/entities/door.hpp" // Para Door
|
||||
#include "game/gameplay/door_tracker.hpp" // Para DoorTracker
|
||||
#include "game/gameplay/inventory.hpp" // Para Inventory
|
||||
#include "game/gameplay/solid_actor_manager.hpp" // Para SolidActorManager
|
||||
#include "utils/utils.hpp" // Para checkCollision
|
||||
|
||||
// Constructor
|
||||
DoorManager::DoorManager(std::string room_id, CollisionMap* collision_map)
|
||||
DoorManager::DoorManager(std::string room_id, SolidActorManager* solid_actors)
|
||||
: room_id_(std::move(room_id)),
|
||||
collision_map_(collision_map) {
|
||||
solid_actors_(solid_actors) {
|
||||
}
|
||||
|
||||
// Añade una puerta. Si está bloqueante (CLOSED), pinta sus 4 tiles como WALL
|
||||
// en el CollisionMap. Si ya está OPENED (porque venía persistida del
|
||||
// DoorTracker), no se tocan los tiles.
|
||||
// Añade una puerta y la registra en el SolidActorManager. El bit
|
||||
// BLOCKS_PLAYER del propio Door determina si bloquea al Player (se setea en
|
||||
// el constructor si la puerta no arranca ya abierta desde el DoorTracker).
|
||||
void DoorManager::addDoor(std::shared_ptr<Door> door) { // NOLINT(readability-identifier-naming)
|
||||
if (door->isBlocking()) {
|
||||
writeDoorTiles(*door, static_cast<int>(TileCollider::Tile::WALL));
|
||||
}
|
||||
solid_actors_->registerActor(door.get());
|
||||
doors_.push_back(std::move(door));
|
||||
}
|
||||
|
||||
// Elimina todas las puertas
|
||||
// Elimina todas las puertas y las desregistra del SolidActorManager
|
||||
void DoorManager::clear() {
|
||||
for (const auto& door : doors_) {
|
||||
solid_actors_->unregisterActor(door.get());
|
||||
}
|
||||
doors_.clear();
|
||||
}
|
||||
|
||||
@@ -36,9 +35,10 @@ void DoorManager::update(float delta_time) {
|
||||
for (const auto& door : doors_) {
|
||||
door->update(delta_time);
|
||||
|
||||
// Si la puerta acaba de transicionar a OPENED, liberar los tiles y persistir
|
||||
// Si la puerta acaba de transicionar a OPENED, persistir en DoorTracker.
|
||||
// El flag BLOCKS_PLAYER ya se limpió dentro de Door::update(), así que
|
||||
// el sweep del SolidActorManager deja de verla como muro automáticamente.
|
||||
if (door->justOpened()) {
|
||||
writeDoorTiles(*door, static_cast<int>(TileCollider::Tile::EMPTY));
|
||||
DoorTracker::get()->addDoor(room_id_, door->getPos());
|
||||
}
|
||||
}
|
||||
@@ -79,48 +79,17 @@ void DoorManager::tryUnlock(const SDL_FRect& player_rect) {
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Mueve una puerta del editor: limpia los WALLs viejos, reposiciona el sprite,
|
||||
// y reescribe los WALLs nuevos si la puerta sigue siendo bloqueante.
|
||||
// Mueve una puerta del editor: reposiciona su sprite y AABB. El registro
|
||||
// en el SolidActorManager se mantiene (el manager lee el AABB actual).
|
||||
void DoorManager::moveDoor(int index, float x, float y) {
|
||||
if (index < 0 || index >= static_cast<int>(doors_.size())) { return; }
|
||||
auto& door = doors_[index];
|
||||
|
||||
// Limpiar los WALLs viejos antes de mover
|
||||
if (door->isBlocking()) {
|
||||
writeDoorTiles(*door, static_cast<int>(TileCollider::Tile::EMPTY));
|
||||
}
|
||||
|
||||
// Reposicionar el sprite y el collider del Door
|
||||
door->setPosition(x, y);
|
||||
|
||||
// Re-escribir los WALLs nuevos en la nueva posición si sigue siendo bloqueante
|
||||
if (door->isBlocking()) {
|
||||
writeDoorTiles(*door, static_cast<int>(TileCollider::Tile::WALL));
|
||||
}
|
||||
doors_[index]->setPosition(x, y);
|
||||
}
|
||||
|
||||
// Elimina una puerta del editor, limpiando los WALLs antes de borrarla del vector
|
||||
// Elimina una puerta del editor, desregistrándola del SolidActorManager
|
||||
void DoorManager::removeDoor(int index) {
|
||||
if (index < 0 || index >= static_cast<int>(doors_.size())) { return; }
|
||||
auto& door = doors_[index];
|
||||
|
||||
if (door->isBlocking()) {
|
||||
writeDoorTiles(*door, static_cast<int>(TileCollider::Tile::EMPTY));
|
||||
}
|
||||
|
||||
solid_actors_->unregisterActor(doors_[index].get());
|
||||
doors_.erase(doors_.begin() + index);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Setea las 4 celdas que ocupa la puerta (1 col × 4 filas) al valor indicado
|
||||
void DoorManager::writeDoorTiles(const Door& door, int tile_value) {
|
||||
// Convertir posición en píxeles a coordenadas de tile
|
||||
const SDL_FPoint POS = door.getPos();
|
||||
const int COL = static_cast<int>(POS.x) / Tile::SIZE;
|
||||
const int ROW = static_cast<int>(POS.y) / Tile::SIZE;
|
||||
|
||||
for (int i = 0; i < DOOR_TILES_HEIGHT; ++i) {
|
||||
const int INDEX = ((ROW + i) * Map::WIDTH) + COL;
|
||||
collision_map_->setCollisionTile(INDEX, tile_value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#include "game/entities/door.hpp" // Para Door, Door::Data
|
||||
|
||||
class CollisionMap;
|
||||
class SolidActorManager;
|
||||
|
||||
/**
|
||||
* @brief Gestor de puertas de una habitación
|
||||
@@ -18,14 +18,14 @@ class CollisionMap;
|
||||
* - Actualizar y renderizar todas las puertas
|
||||
* - Detectar contacto del jugador con puertas cerradas y disparar la apertura
|
||||
* si tiene la llave correspondiente (consultando el Inventory global)
|
||||
* - Sincronizar el estado bloqueante con el CollisionMap: cuando una puerta
|
||||
* está CLOSED u OPENING, sus 4 tiles son WALL; cuando pasa a OPENED, se
|
||||
* ponen a EMPTY
|
||||
* - Registrar cada puerta en el SolidActorManager para que sus AABBs
|
||||
* participen en los sweeps de colisión del Player. El bit BLOCKS_PLAYER
|
||||
* del propio Door se encarga de activar/desactivar el bloqueo al abrir.
|
||||
* - Persistir el estado abierto en DoorTracker
|
||||
*/
|
||||
class DoorManager {
|
||||
public:
|
||||
DoorManager(std::string room_id, CollisionMap* collision_map);
|
||||
DoorManager(std::string room_id, SolidActorManager* solid_actors);
|
||||
~DoorManager() = default;
|
||||
|
||||
// Prohibir copia y movimiento para evitar duplicación accidental
|
||||
@@ -35,7 +35,7 @@ class DoorManager {
|
||||
auto operator=(DoorManager&&) -> DoorManager& = delete;
|
||||
|
||||
// Gestión de puertas
|
||||
void addDoor(std::shared_ptr<Door> door); // Añade una puerta y aplica WALLs si está cerrada
|
||||
void addDoor(std::shared_ptr<Door> door); // Añade una puerta y la registra en el SolidActorManager
|
||||
void clear(); // Elimina todas las puertas
|
||||
|
||||
// Actualización y renderizado
|
||||
@@ -66,24 +66,20 @@ class DoorManager {
|
||||
/**
|
||||
* @brief Mueve la puerta indicada a (x, y) en píxeles
|
||||
*
|
||||
* Limpia los WALLs viejos del CollisionMap y, si la puerta sigue siendo
|
||||
* bloqueante, escribe los nuevos. Encapsula el bookkeeping de tiles para
|
||||
* que el editor nunca toque el CollisionMap directamente.
|
||||
* Reposiciona el sprite y el AABB de la puerta. El registro en el
|
||||
* SolidActorManager no cambia: el manager siempre lee el AABB actual
|
||||
* del SolidActor durante los sweeps.
|
||||
*/
|
||||
void moveDoor(int index, float x, float y);
|
||||
|
||||
/**
|
||||
* @brief Elimina la puerta indicada del vector y limpia sus WALLs del CollisionMap
|
||||
* @brief Elimina la puerta indicada del vector y la desregistra del SolidActorManager
|
||||
*/
|
||||
void removeDoor(int index);
|
||||
#endif
|
||||
|
||||
private:
|
||||
static constexpr int DOOR_TILES_HEIGHT = 4; // Una puerta ocupa 4 tiles verticalmente
|
||||
|
||||
void writeDoorTiles(const Door& door, int tile_value); // Setea las 4 celdas en el CollisionMap
|
||||
|
||||
std::vector<std::shared_ptr<Door>> doors_; // Colección de puertas
|
||||
std::string room_id_; // Identificador de la habitación
|
||||
CollisionMap* collision_map_; // Referencia no propietaria al CollisionMap de la Room
|
||||
SolidActorManager* solid_actors_; // Referencia no propietaria al SolidActorManager de la Room
|
||||
};
|
||||
|
||||
@@ -1,14 +1,26 @@
|
||||
#include "platform_manager.hpp"
|
||||
|
||||
#include "game/entities/moving_platform.hpp" // Para MovingPlatform
|
||||
#include "game/entities/moving_platform.hpp" // Para MovingPlatform
|
||||
#include "game/gameplay/solid_actor_manager.hpp" // Para SolidActorManager
|
||||
|
||||
// Añade una plataforma a la colección
|
||||
// Constructor: recibe el SolidActorManager para registrar las plataformas
|
||||
// como SolidActors (flags CARRY_ON_TOP | ONEWAY_TOP) y que los sweeps del
|
||||
// Player las detecten en moveVertical/checkFalling.
|
||||
PlatformManager::PlatformManager(SolidActorManager* solid_actors)
|
||||
: solid_actors_(solid_actors) {
|
||||
}
|
||||
|
||||
// Añade una plataforma a la colección y la registra en el SolidActorManager
|
||||
void PlatformManager::addPlatform(std::shared_ptr<MovingPlatform> platform) {
|
||||
solid_actors_->registerActor(platform.get());
|
||||
platforms_.push_back(std::move(platform));
|
||||
}
|
||||
|
||||
// Elimina todas las plataformas
|
||||
// Elimina todas las plataformas y las desregistra del SolidActorManager
|
||||
void PlatformManager::clear() {
|
||||
for (const auto& platform : platforms_) {
|
||||
solid_actors_->unregisterActor(platform.get());
|
||||
}
|
||||
platforms_.clear();
|
||||
}
|
||||
|
||||
@@ -57,40 +69,11 @@ auto PlatformManager::getPlatform(int index) -> std::shared_ptr<MovingPlatform>&
|
||||
return platforms_.at(index);
|
||||
}
|
||||
|
||||
// Elimina la última plataforma
|
||||
// Elimina la última plataforma (y la desregistra del SolidActorManager)
|
||||
void PlatformManager::removeLastPlatform() {
|
||||
if (!platforms_.empty()) {
|
||||
solid_actors_->unregisterActor(platforms_.back().get());
|
||||
platforms_.pop_back();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Comprueba si el jugador está sobre alguna plataforma
|
||||
// Devuelve puntero a la plataforma o nullptr si no está sobre ninguna
|
||||
auto PlatformManager::checkPlayerOnPlatform(const SDL_FRect& player_collider, float player_vy) -> MovingPlatform* {
|
||||
// Solo detectamos si el jugador está cayendo o quieto (no saltando hacia arriba)
|
||||
if (player_vy < 0.0F) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (const auto& platform : platforms_) {
|
||||
SDL_FRect plat_rect = platform->getCollider();
|
||||
|
||||
// Comprobar overlap horizontal
|
||||
if (player_collider.x + player_collider.w <= plat_rect.x) { continue; }
|
||||
if (player_collider.x >= plat_rect.x + plat_rect.w) { continue; }
|
||||
|
||||
// Comprobar que los pies del jugador están cerca del top de la plataforma
|
||||
float player_feet = player_collider.y + player_collider.h;
|
||||
float platform_top = plat_rect.y;
|
||||
|
||||
// Tolerancia bidireccional de 4px para compensar el movimiento entre frames
|
||||
// (cuando la plataforma baja, los pies quedan por encima del top momentáneamente)
|
||||
constexpr float TOLERANCE = 4.0F;
|
||||
if (player_feet >= platform_top - TOLERANCE && player_feet <= platform_top + TOLERANCE) {
|
||||
return platform.get();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -7,9 +7,11 @@
|
||||
|
||||
#include "game/entities/moving_platform.hpp" // Para MovingPlatform, MovingPlatform::Data
|
||||
|
||||
class SolidActorManager;
|
||||
|
||||
class PlatformManager {
|
||||
public:
|
||||
PlatformManager() = default;
|
||||
explicit PlatformManager(SolidActorManager* solid_actors);
|
||||
~PlatformManager() = default;
|
||||
|
||||
// Prohibir copia y movimiento
|
||||
@@ -27,10 +29,6 @@ class PlatformManager {
|
||||
void update(float delta_time);
|
||||
void render();
|
||||
|
||||
// Detección de plataforma bajo el jugador
|
||||
// Devuelve puntero a la plataforma sobre la que está el jugador, o nullptr
|
||||
auto checkPlayerOnPlatform(const SDL_FRect& player_collider, float player_vy) -> MovingPlatform*;
|
||||
|
||||
#ifdef _DEBUG
|
||||
void updateAnimations(float delta_time);
|
||||
void resetPositions(const std::vector<MovingPlatform::Data>& platform_data);
|
||||
@@ -41,4 +39,5 @@ class PlatformManager {
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<MovingPlatform>> platforms_;
|
||||
SolidActorManager* solid_actors_{nullptr}; // Referencia no propietaria al SolidActorManager de la Room
|
||||
};
|
||||
|
||||
@@ -2,22 +2,23 @@
|
||||
|
||||
#include <utility> // Para std::move
|
||||
|
||||
#include "core/rendering/surface.hpp" // Para Surface
|
||||
#include "core/resources/resource_cache.hpp" // Para Resource
|
||||
#include "game/defaults.hpp" // Para Defaults::Game
|
||||
#include "game/gameplay/collision_map.hpp" // Para CollisionMap
|
||||
#include "game/gameplay/door_manager.hpp" // Para DoorManager
|
||||
#include "game/gameplay/door_tracker.hpp" // Para DoorTracker
|
||||
#include "game/gameplay/enemy_manager.hpp" // Para EnemyManager
|
||||
#include "game/gameplay/item_manager.hpp" // Para ItemManager
|
||||
#include "game/gameplay/item_tracker.hpp" // Para ItemTracker
|
||||
#include "game/gameplay/key_manager.hpp" // Para KeyManager
|
||||
#include "game/gameplay/key_tracker.hpp" // Para KeyTracker
|
||||
#include "game/gameplay/platform_manager.hpp" // Para PlatformManager
|
||||
#include "game/gameplay/room_format.hpp" // Para RoomFormat
|
||||
#include "game/gameplay/scoreboard.hpp" // Para Scoreboard::Data
|
||||
#include "game/gameplay/tilemap_renderer.hpp" // Para TilemapRenderer
|
||||
#include "utils/defines.hpp" // Para TILE_SIZE
|
||||
#include "core/rendering/surface.hpp" // Para Surface
|
||||
#include "core/resources/resource_cache.hpp" // Para Resource
|
||||
#include "game/defaults.hpp" // Para Defaults::Game
|
||||
#include "game/gameplay/collision_map.hpp" // Para CollisionMap
|
||||
#include "game/gameplay/door_manager.hpp" // Para DoorManager
|
||||
#include "game/gameplay/door_tracker.hpp" // Para DoorTracker
|
||||
#include "game/gameplay/enemy_manager.hpp" // Para EnemyManager
|
||||
#include "game/gameplay/item_manager.hpp" // Para ItemManager
|
||||
#include "game/gameplay/item_tracker.hpp" // Para ItemTracker
|
||||
#include "game/gameplay/key_manager.hpp" // Para KeyManager
|
||||
#include "game/gameplay/key_tracker.hpp" // Para KeyTracker
|
||||
#include "game/gameplay/platform_manager.hpp" // Para PlatformManager
|
||||
#include "game/gameplay/room_format.hpp" // Para RoomFormat
|
||||
#include "game/gameplay/scoreboard.hpp" // Para Scoreboard::Data
|
||||
#include "game/gameplay/solid_actor_manager.hpp" // Para SolidActorManager
|
||||
#include "game/gameplay/tilemap_renderer.hpp" // Para TilemapRenderer
|
||||
#include "utils/defines.hpp" // Para TILE_SIZE
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
// Constructor
|
||||
@@ -25,18 +26,24 @@ Room::Room(const std::string& room_path, std::shared_ptr<Scoreboard::Data> data)
|
||||
: data_(std::move(data)) {
|
||||
auto room = Resource::Cache::get()->getRoom(room_path);
|
||||
|
||||
// Gestor de actores sólidos dinámicos (AABBs): puertas, plataformas móviles,
|
||||
// y otros actores que participan en la resolución de colisión del Player
|
||||
// sin escribir en el tilemap. Debe ir antes que DoorManager/PlatformManager
|
||||
// para poder pasarles su puntero.
|
||||
solid_actor_manager_ = std::make_unique<SolidActorManager>();
|
||||
|
||||
// Crea los managers de enemigos, items, plataformas y llaves
|
||||
enemy_manager_ = std::make_unique<EnemyManager>();
|
||||
item_manager_ = std::make_unique<ItemManager>(room->number, data_);
|
||||
platform_manager_ = std::make_unique<PlatformManager>();
|
||||
platform_manager_ = std::make_unique<PlatformManager>(solid_actor_manager_.get());
|
||||
key_manager_ = std::make_unique<KeyManager>(room->number);
|
||||
|
||||
// Crea el mapa de colisiones desde el collision_tile_map (debe ir antes
|
||||
// del DoorManager porque éste lo necesita para mutar tiles dinámicamente)
|
||||
// Crea el mapa de colisiones desde el collision_tile_map
|
||||
collision_map_ = std::make_unique<CollisionMap>(room->collision_tile_map);
|
||||
|
||||
// Crea el manager de puertas (necesita el CollisionMap para sincronizar muros)
|
||||
door_manager_ = std::make_unique<DoorManager>(room->number, collision_map_.get());
|
||||
// Crea el manager de puertas (registra los Door como SolidActor en el
|
||||
// SolidActorManager; ya no escribe tiles en el CollisionMap).
|
||||
door_manager_ = std::make_unique<DoorManager>(room->number, solid_actor_manager_.get());
|
||||
|
||||
initializeRoom(*room);
|
||||
|
||||
@@ -250,6 +257,14 @@ auto Room::getTileCollider() const -> const TileCollider& {
|
||||
return collision_map_->getTileCollider();
|
||||
}
|
||||
|
||||
auto Room::getSolidActors() const -> const SolidActorManager& {
|
||||
return *solid_actor_manager_;
|
||||
}
|
||||
|
||||
auto Room::getSolidActors() -> SolidActorManager& {
|
||||
return *solid_actor_manager_;
|
||||
}
|
||||
|
||||
auto Room::getCollisionTileMap() const -> const std::vector<int>& {
|
||||
return collision_map_->getCollisionTileMap();
|
||||
}
|
||||
@@ -258,6 +273,10 @@ void Room::updateCollisionBorders(const CollisionMap::AdjacentData& adjacent) {
|
||||
collision_map_->updateBorders(adjacent);
|
||||
}
|
||||
|
||||
void Room::updateSolidActorBorders(const SolidActorManager::AdjacentActors& adjacent) {
|
||||
solid_actor_manager_->setAdjacent(adjacent);
|
||||
}
|
||||
|
||||
// Devuelve la cadena del fichero de la habitación contigua segun el borde
|
||||
auto Room::getRoom(Border border) -> std::string { // NOLINT(readability-convert-member-functions-to-static)
|
||||
switch (border) {
|
||||
@@ -290,10 +309,6 @@ void Room::tryUnlockDoors(const SDL_FRect& player_rect) {
|
||||
door_manager_->tryUnlock(player_rect);
|
||||
}
|
||||
|
||||
auto Room::checkPlayerOnPlatform(const SDL_FRect& player_collider, float player_vy) -> MovingPlatform* {
|
||||
return platform_manager_->checkPlayerOnPlatform(player_collider, player_vy);
|
||||
}
|
||||
|
||||
// Carga una habitación desde un archivo YAML (delegado a RoomFormat)
|
||||
auto Room::loadYAML(const std::string& file_path, bool verbose) -> Data { // NOLINT(readability-convert-member-functions-to-static)
|
||||
return RoomFormat::loadYAML(file_path, verbose);
|
||||
|
||||
@@ -6,15 +6,16 @@
|
||||
#include <string> // Para string
|
||||
#include <vector> // Para vector
|
||||
|
||||
#include "game/defaults.hpp" // Para Defaults::Game::Room
|
||||
#include "game/entities/door.hpp" // Para Door::Data
|
||||
#include "game/entities/enemy.hpp" // Para EnemyData
|
||||
#include "game/entities/item.hpp" // Para ItemData
|
||||
#include "game/entities/key.hpp" // Para Key::Data
|
||||
#include "game/entities/moving_platform.hpp" // Para MovingPlatform::Data
|
||||
#include "game/gameplay/collision_map.hpp" // Para CollisionMap::AdjacentData
|
||||
#include "game/gameplay/scoreboard.hpp" // Para Scoreboard::Data
|
||||
#include "utils/defines.hpp" // Para Tile::SIZE, Map::WIDTH, Map::HEIGHT
|
||||
#include "game/defaults.hpp" // Para Defaults::Game::Room
|
||||
#include "game/entities/door.hpp" // Para Door::Data
|
||||
#include "game/entities/enemy.hpp" // Para EnemyData
|
||||
#include "game/entities/item.hpp" // Para ItemData
|
||||
#include "game/entities/key.hpp" // Para Key::Data
|
||||
#include "game/entities/moving_platform.hpp" // Para MovingPlatform::Data
|
||||
#include "game/gameplay/collision_map.hpp" // Para CollisionMap::AdjacentData
|
||||
#include "game/gameplay/scoreboard.hpp" // Para Scoreboard::Data
|
||||
#include "game/gameplay/solid_actor_manager.hpp" // Para SolidActorManager::AdjacentActors
|
||||
#include "utils/defines.hpp" // Para Tile::SIZE, Map::WIDTH, Map::HEIGHT
|
||||
class Surface;
|
||||
class EnemyManager;
|
||||
class ItemManager;
|
||||
@@ -82,6 +83,7 @@ class Room {
|
||||
auto getPlatformManager() -> PlatformManager* { return platform_manager_.get(); }
|
||||
auto getKeyManager() -> KeyManager* { return key_manager_.get(); }
|
||||
auto getDoorManager() -> DoorManager* { return door_manager_.get(); }
|
||||
auto getSolidActorManager() -> SolidActorManager* { return solid_actor_manager_.get(); }
|
||||
void setTile(int index, int tile_value);
|
||||
void setCollisionTile(int index, int value);
|
||||
void setConnection(Border border, const std::string& room_name);
|
||||
@@ -95,11 +97,13 @@ class Room {
|
||||
auto itemCollision(SDL_FRect& rect) -> bool;
|
||||
auto keyCollision(SDL_FRect& rect) -> bool;
|
||||
void tryUnlockDoors(const SDL_FRect& player_rect);
|
||||
auto checkPlayerOnPlatform(const SDL_FRect& player_collider, float player_vy) -> MovingPlatform*;
|
||||
void setPaused(bool value);
|
||||
[[nodiscard]] auto getTileCollider() const -> const TileCollider&;
|
||||
[[nodiscard]] auto getSolidActors() const -> const SolidActorManager&;
|
||||
[[nodiscard]] auto getSolidActors() -> SolidActorManager&;
|
||||
[[nodiscard]] auto getCollisionTileMap() const -> const std::vector<int>&;
|
||||
void updateCollisionBorders(const CollisionMap::AdjacentData& adjacent);
|
||||
void updateSolidActorBorders(const SolidActorManager::AdjacentActors& adjacent);
|
||||
|
||||
// Método de carga de archivos YAML (delegado a RoomFormat)
|
||||
static auto loadYAML(const std::string& file_path, bool verbose = false) -> Data;
|
||||
@@ -114,6 +118,7 @@ class Room {
|
||||
std::unique_ptr<PlatformManager> platform_manager_;
|
||||
std::unique_ptr<KeyManager> key_manager_;
|
||||
std::unique_ptr<DoorManager> door_manager_;
|
||||
std::unique_ptr<SolidActorManager> solid_actor_manager_;
|
||||
std::unique_ptr<CollisionMap> collision_map_;
|
||||
std::unique_ptr<TilemapRenderer> tilemap_renderer_;
|
||||
std::shared_ptr<Surface> surface_;
|
||||
|
||||
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;
|
||||
}
|
||||
91
source/game/gameplay/solid_actor_manager.hpp
Normal file
91
source/game/gameplay/solid_actor_manager.hpp
Normal file
@@ -0,0 +1,91 @@
|
||||
#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_{};
|
||||
};
|
||||
Reference in New Issue
Block a user