migrades portes i plataformes a solidActor
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -23,3 +23,4 @@ tools/pack_resources/pack_tool.exe
|
|||||||
*.res
|
*.res
|
||||||
dist/
|
dist/
|
||||||
.claude/settings.local.json
|
.claude/settings.local.json
|
||||||
|
_projecte_2026
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ set(APP_SOURCES
|
|||||||
source/game/gameplay/key_manager.cpp
|
source/game/gameplay/key_manager.cpp
|
||||||
source/game/gameplay/door_manager.cpp
|
source/game/gameplay/door_manager.cpp
|
||||||
source/game/gameplay/platform_manager.cpp
|
source/game/gameplay/platform_manager.cpp
|
||||||
|
source/game/gameplay/solid_actor_manager.cpp
|
||||||
source/game/gameplay/item_tracker.cpp
|
source/game/gameplay/item_tracker.cpp
|
||||||
source/game/gameplay/key_tracker.cpp
|
source/game/gameplay/key_tracker.cpp
|
||||||
source/game/gameplay/door_tracker.cpp
|
source/game/gameplay/door_tracker.cpp
|
||||||
|
|||||||
@@ -750,16 +750,12 @@ auto MapEditor::commitEntityDrag() -> bool {
|
|||||||
break;
|
break;
|
||||||
case EntityType::DOOR:
|
case EntityType::DOOR:
|
||||||
if (IDX >= 0 && IDX < static_cast<int>(room_data_.doors.size())) {
|
if (IDX >= 0 && IDX < static_cast<int>(room_data_.doors.size())) {
|
||||||
// Truco crítico: durante el drag, moveEntityVisual movió el sprite
|
// Con el SolidActorManager, moveDoor solo reposiciona el AABB
|
||||||
// pero los WALLs del CollisionMap siguen en la posición antigua. Antes
|
// del Door (no hay tiles que sincronizar). moveEntityVisual ya
|
||||||
// de llamar a moveDoor (que limpia los tiles "actuales" y escribe los
|
// movió el sprite durante el drag; moveDoor hace el ajuste final.
|
||||||
// nuevos), restauramos el sprite a su posición vieja para que coincida
|
|
||||||
// con los tiles. moveDoor luego hace el ciclo limpio y completo.
|
|
||||||
auto* door_mgr = room_->getDoorManager();
|
|
||||||
door_mgr->getDoor(IDX)->setPosition(room_data_.doors[IDX].x, room_data_.doors[IDX].y);
|
|
||||||
room_data_.doors[IDX].x = drag_.snap_x;
|
room_data_.doors[IDX].x = drag_.snap_x;
|
||||||
room_data_.doors[IDX].y = drag_.snap_y;
|
room_data_.doors[IDX].y = drag_.snap_y;
|
||||||
door_mgr->moveDoor(IDX, drag_.snap_x, drag_.snap_y);
|
room_->getDoorManager()->moveDoor(IDX, drag_.snap_x, drag_.snap_y);
|
||||||
selection_ = {EntityType::DOOR, IDX};
|
selection_ = {EntityType::DOOR, IDX};
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -2503,11 +2499,12 @@ auto MapEditor::duplicateKey() -> std::string {
|
|||||||
// (id, animation) requiere recrear el Door y mantener los tiles sincronizados.
|
// (id, animation) requiere recrear el Door y mantener los tiles sincronizados.
|
||||||
void MapEditor::rebuildDoors() {
|
void MapEditor::rebuildDoors() {
|
||||||
auto* door_mgr = room_->getDoorManager();
|
auto* door_mgr = room_->getDoorManager();
|
||||||
// Borrar una a una desde el principio: cada removeDoor limpia sus WALLs
|
// Borrar una a una desde el principio: cada removeDoor desregistra el
|
||||||
|
// Door del SolidActorManager.
|
||||||
while (door_mgr->getCount() > 0) {
|
while (door_mgr->getCount() > 0) {
|
||||||
door_mgr->removeDoor(0);
|
door_mgr->removeDoor(0);
|
||||||
}
|
}
|
||||||
// Re-añadir desde room_data_; addDoor reescribe los WALLs si bloquean
|
// Re-añadir desde room_data_; addDoor las registra como SolidActor.
|
||||||
for (const auto& d : room_data_.doors) {
|
for (const auto& d : room_data_.doors) {
|
||||||
door_mgr->addDoor(std::make_shared<Door>(d, /*start_opened=*/false));
|
door_mgr->addDoor(std::make_shared<Door>(d, /*start_opened=*/false));
|
||||||
}
|
}
|
||||||
@@ -2584,7 +2581,7 @@ auto MapEditor::deleteDoor() -> std::string {
|
|||||||
|
|
||||||
const int IDX = selection_.index;
|
const int IDX = selection_.index;
|
||||||
|
|
||||||
// Importante: usar removeDoor del manager (limpia los WALLs antes de borrar)
|
// Importante: usar removeDoor del manager (desregistra del SolidActorManager)
|
||||||
room_->getDoorManager()->removeDoor(IDX);
|
room_->getDoorManager()->removeDoor(IDX);
|
||||||
room_data_.doors.erase(room_data_.doors.begin() + IDX);
|
room_data_.doors.erase(room_data_.doors.begin() + IDX);
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,9 @@
|
|||||||
|
|
||||||
// Constructor: carga la animación y posiciona la puerta. Si start_opened es
|
// Constructor: carga la animación y posiciona la puerta. Si start_opened es
|
||||||
// true, la puerta se crea ya abierta (estado OPENED, animación "opened"); en
|
// true, la puerta se crea ya abierta (estado OPENED, animación "opened"); en
|
||||||
// caso contrario, se crea cerrada (estado CLOSED, animación "closed").
|
// caso contrario, se crea cerrada (estado CLOSED, animación "closed") y
|
||||||
|
// activa el flag BLOCKS_PLAYER del SolidActor para que los sweeps del
|
||||||
|
// SolidActorManager la vean como muro.
|
||||||
Door::Door(const Data& data, bool start_opened)
|
Door::Door(const Data& data, bool start_opened)
|
||||||
: sprite_(std::make_shared<AnimatedSprite>(Resource::Cache::get()->getAnimationData(data.animation_path))),
|
: sprite_(std::make_shared<AnimatedSprite>(Resource::Cache::get()->getAnimationData(data.animation_path))),
|
||||||
id_(data.id),
|
id_(data.id),
|
||||||
@@ -13,7 +15,10 @@ Door::Door(const Data& data, bool start_opened)
|
|||||||
sprite_->setPosX(data.x);
|
sprite_->setPosX(data.x);
|
||||||
sprite_->setPosY(data.y);
|
sprite_->setPosY(data.y);
|
||||||
sprite_->setCurrentAnimation(start_opened ? "opened" : "closed");
|
sprite_->setCurrentAnimation(start_opened ? "opened" : "closed");
|
||||||
collider_ = sprite_->getRect();
|
aabb_ = sprite_->getRect();
|
||||||
|
if (!start_opened) {
|
||||||
|
flags_ = BLOCKS_PLAYER;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pinta la puerta en pantalla
|
// Pinta la puerta en pantalla
|
||||||
@@ -23,8 +28,9 @@ void Door::render() {
|
|||||||
|
|
||||||
// Avanza la animación. Solo OPENING anima de verdad; CLOSED y OPENED son
|
// Avanza la animación. Solo OPENING anima de verdad; CLOSED y OPENED son
|
||||||
// frames estáticos. Cuando la animación de OPENING termina, transiciona a
|
// frames estáticos. Cuando la animación de OPENING termina, transiciona a
|
||||||
// OPENED, fija el frame final y marca just_opened_ para que el DoorManager
|
// OPENED, fija el frame final, limpia el flag BLOCKS_PLAYER (los sweeps
|
||||||
// libere los tiles de colisión.
|
// del SolidActorManager dejan de verla como muro) y marca just_opened_
|
||||||
|
// para que el DoorManager persista el estado abierto en DoorTracker.
|
||||||
void Door::update(float delta_time) {
|
void Door::update(float delta_time) {
|
||||||
if (is_paused_) {
|
if (is_paused_) {
|
||||||
return;
|
return;
|
||||||
@@ -35,6 +41,7 @@ void Door::update(float delta_time) {
|
|||||||
if (sprite_->animationIsCompleted()) {
|
if (sprite_->animationIsCompleted()) {
|
||||||
state_ = State::OPENED;
|
state_ = State::OPENED;
|
||||||
sprite_->setCurrentAnimation("opened");
|
sprite_->setCurrentAnimation("opened");
|
||||||
|
flags_ = 0; // Deja de bloquear al Player
|
||||||
just_opened_ = true;
|
just_opened_ = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,11 +72,12 @@ auto Door::justOpened() -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
// Mueve la puerta a la posición indicada (sprite + collider). NO toca el
|
// Mueve la puerta a la posición indicada (sprite + AABB del SolidActor).
|
||||||
// CollisionMap — eso es responsabilidad del DoorManager (moveDoor/removeDoor).
|
// Usado por el editor; el DoorManager::moveDoor se encarga del bookkeeping
|
||||||
|
// de registro en el SolidActorManager.
|
||||||
void Door::setPosition(float x, float y) {
|
void Door::setPosition(float x, float y) {
|
||||||
sprite_->setPosX(x);
|
sprite_->setPosX(x);
|
||||||
sprite_->setPosY(y);
|
sprite_->setPosY(y);
|
||||||
collider_ = sprite_->getRect();
|
aabb_ = sprite_->getRect();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
#include <memory> // Para shared_ptr
|
#include <memory> // Para shared_ptr
|
||||||
#include <string> // Para string
|
#include <string> // Para string
|
||||||
|
|
||||||
|
#include "game/entities/solid_actor.hpp" // Para SolidActor
|
||||||
|
|
||||||
class AnimatedSprite;
|
class AnimatedSprite;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -15,12 +17,12 @@ class AnimatedSprite;
|
|||||||
* - "opening": animación de transición que se reproduce una sola vez
|
* - "opening": animación de transición que se reproduce una sola vez
|
||||||
* - "opened": estado final no bloqueante (frame estático)
|
* - "opened": estado final no bloqueante (frame estático)
|
||||||
*
|
*
|
||||||
* El comportamiento de "muro" se implementa marcando los 4 tiles que ocupa
|
* El comportamiento de "muro" se implementa como SolidActor con flag
|
||||||
* la puerta como WALL en el CollisionMap (lo gestiona el DoorManager). Cuando
|
* BLOCKS_PLAYER. El SolidActorManager de la Room lo consulta en los sweeps
|
||||||
* la puerta termina de abrirse, los tiles vuelven a EMPTY y el jugador puede
|
* del Player. Cuando la puerta termina de abrirse, se limpia el flag
|
||||||
* pasar.
|
* BLOCKS_PLAYER y el sweep deja de verla como pared.
|
||||||
*/
|
*/
|
||||||
class Door {
|
class Door : public SolidActor {
|
||||||
public:
|
public:
|
||||||
enum class State : int {
|
enum class State : int {
|
||||||
CLOSED = 0,
|
CLOSED = 0,
|
||||||
@@ -41,11 +43,10 @@ class Door {
|
|||||||
void render(); // Pinta la puerta en pantalla
|
void render(); // Pinta la puerta en pantalla
|
||||||
void update(float delta_time); // Avanza la animación; si OPENING termina → OPENED
|
void update(float delta_time); // Avanza la animación; si OPENING termina → OPENED
|
||||||
|
|
||||||
auto getCollider() -> SDL_FRect& { return collider_; } // Rectángulo de colisión (8x32)
|
auto getCollider() -> SDL_FRect& { return aabb_; } // Rectángulo de colisión (8x32)
|
||||||
[[nodiscard]] auto getPos() const -> SDL_FPoint; // Posición en píxeles
|
[[nodiscard]] auto getPos() const -> SDL_FPoint; // Posición en píxeles
|
||||||
[[nodiscard]] auto getId() const -> const std::string& { return id_; } // Identificador
|
[[nodiscard]] auto getId() const -> const std::string& { return id_; } // Identificador
|
||||||
[[nodiscard]] auto getState() const -> State { return state_; } // Estado actual
|
[[nodiscard]] auto getState() const -> State { return state_; } // Estado actual
|
||||||
[[nodiscard]] auto isBlocking() const -> bool { return state_ != State::OPENED; } // True si bloquea al jugador
|
|
||||||
|
|
||||||
void startOpening(); // Transición CLOSED → OPENING
|
void startOpening(); // Transición CLOSED → OPENING
|
||||||
auto justOpened() -> bool; // Flag one-shot consumido por el manager
|
auto justOpened() -> bool; // Flag one-shot consumido por el manager
|
||||||
@@ -53,12 +54,11 @@ class Door {
|
|||||||
void setPaused(bool paused) { is_paused_ = paused; } // Pausa/despausa la animación
|
void setPaused(bool paused) { is_paused_ = paused; } // Pausa/despausa la animación
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
void setPosition(float x, float y); // Mueve sprite y collider en vivo (editor; NO toca CollisionMap)
|
void setPosition(float x, float y); // Mueve sprite y AABB en vivo (editor)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<AnimatedSprite> sprite_; // Sprite animado de la puerta
|
std::shared_ptr<AnimatedSprite> sprite_; // Sprite animado de la puerta
|
||||||
SDL_FRect collider_{}; // Rectángulo de colisión
|
|
||||||
std::string id_; // Identificador
|
std::string id_; // Identificador
|
||||||
State state_{State::CLOSED}; // Estado actual
|
State state_{State::CLOSED}; // Estado actual
|
||||||
bool just_opened_{false}; // Flag one-shot: la puerta acaba de transicionar a OPENED
|
bool just_opened_{false}; // Flag one-shot: la puerta acaba de transicionar a OPENED
|
||||||
|
|||||||
@@ -28,13 +28,19 @@ MovingPlatform::MovingPlatform(const Data& data)
|
|||||||
speed_(data.speed),
|
speed_(data.speed),
|
||||||
loop_mode_(data.loop),
|
loop_mode_(data.loop),
|
||||||
easing_(resolveEasing(data.easing)) {
|
easing_(resolveEasing(data.easing)) {
|
||||||
|
// Flags del SolidActor: jump-through desde abajo, carry al Player encima.
|
||||||
|
// NOTA: sin BLOCKS_PLAYER, las plataformas móviles no bloquean lateralmente
|
||||||
|
// ni por arriba. ONEWAY_TOP hace que los sweeps verticales solo las vean
|
||||||
|
// al caer desde arriba.
|
||||||
|
flags_ = CARRY_ON_TOP | ONEWAY_TOP;
|
||||||
|
|
||||||
// Colocar el sprite en el primer waypoint
|
// Colocar el sprite en el primer waypoint
|
||||||
if (!path_.empty()) {
|
if (!path_.empty()) {
|
||||||
sprite_->setPosX(path_[0].x);
|
sprite_->setPosX(path_[0].x);
|
||||||
sprite_->setPosY(path_[0].y);
|
sprite_->setPosY(path_[0].y);
|
||||||
}
|
}
|
||||||
|
|
||||||
collider_ = getRect();
|
aabb_ = getRect();
|
||||||
|
|
||||||
// Frame inicial
|
// Frame inicial
|
||||||
sprite_->setCurrentAnimationFrame((data.frame == -1) ? (rand() % sprite_->getCurrentAnimationSize()) : data.frame);
|
sprite_->setCurrentAnimationFrame((data.frame == -1) ? (rand() % sprite_->getCurrentAnimationSize()) : data.frame);
|
||||||
@@ -115,8 +121,8 @@ void MovingPlatform::update(float delta_time) {
|
|||||||
sprite_->animate(delta_time);
|
sprite_->animate(delta_time);
|
||||||
|
|
||||||
if (path_.size() < 2) {
|
if (path_.size() < 2) {
|
||||||
last_dx_ = 0.0F;
|
last_delta_.x = 0.0F;
|
||||||
last_dy_ = 0.0F;
|
last_delta_.y = 0.0F;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,8 +136,8 @@ void MovingPlatform::update(float delta_time) {
|
|||||||
waiting_ = false;
|
waiting_ = false;
|
||||||
advanceSegment();
|
advanceSegment();
|
||||||
} else {
|
} else {
|
||||||
last_dx_ = 0.0F;
|
last_delta_.x = 0.0F;
|
||||||
last_dy_ = 0.0F;
|
last_delta_.y = 0.0F;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,9 +180,9 @@ void MovingPlatform::update(float delta_time) {
|
|||||||
sprite_->setPosY(new_y);
|
sprite_->setPosY(new_y);
|
||||||
}
|
}
|
||||||
|
|
||||||
last_dx_ = sprite_->getPosX() - old_x;
|
last_delta_.x = sprite_->getPosX() - old_x;
|
||||||
last_dy_ = sprite_->getPosY() - old_y;
|
last_delta_.y = sprite_->getPosY() - old_y;
|
||||||
collider_ = getRect();
|
aabb_ = getRect();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pinta la plataforma en pantalla
|
// Pinta la plataforma en pantalla
|
||||||
@@ -208,7 +214,7 @@ void MovingPlatform::resetToInitialPosition(const Data& data) {
|
|||||||
sprite_->setPosY(path_[0].y);
|
sprite_->setPosY(path_[0].y);
|
||||||
}
|
}
|
||||||
|
|
||||||
collider_ = getRect();
|
aabb_ = getRect();
|
||||||
recalcSegmentLength();
|
recalcSegmentLength();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -217,8 +223,3 @@ void MovingPlatform::resetToInitialPosition(const Data& data) {
|
|||||||
auto MovingPlatform::getRect() -> SDL_FRect {
|
auto MovingPlatform::getRect() -> SDL_FRect {
|
||||||
return sprite_->getRect();
|
return sprite_->getRect();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtiene el rectangulo de colisión
|
|
||||||
auto MovingPlatform::getCollider() -> SDL_FRect& {
|
|
||||||
return collider_;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
#include <string> // Para string
|
#include <string> // Para string
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
|
#include "game/entities/solid_actor.hpp" // Para SolidActor
|
||||||
|
|
||||||
class AnimatedSprite;
|
class AnimatedSprite;
|
||||||
|
|
||||||
// Punto de paso en la ruta de una plataforma
|
// Punto de paso en la ruta de una plataforma
|
||||||
@@ -22,7 +24,7 @@ enum class LoopMode { PINGPONG,
|
|||||||
// Tipo de función de easing
|
// Tipo de función de easing
|
||||||
using EasingFunc = float (*)(float);
|
using EasingFunc = float (*)(float);
|
||||||
|
|
||||||
class MovingPlatform {
|
class MovingPlatform : public SolidActor {
|
||||||
public:
|
public:
|
||||||
struct Data {
|
struct Data {
|
||||||
std::string animation_path;
|
std::string animation_path;
|
||||||
@@ -44,10 +46,7 @@ class MovingPlatform {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto getRect() -> SDL_FRect;
|
auto getRect() -> SDL_FRect;
|
||||||
auto getCollider() -> SDL_FRect&;
|
auto getCollider() -> SDL_FRect& { return aabb_; }
|
||||||
|
|
||||||
[[nodiscard]] auto getLastDX() const -> float { return last_dx_; }
|
|
||||||
[[nodiscard]] auto getLastDY() const -> float { return last_dy_; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void advanceSegment();
|
void advanceSegment();
|
||||||
@@ -57,9 +56,6 @@ class MovingPlatform {
|
|||||||
static auto resolveEasing(const std::string& name) -> EasingFunc;
|
static auto resolveEasing(const std::string& name) -> EasingFunc;
|
||||||
|
|
||||||
std::shared_ptr<AnimatedSprite> sprite_;
|
std::shared_ptr<AnimatedSprite> sprite_;
|
||||||
SDL_FRect collider_{};
|
|
||||||
float last_dx_{0.0F};
|
|
||||||
float last_dy_{0.0F};
|
|
||||||
|
|
||||||
// Estado del path
|
// Estado del path
|
||||||
std::vector<Waypoint> path_;
|
std::vector<Waypoint> path_;
|
||||||
|
|||||||
@@ -9,7 +9,9 @@
|
|||||||
#include "core/rendering/sprite/animated_sprite.hpp" // Para AnimatedSprite
|
#include "core/rendering/sprite/animated_sprite.hpp" // Para AnimatedSprite
|
||||||
#include "core/resources/resource_cache.hpp" // Para Resource
|
#include "core/resources/resource_cache.hpp" // Para Resource
|
||||||
#include "game/defaults.hpp" // Para Defaults::Game::Player, Defaults::Sound::Files
|
#include "game/defaults.hpp" // Para Defaults::Game::Player, Defaults::Sound::Files
|
||||||
|
#include "game/entities/solid_actor.hpp" // Para SolidActor
|
||||||
#include "game/gameplay/room.hpp" // Para Room
|
#include "game/gameplay/room.hpp" // Para Room
|
||||||
|
#include "game/gameplay/solid_actor_manager.hpp" // Para SolidActorManager
|
||||||
#include "game/gameplay/tile_collider.hpp" // Para TileCollider
|
#include "game/gameplay/tile_collider.hpp" // Para TileCollider
|
||||||
#include "game/options.hpp" // Para Cheat, Options
|
#include "game/options.hpp" // Para Cheat, Options
|
||||||
#include "utils/defines.hpp" // Para PlayArea, Collision
|
#include "utils/defines.hpp" // Para PlayArea, Collision
|
||||||
@@ -18,6 +20,29 @@
|
|||||||
#include "core/system/debug.hpp" // Para Debug
|
#include "core/system/debug.hpp" // Para Debug
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Helpers NONE-aware para combinar un hit del TileCollider con un hit del
|
||||||
|
// SolidActorManager. Devuelven el valor más "clampante" (más a la derecha
|
||||||
|
// para left-wall, más a la izquierda para right-wall, más abajo para ceiling).
|
||||||
|
namespace {
|
||||||
|
auto combineLeftWall(float tile_hit, float actor_hit) -> float {
|
||||||
|
if (tile_hit == Collision::NONE) { return actor_hit; }
|
||||||
|
if (actor_hit == Collision::NONE) { return tile_hit; }
|
||||||
|
return std::max(tile_hit, actor_hit);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto combineRightWall(float tile_hit, float actor_hit) -> float {
|
||||||
|
if (tile_hit == Collision::NONE) { return actor_hit; }
|
||||||
|
if (actor_hit == Collision::NONE) { return tile_hit; }
|
||||||
|
return std::min(tile_hit, actor_hit);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto combineCeiling(float tile_hit, float actor_hit) -> float {
|
||||||
|
if (tile_hit == Collision::NONE) { return actor_hit; }
|
||||||
|
if (actor_hit == Collision::NONE) { return tile_hit; }
|
||||||
|
return std::max(tile_hit, actor_hit);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Constructor
|
// Constructor
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -46,6 +71,16 @@ void Player::render() {
|
|||||||
void Player::update(float delta_time) {
|
void Player::update(float delta_time) {
|
||||||
if (is_paused_) { return; }
|
if (is_paused_) { return; }
|
||||||
|
|
||||||
|
// 0. Carry de plataforma móvil (SolidActor con CARRY_ON_TOP).
|
||||||
|
// Snap absoluto del eje Y al top del AABB y desplazamiento horizontal
|
||||||
|
// por el delta del último frame del actor. Esto mantiene al Player
|
||||||
|
// pegado a la plataforma cuando sube/baja y lo arrastra lateralmente.
|
||||||
|
if (current_carrier_ != nullptr) {
|
||||||
|
const auto& aabb = current_carrier_->getAABB();
|
||||||
|
x_ += current_carrier_->getLastDelta().x;
|
||||||
|
y_ = aabb.y - HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
// 1. Leer input
|
// 1. Leer input
|
||||||
handleInput();
|
handleInput();
|
||||||
|
|
||||||
@@ -236,34 +271,48 @@ void Player::startJump() {
|
|||||||
// Fase 4a: Movimiento horizontal
|
// Fase 4a: Movimiento horizontal
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
void Player::moveHorizontal(float delta_time) {
|
// Early exit del movimiento horizontal si el player ya está pegado a una
|
||||||
|
// pared en su dirección de movimiento. Sin esto, el player choca pero
|
||||||
|
// conserva vx_ != 0 y animate() reproduce "walk" continuamente.
|
||||||
|
auto Player::stuckAgainstWall() const -> bool {
|
||||||
const auto& tc = room_->getTileCollider();
|
const auto& tc = room_->getTileCollider();
|
||||||
|
const auto& sm = room_->getSolidActors();
|
||||||
// Early exit: si hay pared inmediata en la dirección de movimiento, parar
|
if (vx_ > 0.0F) {
|
||||||
// y poner vx_=0. Sin esto, el player choca, queda re-posicionado en el
|
return tc.checkWallRight(x_, y_, WIDTH, HEIGHT) != Collision::NONE ||
|
||||||
// mismo sitio pero conserva vx_ != 0, así que animate() reproduce walk
|
sm.checkWallRight(x_, y_, WIDTH, HEIGHT) != Collision::NONE;
|
||||||
// anim continuamente mientras empuja contra la pared.
|
|
||||||
if (vx_ > 0.0F && tc.checkWallRight(x_, y_, WIDTH, HEIGHT) != Collision::NONE) {
|
|
||||||
vx_ = 0.0F;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (vx_ < 0.0F && tc.checkWallLeft(x_, y_, WIDTH, HEIGHT) != Collision::NONE) {
|
if (vx_ < 0.0F) {
|
||||||
|
return tc.checkWallLeft(x_, y_, WIDTH, HEIGHT) != Collision::NONE ||
|
||||||
|
sm.checkWallLeft(x_, y_, WIDTH, HEIGHT) != Collision::NONE;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::moveHorizontal(float delta_time) {
|
||||||
|
if (stuckAgainstWall()) {
|
||||||
vx_ = 0.0F;
|
vx_ = 0.0F;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto& tc = room_->getTileCollider();
|
||||||
|
const auto& sm = room_->getSolidActors();
|
||||||
float new_x = x_ + (vx_ * delta_time);
|
float new_x = x_ + (vx_ * delta_time);
|
||||||
|
|
||||||
// Comprobar ambos muros siempre (el tilemap extendido incluye paredes de rooms
|
// Comprobar ambos muros siempre (el tilemap extendido incluye paredes de rooms
|
||||||
// adyacentes; comprobar ambos lados evita solapamiento en zona de borde)
|
// adyacentes; comprobar ambos lados evita solapamiento en zona de borde).
|
||||||
float wall = tc.checkWallLeft(new_x, y_, WIDTH, HEIGHT);
|
// Se combinan tiles + solid actors en cada lado tomando el muro más "clampante".
|
||||||
if (wall != Collision::NONE && wall > new_x) {
|
const float LEFT_WALL = combineLeftWall(
|
||||||
new_x = wall;
|
tc.checkWallLeft(new_x, y_, WIDTH, HEIGHT),
|
||||||
|
sm.checkWallLeft(new_x, y_, WIDTH, HEIGHT));
|
||||||
|
if (LEFT_WALL != Collision::NONE && LEFT_WALL > new_x) {
|
||||||
|
new_x = LEFT_WALL;
|
||||||
}
|
}
|
||||||
wall = tc.checkWallRight(new_x, y_, WIDTH, HEIGHT);
|
const float RIGHT_WALL = combineRightWall(
|
||||||
if (wall != Collision::NONE) {
|
tc.checkWallRight(new_x, y_, WIDTH, HEIGHT),
|
||||||
float corrected = wall - WIDTH;
|
sm.checkWallRight(new_x, y_, WIDTH, HEIGHT));
|
||||||
if (corrected < new_x) { new_x = corrected; }
|
if (RIGHT_WALL != Collision::NONE) {
|
||||||
|
const float CORRECTED = RIGHT_WALL - WIDTH;
|
||||||
|
new_x = std::min(new_x, CORRECTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
x_ = new_x;
|
x_ = new_x;
|
||||||
@@ -355,44 +404,74 @@ void Player::detectSlopeEntry() {
|
|||||||
// Fase 4b: Movimiento vertical
|
// Fase 4b: Movimiento vertical
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
void Player::moveVertical(float delta_time) {
|
// Subiendo: comprobar techo (tiles + solid actors). Si hay colisión,
|
||||||
if (state_ != State::ON_AIR) { return; }
|
// snap y parar vy.
|
||||||
|
void Player::moveVerticalUp(float displacement) {
|
||||||
const auto& tc = room_->getTileCollider();
|
const auto& tc = room_->getTileCollider();
|
||||||
float displacement = vy_ * delta_time;
|
const auto& sm = room_->getSolidActors();
|
||||||
|
const float NEW_Y = y_ + displacement;
|
||||||
if (vy_ < 0.0F) {
|
const float CEILING = combineCeiling(
|
||||||
// Subiendo: comprobar techo
|
tc.checkCeiling(x_, NEW_Y, WIDTH),
|
||||||
float new_y = y_ + displacement;
|
sm.checkCeiling(x_, NEW_Y, WIDTH));
|
||||||
float ceiling = tc.checkCeiling(x_, new_y, WIDTH);
|
if (CEILING != Collision::NONE) {
|
||||||
if (ceiling != Collision::NONE) {
|
y_ = CEILING;
|
||||||
y_ = ceiling;
|
|
||||||
vy_ = 0.0F;
|
vy_ = 0.0F;
|
||||||
} else {
|
} else {
|
||||||
y_ = new_y;
|
y_ = NEW_Y;
|
||||||
}
|
}
|
||||||
} else if (vy_ > 0.0F) {
|
}
|
||||||
// Bajando: comprobar suelo
|
|
||||||
float foot_y = y_ + HEIGHT;
|
|
||||||
float new_foot_y = foot_y + displacement;
|
|
||||||
auto hit = tc.checkFloor(x_, foot_y, WIDTH, new_foot_y);
|
|
||||||
|
|
||||||
if (hit.y != Collision::NONE) {
|
// Bajando: comprobar suelo en tiles y en solid actors; el que esté antes
|
||||||
y_ = hit.y - HEIGHT;
|
// (menor y) gana. Si es un SolidActor con CARRY_ON_TOP, guarda el carrier.
|
||||||
if (hit.type == TileCollider::Tile::SLOPE_L || hit.type == TileCollider::Tile::SLOPE_R) {
|
void Player::moveVerticalDown(float displacement) {
|
||||||
slope_tile_x_ = hit.tile_x;
|
const auto& tc = room_->getTileCollider();
|
||||||
slope_tile_y_ = hit.tile_y;
|
const auto& sm = room_->getSolidActors();
|
||||||
slope_type_ = hit.type;
|
const float FOOT_Y = y_ + HEIGHT;
|
||||||
|
const float NEW_FOOT_Y = FOOT_Y + displacement;
|
||||||
|
const auto TILE_HIT = tc.checkFloor(x_, FOOT_Y, WIDTH, NEW_FOOT_Y);
|
||||||
|
const auto ACTOR_HIT = sm.checkFloor(x_, FOOT_Y, WIDTH, NEW_FOOT_Y);
|
||||||
|
|
||||||
|
// El tile tiene prioridad si está igual o más arriba que el actor.
|
||||||
|
const bool TILE_WINS = (TILE_HIT.y != Collision::NONE) &&
|
||||||
|
(ACTOR_HIT.y == Collision::NONE || TILE_HIT.y <= ACTOR_HIT.y);
|
||||||
|
const bool ACTOR_WINS = !TILE_WINS && (ACTOR_HIT.y != Collision::NONE);
|
||||||
|
|
||||||
|
if (TILE_WINS) {
|
||||||
|
y_ = TILE_HIT.y - HEIGHT;
|
||||||
|
const bool IS_SLOPE = TILE_HIT.type == TileCollider::Tile::SLOPE_L ||
|
||||||
|
TILE_HIT.type == TileCollider::Tile::SLOPE_R;
|
||||||
|
if (IS_SLOPE) {
|
||||||
|
slope_tile_x_ = TILE_HIT.tile_x;
|
||||||
|
slope_tile_y_ = TILE_HIT.tile_y;
|
||||||
|
slope_type_ = TILE_HIT.type;
|
||||||
transitionToState(State::ON_SLOPE);
|
transitionToState(State::ON_SLOPE);
|
||||||
} else {
|
} else {
|
||||||
transitionToState(State::ON_GROUND);
|
transitionToState(State::ON_GROUND);
|
||||||
}
|
}
|
||||||
} else {
|
current_carrier_ = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ACTOR_WINS) {
|
||||||
|
y_ = ACTOR_HIT.y - HEIGHT;
|
||||||
|
transitionToState(State::ON_GROUND);
|
||||||
|
current_carrier_ = ACTOR_HIT.carrier;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cae libremente
|
||||||
y_ += displacement;
|
y_ += displacement;
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
if (y_ > PlayArea::BOTTOM + 100) { y_ = PlayArea::TOP + 2; }
|
if (y_ > PlayArea::BOTTOM + 100) { y_ = PlayArea::TOP + 2; }
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Player::moveVertical(float delta_time) {
|
||||||
|
if (state_ != State::ON_AIR) { return; }
|
||||||
|
const float DISPLACEMENT = vy_ * delta_time;
|
||||||
|
if (vy_ < 0.0F) {
|
||||||
|
moveVerticalUp(DISPLACEMENT);
|
||||||
|
} else if (vy_ > 0.0F) {
|
||||||
|
moveVerticalDown(DISPLACEMENT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,6 +483,7 @@ void Player::checkFalling() {
|
|||||||
if (state_ == State::ON_AIR) { return; }
|
if (state_ == State::ON_AIR) { return; }
|
||||||
|
|
||||||
const auto& tc = room_->getTileCollider();
|
const auto& tc = room_->getTileCollider();
|
||||||
|
const auto& sm = room_->getSolidActors();
|
||||||
|
|
||||||
if (state_ == State::ON_SLOPE) {
|
if (state_ == State::ON_SLOPE) {
|
||||||
// Verificar que el tile de slope sigue existiendo
|
// Verificar que el tile de slope sigue existiendo
|
||||||
@@ -415,13 +495,14 @@ void Player::checkFalling() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ON_GROUND: si está sobre una plataforma móvil, no comprobar tiles
|
// ON_GROUND: comprobar si sigue habiendo suelo (tile o solid actor).
|
||||||
if (on_platform_) { return; }
|
// El tilemap extendido incluye tiles de las rooms adyacentes, así que
|
||||||
|
// no hace falta cross-room para tiles.
|
||||||
// ON_GROUND: comprobar si sigue habiendo suelo (el tilemap extendido
|
|
||||||
// incluye tiles de las rooms adyacentes, así que no hace falta cross-room)
|
|
||||||
float foot_y = y_ + HEIGHT;
|
float foot_y = y_ + HEIGHT;
|
||||||
if (!tc.hasGroundBelow(x_, foot_y, WIDTH)) {
|
const bool TILE_GROUND = tc.hasGroundBelow(x_, foot_y, WIDTH);
|
||||||
|
const bool ACTOR_GROUND = sm.hasGroundBelow(x_, foot_y, WIDTH);
|
||||||
|
|
||||||
|
if (!TILE_GROUND && !ACTOR_GROUND) {
|
||||||
// Sticking: si no hay suelo pero hay slope debajo, snapear a ella
|
// Sticking: si no hay suelo pero hay slope debajo, snapear a ella
|
||||||
// para transición suave suelo→slope (bajada de rampas sin caer)
|
// para transición suave suelo→slope (bajada de rampas sin caer)
|
||||||
auto slope = tc.checkSlopeBelow(x_, foot_y, WIDTH);
|
auto slope = tc.checkSlopeBelow(x_, foot_y, WIDTH);
|
||||||
@@ -436,6 +517,16 @@ void Player::checkFalling() {
|
|||||||
|
|
||||||
vy_ = 0.0F;
|
vy_ = 0.0F;
|
||||||
transitionToState(State::ON_AIR);
|
transitionToState(State::ON_AIR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refrescar current_carrier_ para la próxima iteración: si hay actor
|
||||||
|
// debajo, comprobar si lleva CARRY_ON_TOP y guardarlo; si no, limpiarlo.
|
||||||
|
if (ACTOR_GROUND) {
|
||||||
|
auto hit = sm.checkFloor(x_, foot_y, WIDTH, foot_y + 1.0F);
|
||||||
|
current_carrier_ = hit.carrier;
|
||||||
|
} else {
|
||||||
|
current_carrier_ = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,6 +558,7 @@ void Player::transitionToState(State state) {
|
|||||||
break;
|
break;
|
||||||
case State::ON_AIR:
|
case State::ON_AIR:
|
||||||
last_grounded_position_ = static_cast<int>(y_);
|
last_grounded_position_ = static_cast<int>(y_);
|
||||||
|
current_carrier_ = nullptr; // Perder carry al despegar
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -518,18 +610,6 @@ void Player::syncSpriteAndCollider() {
|
|||||||
collider_box_ = getRect();
|
collider_box_ = getRect();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aplica el desplazamiento de una plataforma móvil al jugador
|
|
||||||
void Player::applyPlatformDisplacement(float dx, float surface_y) {
|
|
||||||
y_ = surface_y - HEIGHT; // Snap vertical al top de la plataforma
|
|
||||||
x_ += dx; // Desplazamiento horizontal
|
|
||||||
vy_ = 0.0F;
|
|
||||||
on_platform_ = true;
|
|
||||||
if (state_ != State::ON_GROUND) {
|
|
||||||
transitionToState(State::ON_GROUND);
|
|
||||||
}
|
|
||||||
syncSpriteAndCollider();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Player::placeSprite() {
|
void Player::placeSprite() {
|
||||||
sprite_->setPos(x_, y_);
|
sprite_->setPos(x_, y_);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "game/options.hpp" // Para Cheat, Options
|
#include "game/options.hpp" // Para Cheat, Options
|
||||||
#include "utils/defines.hpp" // Para PlayArea, Tile, Flip
|
#include "utils/defines.hpp" // Para PlayArea, Tile, Flip
|
||||||
struct JA_Sound_t;
|
struct JA_Sound_t;
|
||||||
|
class SolidActor;
|
||||||
|
|
||||||
class Player {
|
class Player {
|
||||||
public:
|
public:
|
||||||
@@ -72,8 +73,6 @@ class Player {
|
|||||||
void setRoom(std::shared_ptr<Room> room) { room_ = std::move(room); }
|
void setRoom(std::shared_ptr<Room> room) { room_ = std::move(room); }
|
||||||
[[nodiscard]] auto isAlive() const -> bool { return is_alive_; }
|
[[nodiscard]] auto isAlive() const -> bool { return is_alive_; }
|
||||||
[[nodiscard]] auto getVY() const -> float { return vy_; }
|
[[nodiscard]] auto getVY() const -> float { return vy_; }
|
||||||
void applyPlatformDisplacement(float dx, float surface_y);
|
|
||||||
void clearPlatformFlag() { on_platform_ = false; }
|
|
||||||
|
|
||||||
void setPaused(bool value) { is_paused_ = value; }
|
void setPaused(bool value) { is_paused_ = value; }
|
||||||
void setIgnoreInput(bool value) { ignore_input_ = value; }
|
void setIgnoreInput(bool value) { ignore_input_ = value; }
|
||||||
@@ -119,7 +118,7 @@ class Player {
|
|||||||
bool is_alive_ = true;
|
bool is_alive_ = true;
|
||||||
bool is_paused_ = false;
|
bool is_paused_ = false;
|
||||||
bool ignore_input_ = false;
|
bool ignore_input_ = false;
|
||||||
bool on_platform_ = false;
|
SolidActor* current_carrier_ = nullptr; // Actor con CARRY_ON_TOP sobre el que estamos de pie
|
||||||
bool turning_ = false;
|
bool turning_ = false;
|
||||||
Direction facing_ = Direction::RIGHT;
|
Direction facing_ = Direction::RIGHT;
|
||||||
Room::Border border_ = Room::Border::TOP;
|
Room::Border border_ = Room::Border::TOP;
|
||||||
@@ -134,8 +133,11 @@ class Player {
|
|||||||
void updateVelocity(float delta_time);
|
void updateVelocity(float delta_time);
|
||||||
void applyGravity(float delta_time);
|
void applyGravity(float delta_time);
|
||||||
void handleJumpAndDrop();
|
void handleJumpAndDrop();
|
||||||
|
[[nodiscard]] auto stuckAgainstWall() const -> bool;
|
||||||
void moveHorizontal(float delta_time);
|
void moveHorizontal(float delta_time);
|
||||||
void moveVertical(float delta_time);
|
void moveVertical(float delta_time);
|
||||||
|
void moveVerticalUp(float displacement);
|
||||||
|
void moveVerticalDown(float displacement);
|
||||||
void followSlope();
|
void followSlope();
|
||||||
void exitSlope();
|
void exitSlope();
|
||||||
void detectSlopeEntry();
|
void detectSlopeEntry();
|
||||||
|
|||||||
53
source/game/entities/solid_actor.hpp
Normal file
53
source/game/entities/solid_actor.hpp
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clase base ligera para entidades con AABB dinámico de colisión.
|
||||||
|
*
|
||||||
|
* Un SolidActor expone un rectángulo (aabb_) al SolidActorManager, que lo
|
||||||
|
* usa para los sweeps del Player y otras queries. No es polimórfica: no
|
||||||
|
* tiene métodos virtuales (las entidades concretas viven en sus propios
|
||||||
|
* managers, que las actualizan y las renderizan). La base solo unifica
|
||||||
|
* el "bit de colisión" — AABB, flags y delta del último frame.
|
||||||
|
*
|
||||||
|
* Hereda de esta clase cualquier entidad que necesite participar en la
|
||||||
|
* resolución de colisión del Player: puertas, plataformas móviles,
|
||||||
|
* bloques empujables, ascensores, compuertas, etc.
|
||||||
|
*
|
||||||
|
* Las flags determinan el comportamiento del actor ante el sweep:
|
||||||
|
* - BLOCKS_PLAYER: el AABB bloquea al Player como un muro en las 4 dirs.
|
||||||
|
* - CARRY_ON_TOP: si el Player está de pie encima, aplica last_delta_.x.
|
||||||
|
* - ONEWAY_TOP: solo bloquea desde arriba (jump-through desde abajo).
|
||||||
|
* - KILLS_ON_CRUSH: (futuro) si aplasta al Player contra tile sólido, mata.
|
||||||
|
*/
|
||||||
|
class SolidActor {
|
||||||
|
public:
|
||||||
|
enum Flags : uint32_t {
|
||||||
|
BLOCKS_PLAYER = 1U << 0U,
|
||||||
|
CARRY_ON_TOP = 1U << 1U,
|
||||||
|
ONEWAY_TOP = 1U << 2U,
|
||||||
|
KILLS_ON_CRUSH = 1U << 3U,
|
||||||
|
};
|
||||||
|
|
||||||
|
SolidActor() = default;
|
||||||
|
SolidActor(const SolidActor&) = delete;
|
||||||
|
auto operator=(const SolidActor&) -> SolidActor& = delete;
|
||||||
|
SolidActor(SolidActor&&) = delete;
|
||||||
|
auto operator=(SolidActor&&) -> SolidActor& = delete;
|
||||||
|
~SolidActor() = default;
|
||||||
|
|
||||||
|
[[nodiscard]] auto getAABB() const -> const SDL_FRect& { return aabb_; }
|
||||||
|
[[nodiscard]] auto getFlags() const -> uint32_t { return flags_; }
|
||||||
|
[[nodiscard]] auto getLastDelta() const -> SDL_FPoint { return last_delta_; }
|
||||||
|
[[nodiscard]] auto isBlocking() const -> bool { return (flags_ & BLOCKS_PLAYER) != 0U; }
|
||||||
|
[[nodiscard]] auto carriesOnTop() const -> bool { return (flags_ & CARRY_ON_TOP) != 0U; }
|
||||||
|
[[nodiscard]] auto isOneWayTop() const -> bool { return (flags_ & ONEWAY_TOP) != 0U; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
SDL_FRect aabb_{}; // Rectángulo de colisión (room-local pixel space)
|
||||||
|
SDL_FPoint last_delta_{}; // (dx, dy) del último frame — para carry horizontal
|
||||||
|
uint32_t flags_{0};
|
||||||
|
};
|
||||||
@@ -3,31 +3,30 @@
|
|||||||
#include <utility> // Para std::move
|
#include <utility> // Para std::move
|
||||||
|
|
||||||
#include "game/entities/door.hpp" // Para Door
|
#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/door_tracker.hpp" // Para DoorTracker
|
||||||
#include "game/gameplay/inventory.hpp" // Para Inventory
|
#include "game/gameplay/inventory.hpp" // Para Inventory
|
||||||
#include "game/gameplay/tile_collider.hpp" // Para TileCollider::Tile
|
#include "game/gameplay/solid_actor_manager.hpp" // Para SolidActorManager
|
||||||
#include "utils/defines.hpp" // Para Map::WIDTH, Tile::SIZE
|
|
||||||
#include "utils/utils.hpp" // Para checkCollision
|
#include "utils/utils.hpp" // Para checkCollision
|
||||||
|
|
||||||
// Constructor
|
// 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)),
|
: 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
|
// Añade una puerta y la registra en el SolidActorManager. El bit
|
||||||
// en el CollisionMap. Si ya está OPENED (porque venía persistida del
|
// BLOCKS_PLAYER del propio Door determina si bloquea al Player (se setea en
|
||||||
// DoorTracker), no se tocan los tiles.
|
// el constructor si la puerta no arranca ya abierta desde el DoorTracker).
|
||||||
void DoorManager::addDoor(std::shared_ptr<Door> door) { // NOLINT(readability-identifier-naming)
|
void DoorManager::addDoor(std::shared_ptr<Door> door) { // NOLINT(readability-identifier-naming)
|
||||||
if (door->isBlocking()) {
|
solid_actors_->registerActor(door.get());
|
||||||
writeDoorTiles(*door, static_cast<int>(TileCollider::Tile::WALL));
|
|
||||||
}
|
|
||||||
doors_.push_back(std::move(door));
|
doors_.push_back(std::move(door));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Elimina todas las puertas
|
// Elimina todas las puertas y las desregistra del SolidActorManager
|
||||||
void DoorManager::clear() {
|
void DoorManager::clear() {
|
||||||
|
for (const auto& door : doors_) {
|
||||||
|
solid_actors_->unregisterActor(door.get());
|
||||||
|
}
|
||||||
doors_.clear();
|
doors_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,9 +35,10 @@ void DoorManager::update(float delta_time) {
|
|||||||
for (const auto& door : doors_) {
|
for (const auto& door : doors_) {
|
||||||
door->update(delta_time);
|
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()) {
|
if (door->justOpened()) {
|
||||||
writeDoorTiles(*door, static_cast<int>(TileCollider::Tile::EMPTY));
|
|
||||||
DoorTracker::get()->addDoor(room_id_, door->getPos());
|
DoorTracker::get()->addDoor(room_id_, door->getPos());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,48 +79,17 @@ void DoorManager::tryUnlock(const SDL_FRect& player_rect) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
// Mueve una puerta del editor: limpia los WALLs viejos, reposiciona el sprite,
|
// Mueve una puerta del editor: reposiciona su sprite y AABB. El registro
|
||||||
// y reescribe los WALLs nuevos si la puerta sigue siendo bloqueante.
|
// en el SolidActorManager se mantiene (el manager lee el AABB actual).
|
||||||
void DoorManager::moveDoor(int index, float x, float y) {
|
void DoorManager::moveDoor(int index, float x, float y) {
|
||||||
if (index < 0 || index >= static_cast<int>(doors_.size())) { return; }
|
if (index < 0 || index >= static_cast<int>(doors_.size())) { return; }
|
||||||
auto& door = doors_[index];
|
doors_[index]->setPosition(x, y);
|
||||||
|
|
||||||
// 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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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) {
|
void DoorManager::removeDoor(int index) {
|
||||||
if (index < 0 || index >= static_cast<int>(doors_.size())) { return; }
|
if (index < 0 || index >= static_cast<int>(doors_.size())) { return; }
|
||||||
auto& door = doors_[index];
|
solid_actors_->unregisterActor(doors_[index].get());
|
||||||
|
|
||||||
if (door->isBlocking()) {
|
|
||||||
writeDoorTiles(*door, static_cast<int>(TileCollider::Tile::EMPTY));
|
|
||||||
}
|
|
||||||
|
|
||||||
doors_.erase(doors_.begin() + index);
|
doors_.erase(doors_.begin() + index);
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
#include "game/entities/door.hpp" // Para Door, Door::Data
|
||||||
|
|
||||||
class CollisionMap;
|
class SolidActorManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Gestor de puertas de una habitación
|
* @brief Gestor de puertas de una habitación
|
||||||
@@ -18,14 +18,14 @@ class CollisionMap;
|
|||||||
* - Actualizar y renderizar todas las puertas
|
* - Actualizar y renderizar todas las puertas
|
||||||
* - Detectar contacto del jugador con puertas cerradas y disparar la apertura
|
* - Detectar contacto del jugador con puertas cerradas y disparar la apertura
|
||||||
* si tiene la llave correspondiente (consultando el Inventory global)
|
* si tiene la llave correspondiente (consultando el Inventory global)
|
||||||
* - Sincronizar el estado bloqueante con el CollisionMap: cuando una puerta
|
* - Registrar cada puerta en el SolidActorManager para que sus AABBs
|
||||||
* está CLOSED u OPENING, sus 4 tiles son WALL; cuando pasa a OPENED, se
|
* participen en los sweeps de colisión del Player. El bit BLOCKS_PLAYER
|
||||||
* ponen a EMPTY
|
* del propio Door se encarga de activar/desactivar el bloqueo al abrir.
|
||||||
* - Persistir el estado abierto en DoorTracker
|
* - Persistir el estado abierto en DoorTracker
|
||||||
*/
|
*/
|
||||||
class DoorManager {
|
class DoorManager {
|
||||||
public:
|
public:
|
||||||
DoorManager(std::string room_id, CollisionMap* collision_map);
|
DoorManager(std::string room_id, SolidActorManager* solid_actors);
|
||||||
~DoorManager() = default;
|
~DoorManager() = default;
|
||||||
|
|
||||||
// Prohibir copia y movimiento para evitar duplicación accidental
|
// Prohibir copia y movimiento para evitar duplicación accidental
|
||||||
@@ -35,7 +35,7 @@ class DoorManager {
|
|||||||
auto operator=(DoorManager&&) -> DoorManager& = delete;
|
auto operator=(DoorManager&&) -> DoorManager& = delete;
|
||||||
|
|
||||||
// Gestión de puertas
|
// 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
|
void clear(); // Elimina todas las puertas
|
||||||
|
|
||||||
// Actualización y renderizado
|
// Actualización y renderizado
|
||||||
@@ -66,24 +66,20 @@ class DoorManager {
|
|||||||
/**
|
/**
|
||||||
* @brief Mueve la puerta indicada a (x, y) en píxeles
|
* @brief Mueve la puerta indicada a (x, y) en píxeles
|
||||||
*
|
*
|
||||||
* Limpia los WALLs viejos del CollisionMap y, si la puerta sigue siendo
|
* Reposiciona el sprite y el AABB de la puerta. El registro en el
|
||||||
* bloqueante, escribe los nuevos. Encapsula el bookkeeping de tiles para
|
* SolidActorManager no cambia: el manager siempre lee el AABB actual
|
||||||
* que el editor nunca toque el CollisionMap directamente.
|
* del SolidActor durante los sweeps.
|
||||||
*/
|
*/
|
||||||
void moveDoor(int index, float x, float y);
|
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);
|
void removeDoor(int index);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
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::vector<std::shared_ptr<Door>> doors_; // Colección de puertas
|
||||||
std::string room_id_; // Identificador de la habitación
|
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 "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) {
|
void PlatformManager::addPlatform(std::shared_ptr<MovingPlatform> platform) {
|
||||||
|
solid_actors_->registerActor(platform.get());
|
||||||
platforms_.push_back(std::move(platform));
|
platforms_.push_back(std::move(platform));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Elimina todas las plataformas
|
// Elimina todas las plataformas y las desregistra del SolidActorManager
|
||||||
void PlatformManager::clear() {
|
void PlatformManager::clear() {
|
||||||
|
for (const auto& platform : platforms_) {
|
||||||
|
solid_actors_->unregisterActor(platform.get());
|
||||||
|
}
|
||||||
platforms_.clear();
|
platforms_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,40 +69,11 @@ auto PlatformManager::getPlatform(int index) -> std::shared_ptr<MovingPlatform>&
|
|||||||
return platforms_.at(index);
|
return platforms_.at(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Elimina la última plataforma
|
// Elimina la última plataforma (y la desregistra del SolidActorManager)
|
||||||
void PlatformManager::removeLastPlatform() {
|
void PlatformManager::removeLastPlatform() {
|
||||||
if (!platforms_.empty()) {
|
if (!platforms_.empty()) {
|
||||||
|
solid_actors_->unregisterActor(platforms_.back().get());
|
||||||
platforms_.pop_back();
|
platforms_.pop_back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
#include "game/entities/moving_platform.hpp" // Para MovingPlatform, MovingPlatform::Data
|
||||||
|
|
||||||
|
class SolidActorManager;
|
||||||
|
|
||||||
class PlatformManager {
|
class PlatformManager {
|
||||||
public:
|
public:
|
||||||
PlatformManager() = default;
|
explicit PlatformManager(SolidActorManager* solid_actors);
|
||||||
~PlatformManager() = default;
|
~PlatformManager() = default;
|
||||||
|
|
||||||
// Prohibir copia y movimiento
|
// Prohibir copia y movimiento
|
||||||
@@ -27,10 +29,6 @@ class PlatformManager {
|
|||||||
void update(float delta_time);
|
void update(float delta_time);
|
||||||
void render();
|
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
|
#ifdef _DEBUG
|
||||||
void updateAnimations(float delta_time);
|
void updateAnimations(float delta_time);
|
||||||
void resetPositions(const std::vector<MovingPlatform::Data>& platform_data);
|
void resetPositions(const std::vector<MovingPlatform::Data>& platform_data);
|
||||||
@@ -41,4 +39,5 @@ class PlatformManager {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::shared_ptr<MovingPlatform>> platforms_;
|
std::vector<std::shared_ptr<MovingPlatform>> platforms_;
|
||||||
|
SolidActorManager* solid_actors_{nullptr}; // Referencia no propietaria al SolidActorManager de la Room
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "game/gameplay/platform_manager.hpp" // Para PlatformManager
|
#include "game/gameplay/platform_manager.hpp" // Para PlatformManager
|
||||||
#include "game/gameplay/room_format.hpp" // Para RoomFormat
|
#include "game/gameplay/room_format.hpp" // Para RoomFormat
|
||||||
#include "game/gameplay/scoreboard.hpp" // Para Scoreboard::Data
|
#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 "game/gameplay/tilemap_renderer.hpp" // Para TilemapRenderer
|
||||||
#include "utils/defines.hpp" // Para TILE_SIZE
|
#include "utils/defines.hpp" // Para TILE_SIZE
|
||||||
#include "utils/utils.hpp"
|
#include "utils/utils.hpp"
|
||||||
@@ -25,18 +26,24 @@ Room::Room(const std::string& room_path, std::shared_ptr<Scoreboard::Data> data)
|
|||||||
: data_(std::move(data)) {
|
: data_(std::move(data)) {
|
||||||
auto room = Resource::Cache::get()->getRoom(room_path);
|
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
|
// Crea los managers de enemigos, items, plataformas y llaves
|
||||||
enemy_manager_ = std::make_unique<EnemyManager>();
|
enemy_manager_ = std::make_unique<EnemyManager>();
|
||||||
item_manager_ = std::make_unique<ItemManager>(room->number, data_);
|
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);
|
key_manager_ = std::make_unique<KeyManager>(room->number);
|
||||||
|
|
||||||
// Crea el mapa de colisiones desde el collision_tile_map (debe ir antes
|
// Crea el mapa de colisiones desde el collision_tile_map
|
||||||
// del DoorManager porque éste lo necesita para mutar tiles dinámicamente)
|
|
||||||
collision_map_ = std::make_unique<CollisionMap>(room->collision_tile_map);
|
collision_map_ = std::make_unique<CollisionMap>(room->collision_tile_map);
|
||||||
|
|
||||||
// Crea el manager de puertas (necesita el CollisionMap para sincronizar muros)
|
// Crea el manager de puertas (registra los Door como SolidActor en el
|
||||||
door_manager_ = std::make_unique<DoorManager>(room->number, collision_map_.get());
|
// SolidActorManager; ya no escribe tiles en el CollisionMap).
|
||||||
|
door_manager_ = std::make_unique<DoorManager>(room->number, solid_actor_manager_.get());
|
||||||
|
|
||||||
initializeRoom(*room);
|
initializeRoom(*room);
|
||||||
|
|
||||||
@@ -250,6 +257,14 @@ auto Room::getTileCollider() const -> const TileCollider& {
|
|||||||
return collision_map_->getTileCollider();
|
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>& {
|
auto Room::getCollisionTileMap() const -> const std::vector<int>& {
|
||||||
return collision_map_->getCollisionTileMap();
|
return collision_map_->getCollisionTileMap();
|
||||||
}
|
}
|
||||||
@@ -258,6 +273,10 @@ void Room::updateCollisionBorders(const CollisionMap::AdjacentData& adjacent) {
|
|||||||
collision_map_->updateBorders(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
|
// 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)
|
auto Room::getRoom(Border border) -> std::string { // NOLINT(readability-convert-member-functions-to-static)
|
||||||
switch (border) {
|
switch (border) {
|
||||||
@@ -290,10 +309,6 @@ void Room::tryUnlockDoors(const SDL_FRect& player_rect) {
|
|||||||
door_manager_->tryUnlock(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)
|
// 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)
|
auto Room::loadYAML(const std::string& file_path, bool verbose) -> Data { // NOLINT(readability-convert-member-functions-to-static)
|
||||||
return RoomFormat::loadYAML(file_path, verbose);
|
return RoomFormat::loadYAML(file_path, verbose);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include "game/entities/moving_platform.hpp" // Para MovingPlatform::Data
|
#include "game/entities/moving_platform.hpp" // Para MovingPlatform::Data
|
||||||
#include "game/gameplay/collision_map.hpp" // Para CollisionMap::AdjacentData
|
#include "game/gameplay/collision_map.hpp" // Para CollisionMap::AdjacentData
|
||||||
#include "game/gameplay/scoreboard.hpp" // Para Scoreboard::Data
|
#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
|
#include "utils/defines.hpp" // Para Tile::SIZE, Map::WIDTH, Map::HEIGHT
|
||||||
class Surface;
|
class Surface;
|
||||||
class EnemyManager;
|
class EnemyManager;
|
||||||
@@ -82,6 +83,7 @@ class Room {
|
|||||||
auto getPlatformManager() -> PlatformManager* { return platform_manager_.get(); }
|
auto getPlatformManager() -> PlatformManager* { return platform_manager_.get(); }
|
||||||
auto getKeyManager() -> KeyManager* { return key_manager_.get(); }
|
auto getKeyManager() -> KeyManager* { return key_manager_.get(); }
|
||||||
auto getDoorManager() -> DoorManager* { return door_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 setTile(int index, int tile_value);
|
||||||
void setCollisionTile(int index, int value);
|
void setCollisionTile(int index, int value);
|
||||||
void setConnection(Border border, const std::string& room_name);
|
void setConnection(Border border, const std::string& room_name);
|
||||||
@@ -95,11 +97,13 @@ class Room {
|
|||||||
auto itemCollision(SDL_FRect& rect) -> bool;
|
auto itemCollision(SDL_FRect& rect) -> bool;
|
||||||
auto keyCollision(SDL_FRect& rect) -> bool;
|
auto keyCollision(SDL_FRect& rect) -> bool;
|
||||||
void tryUnlockDoors(const SDL_FRect& player_rect);
|
void tryUnlockDoors(const SDL_FRect& player_rect);
|
||||||
auto checkPlayerOnPlatform(const SDL_FRect& player_collider, float player_vy) -> MovingPlatform*;
|
|
||||||
void setPaused(bool value);
|
void setPaused(bool value);
|
||||||
[[nodiscard]] auto getTileCollider() const -> const TileCollider&;
|
[[nodiscard]] auto getTileCollider() const -> const TileCollider&;
|
||||||
|
[[nodiscard]] auto getSolidActors() const -> const SolidActorManager&;
|
||||||
|
[[nodiscard]] auto getSolidActors() -> SolidActorManager&;
|
||||||
[[nodiscard]] auto getCollisionTileMap() const -> const std::vector<int>&;
|
[[nodiscard]] auto getCollisionTileMap() const -> const std::vector<int>&;
|
||||||
void updateCollisionBorders(const CollisionMap::AdjacentData& adjacent);
|
void updateCollisionBorders(const CollisionMap::AdjacentData& adjacent);
|
||||||
|
void updateSolidActorBorders(const SolidActorManager::AdjacentActors& adjacent);
|
||||||
|
|
||||||
// Método de carga de archivos YAML (delegado a RoomFormat)
|
// Método de carga de archivos YAML (delegado a RoomFormat)
|
||||||
static auto loadYAML(const std::string& file_path, bool verbose = false) -> Data;
|
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<PlatformManager> platform_manager_;
|
||||||
std::unique_ptr<KeyManager> key_manager_;
|
std::unique_ptr<KeyManager> key_manager_;
|
||||||
std::unique_ptr<DoorManager> door_manager_;
|
std::unique_ptr<DoorManager> door_manager_;
|
||||||
|
std::unique_ptr<SolidActorManager> solid_actor_manager_;
|
||||||
std::unique_ptr<CollisionMap> collision_map_;
|
std::unique_ptr<CollisionMap> collision_map_;
|
||||||
std::unique_ptr<TilemapRenderer> tilemap_renderer_;
|
std::unique_ptr<TilemapRenderer> tilemap_renderer_;
|
||||||
std::shared_ptr<Surface> surface_;
|
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_{};
|
||||||
|
};
|
||||||
@@ -319,9 +319,6 @@ void Game::updatePlaying(float delta_time) {
|
|||||||
updateAdjacentRooms(delta_time);
|
updateAdjacentRooms(delta_time);
|
||||||
switch (mode_) {
|
switch (mode_) {
|
||||||
case Mode::GAME:
|
case Mode::GAME:
|
||||||
// Plataformas: resetear flag y detectar antes de la física del player
|
|
||||||
player_->clearPlatformFlag();
|
|
||||||
checkPlayerAndPlatforms();
|
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
// Maneja el arrastre del jugador con el ratón (debug)
|
// Maneja el arrastre del jugador con el ratón (debug)
|
||||||
@@ -858,6 +855,24 @@ void Game::buildCollisionBorders() {
|
|||||||
adj.bottom_right = getDiagCollision(Room::Border::BOTTOM, Room::Border::RIGHT);
|
adj.bottom_right = getDiagCollision(Room::Border::BOTTOM, Room::Border::RIGHT);
|
||||||
|
|
||||||
room_->updateCollisionBorders(adj);
|
room_->updateCollisionBorders(adj);
|
||||||
|
|
||||||
|
// Además del tilemap extendido, propagar también los punteros a los
|
||||||
|
// SolidActorManager de las rooms cardinales adyacentes. Esto permite
|
||||||
|
// que los sweeps del Player vean AABBs dinámicos (puertas, plataformas)
|
||||||
|
// de la room vecina cuando está cerca del borde, sin tener que esperar
|
||||||
|
// a una transición completa de room.
|
||||||
|
auto getAdjacentSolidActors = [&](Room::Border b) -> SolidActorManager* {
|
||||||
|
auto name = room_->getRoom(b);
|
||||||
|
if (name == "0") { return nullptr; }
|
||||||
|
return &getOrCreateRoom(name)->getSolidActors();
|
||||||
|
};
|
||||||
|
|
||||||
|
SolidActorManager::AdjacentActors sadj;
|
||||||
|
sadj.upper = getAdjacentSolidActors(Room::Border::TOP);
|
||||||
|
sadj.lower = getAdjacentSolidActors(Room::Border::BOTTOM);
|
||||||
|
sadj.left = getAdjacentSolidActors(Room::Border::LEFT);
|
||||||
|
sadj.right = getAdjacentSolidActors(Room::Border::RIGHT);
|
||||||
|
room_->updateSolidActorBorders(sadj);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza los enemigos de las habitaciones adyacentes a la actual
|
// Actualiza los enemigos de las habitaciones adyacentes a la actual
|
||||||
@@ -950,14 +965,6 @@ auto Game::checkPlayerAndEnemies() -> bool {
|
|||||||
return DEATH;
|
return DEATH;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comprueba si el jugador está sobre una plataforma móvil y lo transporta
|
|
||||||
void Game::checkPlayerAndPlatforms() {
|
|
||||||
auto* platform = room_->checkPlayerOnPlatform(player_->getCollider(), player_->getVY());
|
|
||||||
if (platform != nullptr) {
|
|
||||||
player_->applyPlatformDisplacement(platform->getLastDX(), platform->getCollider().y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comprueba las colisiones del jugador con los objetos
|
// Comprueba las colisiones del jugador con los objetos
|
||||||
void Game::checkPlayerAndKeys() {
|
void Game::checkPlayerAndKeys() {
|
||||||
room_->keyCollision(player_->getCollider());
|
room_->keyCollision(player_->getCollider());
|
||||||
|
|||||||
@@ -75,7 +75,6 @@ class Game {
|
|||||||
void handleInput(); // Comprueba el teclado
|
void handleInput(); // Comprueba el teclado
|
||||||
void checkPlayerIsOnBorder(); // Comprueba si el jugador esta en el borde de la pantalla y actua
|
void checkPlayerIsOnBorder(); // Comprueba si el jugador esta en el borde de la pantalla y actua
|
||||||
auto checkPlayerAndEnemies() -> bool; // Comprueba las colisiones del jugador con los enemigos
|
auto checkPlayerAndEnemies() -> bool; // Comprueba las colisiones del jugador con los enemigos
|
||||||
void checkPlayerAndPlatforms(); // Comprueba si el jugador está sobre una plataforma móvil
|
|
||||||
void checkPlayerAndItems(); // Comprueba las colisiones del jugador con los objetos
|
void checkPlayerAndItems(); // Comprueba las colisiones del jugador con los objetos
|
||||||
void checkPlayerAndKeys(); // Comprueba las colisiones del jugador con las llaves
|
void checkPlayerAndKeys(); // Comprueba las colisiones del jugador con las llaves
|
||||||
void checkIfPlayerIsAlive(); // Comprueba si el jugador esta vivo
|
void checkIfPlayerIsAlive(); // Comprueba si el jugador esta vivo
|
||||||
|
|||||||
Reference in New Issue
Block a user