polimorfise d'enemics
moving platforms
This commit is contained in:
@@ -67,7 +67,9 @@ set(APP_SOURCES
|
||||
|
||||
# Game - Entities
|
||||
source/game/entities/enemy.cpp
|
||||
source/game/entities/path_enemy.cpp
|
||||
source/game/entities/item.cpp
|
||||
source/game/entities/moving_platform.cpp
|
||||
source/game/entities/player.cpp
|
||||
|
||||
# Game - Configuration
|
||||
@@ -79,6 +81,7 @@ set(APP_SOURCES
|
||||
source/game/gameplay/tile_collider.cpp
|
||||
source/game/gameplay/enemy_manager.cpp
|
||||
source/game/gameplay/item_manager.cpp
|
||||
source/game/gameplay/platform_manager.cpp
|
||||
source/game/gameplay/item_tracker.cpp
|
||||
source/game/gameplay/room_loader.cpp
|
||||
source/game/gameplay/room_tracker.cpp
|
||||
|
||||
@@ -83,6 +83,16 @@ enemies:
|
||||
color: 10
|
||||
flip: true
|
||||
|
||||
# Plataformas móviles en esta habitación
|
||||
platforms:
|
||||
- animation: bin.yaml
|
||||
position: {x: 5, y: 17}
|
||||
velocity: {x: 25, y: 0}
|
||||
boundaries:
|
||||
position1: {x: 3, y: 17}
|
||||
position2: {x: 20, y: 17}
|
||||
frame: 0
|
||||
|
||||
# Objetos en esta habitación
|
||||
items:
|
||||
- tileSetFile: items.gif
|
||||
|
||||
@@ -195,6 +195,7 @@ void MapEditor::enter(std::shared_ptr<Room> room, std::shared_ptr<Player> player
|
||||
yaml_backup_ = yaml_; // Copia profunda para revert
|
||||
}
|
||||
|
||||
bool is_reenter = reenter_;
|
||||
if (!reenter_) {
|
||||
// Solo guardar estado previo en el primer enter (no en re-enter tras cambio de room)
|
||||
invincible_before_editor_ = Options::cheats.invincible;
|
||||
@@ -219,13 +220,16 @@ void MapEditor::enter(std::shared_ptr<Room> room, std::shared_ptr<Player> player
|
||||
// Crear la barra de estado
|
||||
statusbar_ = std::make_unique<EditorStatusBar>(room_->getNumber());
|
||||
|
||||
// Resetear estado
|
||||
// Resetear estado (preservar modo de edición en re-enter)
|
||||
drag_ = {};
|
||||
selected_enemy_ = -1;
|
||||
selected_item_ = -1;
|
||||
brush_tile_ = NO_BRUSH;
|
||||
painting_ = false;
|
||||
editing_collision_ = false;
|
||||
if (!is_reenter) {
|
||||
brush_tile_ = NO_BRUSH;
|
||||
painting_ = false;
|
||||
editing_collision_ = false;
|
||||
}
|
||||
painting_ = false; // Siempre dejar de pintar al cambiar de room
|
||||
|
||||
// Asegurar que collision_tile_map tiene el tamaño correcto
|
||||
if (room_data_.collision_tile_map.size() != static_cast<size_t>(Map::WIDTH * Map::HEIGHT)) {
|
||||
@@ -1053,7 +1057,7 @@ auto MapEditor::setEnemyProperty(const std::string& property, const std::string&
|
||||
enemy.animation_path = anim;
|
||||
try {
|
||||
auto* enemy_mgr = room_->getEnemyManager();
|
||||
enemy_mgr->getEnemy(selected_enemy_) = std::make_shared<Enemy>(enemy);
|
||||
enemy_mgr->getEnemy(selected_enemy_) = Enemy::create(enemy);
|
||||
} catch (const std::exception& e) {
|
||||
enemy.animation_path = old_anim; // Restaurar si falla
|
||||
return std::string("Error: ") + e.what();
|
||||
@@ -1096,7 +1100,7 @@ auto MapEditor::setEnemyProperty(const std::string& property, const std::string&
|
||||
// Recrear el enemigo (flip/mirror se aplican en el constructor)
|
||||
try {
|
||||
auto* enemy_mgr = room_->getEnemyManager();
|
||||
enemy_mgr->getEnemy(selected_enemy_) = std::make_shared<Enemy>(enemy);
|
||||
enemy_mgr->getEnemy(selected_enemy_) = Enemy::create(enemy);
|
||||
} catch (const std::exception& e) {
|
||||
return std::string("Error: ") + e.what();
|
||||
}
|
||||
@@ -1112,7 +1116,7 @@ auto MapEditor::setEnemyProperty(const std::string& property, const std::string&
|
||||
// Recrear el enemigo (flip/mirror se aplican en el constructor)
|
||||
try {
|
||||
auto* enemy_mgr = room_->getEnemyManager();
|
||||
enemy_mgr->getEnemy(selected_enemy_) = std::make_shared<Enemy>(enemy);
|
||||
enemy_mgr->getEnemy(selected_enemy_) = Enemy::create(enemy);
|
||||
} catch (const std::exception& e) {
|
||||
return std::string("Error: ") + e.what();
|
||||
}
|
||||
@@ -1147,7 +1151,7 @@ auto MapEditor::addEnemy() -> std::string {
|
||||
|
||||
// Añadir a los datos y crear el sprite vivo
|
||||
room_data_.enemies.push_back(new_enemy);
|
||||
room_->getEnemyManager()->addEnemy(std::make_shared<Enemy>(new_enemy));
|
||||
room_->getEnemyManager()->addEnemy(Enemy::create(new_enemy));
|
||||
|
||||
// Seleccionar el nuevo enemigo
|
||||
selected_enemy_ = static_cast<int>(room_data_.enemies.size()) - 1;
|
||||
@@ -1170,7 +1174,7 @@ auto MapEditor::deleteEnemy() -> std::string {
|
||||
auto* enemy_mgr = room_->getEnemyManager();
|
||||
enemy_mgr->clear();
|
||||
for (const auto& enemy_data : room_data_.enemies) {
|
||||
enemy_mgr->addEnemy(std::make_shared<Enemy>(enemy_data));
|
||||
enemy_mgr->addEnemy(Enemy::create(enemy_data));
|
||||
}
|
||||
|
||||
selected_enemy_ = -1;
|
||||
@@ -1193,7 +1197,7 @@ auto MapEditor::duplicateEnemy() -> std::string {
|
||||
|
||||
// Añadir y crear sprite vivo
|
||||
room_data_.enemies.push_back(copy);
|
||||
room_->getEnemyManager()->addEnemy(std::make_shared<Enemy>(copy));
|
||||
room_->getEnemyManager()->addEnemy(Enemy::create(copy));
|
||||
|
||||
// Seleccionar el nuevo enemigo
|
||||
selected_enemy_ = static_cast<int>(room_data_.enemies.size()) - 1;
|
||||
|
||||
@@ -112,6 +112,7 @@ auto RoomSaver::buildYAML(const fkyaml::node& original_yaml, const Room::Data& r
|
||||
out << "enemies:\n";
|
||||
for (const auto& enemy : room_data.enemies) {
|
||||
out << " - animation: " << enemy.animation_path << "\n";
|
||||
if (enemy.type != "path") { out << " type: " << enemy.type << "\n"; }
|
||||
|
||||
int pos_x = static_cast<int>(std::round(enemy.x / Tile::SIZE));
|
||||
int pos_y = static_cast<int>(std::round(enemy.y / Tile::SIZE));
|
||||
@@ -155,6 +156,33 @@ auto RoomSaver::buildYAML(const fkyaml::node& original_yaml, const Room::Data& r
|
||||
}
|
||||
}
|
||||
|
||||
// --- Plataformas ---
|
||||
if (!room_data.platforms.empty()) {
|
||||
out << "# Plataformas móviles en esta habitación\n";
|
||||
out << "platforms:\n";
|
||||
for (const auto& plat : room_data.platforms) {
|
||||
out << " - animation: " << plat.animation_path << "\n";
|
||||
|
||||
int pos_x = static_cast<int>(std::round(plat.x / Tile::SIZE));
|
||||
int pos_y = static_cast<int>(std::round(plat.y / Tile::SIZE));
|
||||
out << " position: {x: " << pos_x << ", y: " << pos_y << "}\n";
|
||||
|
||||
out << " velocity: {x: " << plat.vx << ", y: " << plat.vy << "}\n";
|
||||
|
||||
int b1_x = plat.x1 / Tile::SIZE;
|
||||
int b1_y = plat.y1 / Tile::SIZE;
|
||||
int b2_x = plat.x2 / Tile::SIZE;
|
||||
int b2_y = plat.y2 / Tile::SIZE;
|
||||
out << " boundaries:\n";
|
||||
out << " position1: {x: " << b1_x << ", y: " << b1_y << "}\n";
|
||||
out << " position2: {x: " << b2_x << ", y: " << b2_y << "}\n";
|
||||
|
||||
if (plat.frame != -1) { out << " frame: " << plat.frame << "\n"; }
|
||||
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return out.str();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,18 +6,13 @@
|
||||
|
||||
#include "core/rendering/sprite/animated_sprite.hpp" // Para SAnimatedSprite
|
||||
#include "core/resources/resource_cache.hpp" // Para Resource
|
||||
#include "utils/utils.hpp"
|
||||
#include "game/entities/path_enemy.hpp" // Para PathEnemy
|
||||
|
||||
// Constructor
|
||||
// Constructor base: configura sprite, collider, flip/mirror
|
||||
Enemy::Enemy(const Data& enemy)
|
||||
: sprite_(std::make_shared<AnimatedSprite>(Resource::Cache::get()->getAnimationData(enemy.animation_path))),
|
||||
x1_(enemy.x1),
|
||||
x2_(enemy.x2),
|
||||
y1_(enemy.y1),
|
||||
y2_(enemy.y2),
|
||||
should_flip_(enemy.flip),
|
||||
should_mirror_(enemy.mirror) {
|
||||
// Obten el resto de valores
|
||||
sprite_->setPosX(enemy.x);
|
||||
sprite_->setPosY(enemy.y);
|
||||
sprite_->setVelX(enemy.vx);
|
||||
@@ -36,74 +31,13 @@ void Enemy::render() {
|
||||
sprite_->render();
|
||||
}
|
||||
|
||||
// Actualiza las variables del objeto
|
||||
void Enemy::update(float delta_time) {
|
||||
sprite_->update(delta_time);
|
||||
checkPath();
|
||||
collider_ = getRect();
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Solo actualiza la animación sin mover al enemigo
|
||||
void Enemy::updateAnimation(float delta_time) {
|
||||
sprite_->animate(delta_time);
|
||||
}
|
||||
|
||||
// Resetea el enemigo a su posición inicial (para editor)
|
||||
void Enemy::resetToInitialPosition(const Data& data) {
|
||||
sprite_->setPosX(data.x);
|
||||
sprite_->setPosY(data.y);
|
||||
sprite_->setVelX(data.vx);
|
||||
sprite_->setVelY(data.vy);
|
||||
|
||||
x1_ = data.x1;
|
||||
x2_ = data.x2;
|
||||
y1_ = data.y1;
|
||||
y2_ = data.y2;
|
||||
|
||||
applyFlipMirror(data.vx);
|
||||
|
||||
collider_ = getRect();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Comprueba si ha llegado al limite del recorrido para darse media vuelta
|
||||
void Enemy::checkPath() { // NOLINT(readability-make-member-function-const)
|
||||
if (sprite_->getPosX() > x2_ || sprite_->getPosX() < x1_) {
|
||||
// Recoloca
|
||||
if (sprite_->getPosX() > x2_) {
|
||||
sprite_->setPosX(x2_);
|
||||
} else {
|
||||
sprite_->setPosX(x1_);
|
||||
}
|
||||
|
||||
// Cambia el sentido
|
||||
sprite_->setVelX(sprite_->getVelX() * (-1));
|
||||
|
||||
// Invierte el sprite
|
||||
if (should_flip_) {
|
||||
sprite_->flip();
|
||||
}
|
||||
}
|
||||
|
||||
if (sprite_->getPosY() > y2_ || sprite_->getPosY() < y1_) {
|
||||
// Recoloca
|
||||
if (sprite_->getPosY() > y2_) {
|
||||
sprite_->setPosY(y2_);
|
||||
} else {
|
||||
sprite_->setPosY(y1_);
|
||||
}
|
||||
|
||||
// Cambia el sentido
|
||||
sprite_->setVelY(sprite_->getVelY() * (-1));
|
||||
|
||||
// Invierte el sprite
|
||||
if (should_flip_) {
|
||||
sprite_->flip();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Aplica flip horizontal y/o mirror vertical al sprite
|
||||
void Enemy::applyFlipMirror(float vx) {
|
||||
const int FLIP = (should_flip_ && vx < 0.0F) ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE;
|
||||
@@ -120,3 +54,9 @@ auto Enemy::getRect() -> SDL_FRect {
|
||||
auto Enemy::getCollider() -> SDL_FRect& {
|
||||
return collider_;
|
||||
}
|
||||
|
||||
// Factory: crea el subtipo de enemigo correcto según data.type
|
||||
auto Enemy::create(const Data& data) -> std::shared_ptr<Enemy> {
|
||||
// Por ahora solo existe PathEnemy; futuros tipos se añadirán aquí
|
||||
return std::make_shared<PathEnemy>(data);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ class AnimatedSprite;
|
||||
class Enemy {
|
||||
public:
|
||||
struct Data {
|
||||
std::string type{"path"}; // Tipo de enemigo (path, etc.)
|
||||
std::string animation_path; // Ruta al fichero con la animación
|
||||
float x{0.0F}; // Posición inicial en el eje X
|
||||
float y{0.0F}; // Posición inicial en el eje Y
|
||||
@@ -25,30 +26,26 @@ class Enemy {
|
||||
};
|
||||
|
||||
explicit Enemy(const Data& enemy); // Constructor
|
||||
~Enemy() = default; // Destructor
|
||||
virtual ~Enemy() = default; // Destructor virtual
|
||||
|
||||
void render(); // Pinta el enemigo en pantalla
|
||||
void update(float delta_time); // Actualiza las variables del objeto
|
||||
virtual void render(); // Pinta el enemigo en pantalla
|
||||
virtual void update(float delta_time) = 0; // Actualiza las variables del objeto (pura virtual)
|
||||
#ifdef _DEBUG
|
||||
void updateAnimation(float delta_time); // Solo actualiza la animación sin mover al enemigo
|
||||
void resetToInitialPosition(const Data& data); // Resetea el enemigo a su posición inicial (para editor)
|
||||
virtual void updateAnimation(float delta_time); // Solo actualiza la animación sin mover al enemigo
|
||||
virtual void resetToInitialPosition(const Data& data) = 0; // Resetea a posición inicial (pura virtual)
|
||||
#endif
|
||||
|
||||
auto getRect() -> SDL_FRect; // Devuelve el rectangulo que contiene al enemigo
|
||||
auto getCollider() -> SDL_FRect&; // Obtiene el rectangulo de colision del enemigo
|
||||
|
||||
private:
|
||||
// Factory: crea el subtipo de enemigo correcto según data.type
|
||||
static auto create(const Data& data) -> std::shared_ptr<Enemy>;
|
||||
|
||||
protected:
|
||||
void applyFlipMirror(float vx); // Aplica flip horizontal y/o mirror vertical al sprite
|
||||
void checkPath(); // Comprueba si ha llegado al limite del recorrido para darse media vuelta
|
||||
|
||||
std::shared_ptr<AnimatedSprite> sprite_; // Sprite del enemigo
|
||||
|
||||
// Variables
|
||||
int x1_{0}; // Limite izquierdo de la ruta en el eje X
|
||||
int x2_{0}; // Limite derecho de la ruta en el eje X
|
||||
int y1_{0}; // Limite superior de la ruta en el eje Y
|
||||
int y2_{0}; // Limite inferior de la ruta en el eje Y
|
||||
SDL_FRect collider_{}; // Caja de colisión
|
||||
bool should_flip_{false}; // Indica si el enemigo hace flip al terminar su ruta
|
||||
bool should_mirror_{false}; // Indica si el enemigo se dibuja volteado verticalmente
|
||||
SDL_FRect collider_{}; // Caja de colisión
|
||||
bool should_flip_{false}; // Indica si el enemigo hace flip al terminar su ruta
|
||||
bool should_mirror_{false}; // Indica si el enemigo se dibuja volteado verticalmente
|
||||
};
|
||||
|
||||
96
source/game/entities/moving_platform.cpp
Normal file
96
source/game/entities/moving_platform.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
#include "game/entities/moving_platform.hpp"
|
||||
|
||||
#include <cstdlib> // Para rand
|
||||
|
||||
#include "core/rendering/sprite/animated_sprite.hpp" // Para AnimatedSprite
|
||||
#include "core/resources/resource_cache.hpp" // Para Resource
|
||||
|
||||
// Constructor
|
||||
MovingPlatform::MovingPlatform(const Data& data)
|
||||
: sprite_(std::make_shared<AnimatedSprite>(Resource::Cache::get()->getAnimationData(data.animation_path))),
|
||||
x1_(data.x1),
|
||||
x2_(data.x2),
|
||||
y1_(data.y1),
|
||||
y2_(data.y2) {
|
||||
sprite_->setPosX(data.x);
|
||||
sprite_->setPosY(data.y);
|
||||
sprite_->setVelX(data.vx);
|
||||
sprite_->setVelY(data.vy);
|
||||
|
||||
collider_ = getRect();
|
||||
|
||||
// Coloca un frame al azar o el designado
|
||||
sprite_->setCurrentAnimationFrame((data.frame == -1) ? (rand() % sprite_->getCurrentAnimationSize()) : data.frame);
|
||||
}
|
||||
|
||||
// Actualiza posición, calcula desplazamiento real del frame
|
||||
void MovingPlatform::update(float delta_time) {
|
||||
float old_x = sprite_->getPosX();
|
||||
float old_y = sprite_->getPosY();
|
||||
|
||||
sprite_->update(delta_time);
|
||||
checkPath();
|
||||
|
||||
last_dx_ = sprite_->getPosX() - old_x;
|
||||
last_dy_ = sprite_->getPosY() - old_y;
|
||||
|
||||
collider_ = getRect();
|
||||
}
|
||||
|
||||
// Pinta la plataforma en pantalla
|
||||
void MovingPlatform::render() {
|
||||
sprite_->render();
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Solo actualiza la animación sin mover la plataforma
|
||||
void MovingPlatform::updateAnimation(float delta_time) {
|
||||
sprite_->animate(delta_time);
|
||||
}
|
||||
|
||||
// Resetea la plataforma a su posición inicial (para editor)
|
||||
void MovingPlatform::resetToInitialPosition(const Data& data) {
|
||||
sprite_->setPosX(data.x);
|
||||
sprite_->setPosY(data.y);
|
||||
sprite_->setVelX(data.vx);
|
||||
sprite_->setVelY(data.vy);
|
||||
|
||||
x1_ = data.x1;
|
||||
x2_ = data.x2;
|
||||
y1_ = data.y1;
|
||||
y2_ = data.y2;
|
||||
|
||||
collider_ = getRect();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Devuelve el rectangulo que contiene a la plataforma
|
||||
auto MovingPlatform::getRect() -> SDL_FRect {
|
||||
return sprite_->getRect();
|
||||
}
|
||||
|
||||
// Obtiene el rectangulo de colisión
|
||||
auto MovingPlatform::getCollider() -> SDL_FRect& {
|
||||
return collider_;
|
||||
}
|
||||
|
||||
// Comprueba los límites del recorrido para invertir dirección
|
||||
void MovingPlatform::checkPath() { // NOLINT(readability-make-member-function-const)
|
||||
if (sprite_->getPosX() > x2_ || sprite_->getPosX() < x1_) {
|
||||
if (sprite_->getPosX() > x2_) {
|
||||
sprite_->setPosX(x2_);
|
||||
} else {
|
||||
sprite_->setPosX(x1_);
|
||||
}
|
||||
sprite_->setVelX(sprite_->getVelX() * (-1));
|
||||
}
|
||||
|
||||
if (sprite_->getPosY() > y2_ || sprite_->getPosY() < y1_) {
|
||||
if (sprite_->getPosY() > y2_) {
|
||||
sprite_->setPosY(y2_);
|
||||
} else {
|
||||
sprite_->setPosY(y1_);
|
||||
}
|
||||
sprite_->setVelY(sprite_->getVelY() * (-1));
|
||||
}
|
||||
}
|
||||
53
source/game/entities/moving_platform.hpp
Normal file
53
source/game/entities/moving_platform.hpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <memory> // Para shared_ptr
|
||||
#include <string> // Para string
|
||||
|
||||
class AnimatedSprite;
|
||||
|
||||
class MovingPlatform {
|
||||
public:
|
||||
struct Data {
|
||||
std::string animation_path; // Ruta al fichero con la animación
|
||||
float x{0.0F}; // Posición inicial en el eje X
|
||||
float y{0.0F}; // Posición inicial en el eje Y
|
||||
float vx{0.0F}; // Velocidad en el eje X
|
||||
float vy{0.0F}; // Velocidad en el eje Y
|
||||
int x1{0}; // Límite izquierdo de la ruta en el eje X
|
||||
int x2{0}; // Límite derecho de la ruta en el eje X
|
||||
int y1{0}; // Límite superior de la ruta en el eje Y
|
||||
int y2{0}; // Límite inferior de la ruta en el eje Y
|
||||
int frame{0}; // Frame inicial para la animación
|
||||
};
|
||||
|
||||
explicit MovingPlatform(const Data& data);
|
||||
~MovingPlatform() = default;
|
||||
|
||||
void update(float delta_time);
|
||||
void render();
|
||||
#ifdef _DEBUG
|
||||
void updateAnimation(float delta_time);
|
||||
void resetToInitialPosition(const Data& data);
|
||||
#endif
|
||||
|
||||
auto getRect() -> SDL_FRect;
|
||||
auto getCollider() -> SDL_FRect&;
|
||||
|
||||
// Desplazamiento real del último frame (para transportar al jugador)
|
||||
[[nodiscard]] auto getLastDX() const -> float { return last_dx_; }
|
||||
[[nodiscard]] auto getLastDY() const -> float { return last_dy_; }
|
||||
|
||||
private:
|
||||
void checkPath(); // Comprueba los límites del recorrido
|
||||
|
||||
std::shared_ptr<AnimatedSprite> sprite_;
|
||||
SDL_FRect collider_{};
|
||||
int x1_{0};
|
||||
int x2_{0};
|
||||
int y1_{0};
|
||||
int y2_{0};
|
||||
float last_dx_{0.0F}; // Desplazamiento horizontal del último frame
|
||||
float last_dy_{0.0F}; // Desplazamiento vertical del último frame
|
||||
};
|
||||
75
source/game/entities/path_enemy.cpp
Normal file
75
source/game/entities/path_enemy.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
#include "game/entities/path_enemy.hpp"
|
||||
|
||||
#include "core/rendering/sprite/animated_sprite.hpp" // Para AnimatedSprite
|
||||
|
||||
// Constructor: delega al base y almacena boundaries
|
||||
PathEnemy::PathEnemy(const Data& data)
|
||||
: Enemy(data),
|
||||
x1_(data.x1),
|
||||
x2_(data.x2),
|
||||
y1_(data.y1),
|
||||
y2_(data.y2) {
|
||||
}
|
||||
|
||||
// Actualiza posición y animación del enemigo
|
||||
void PathEnemy::update(float delta_time) {
|
||||
sprite_->update(delta_time);
|
||||
checkPath();
|
||||
collider_ = getRect();
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Resetea el enemigo a su posición inicial (para editor)
|
||||
void PathEnemy::resetToInitialPosition(const Data& data) {
|
||||
sprite_->setPosX(data.x);
|
||||
sprite_->setPosY(data.y);
|
||||
sprite_->setVelX(data.vx);
|
||||
sprite_->setVelY(data.vy);
|
||||
|
||||
x1_ = data.x1;
|
||||
x2_ = data.x2;
|
||||
y1_ = data.y1;
|
||||
y2_ = data.y2;
|
||||
|
||||
applyFlipMirror(data.vx);
|
||||
|
||||
collider_ = getRect();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Comprueba si ha llegado al limite del recorrido para darse media vuelta
|
||||
void PathEnemy::checkPath() { // NOLINT(readability-make-member-function-const)
|
||||
if (sprite_->getPosX() > x2_ || sprite_->getPosX() < x1_) {
|
||||
// Recoloca
|
||||
if (sprite_->getPosX() > x2_) {
|
||||
sprite_->setPosX(x2_);
|
||||
} else {
|
||||
sprite_->setPosX(x1_);
|
||||
}
|
||||
|
||||
// Cambia el sentido
|
||||
sprite_->setVelX(sprite_->getVelX() * (-1));
|
||||
|
||||
// Invierte el sprite
|
||||
if (should_flip_) {
|
||||
sprite_->flip();
|
||||
}
|
||||
}
|
||||
|
||||
if (sprite_->getPosY() > y2_ || sprite_->getPosY() < y1_) {
|
||||
// Recoloca
|
||||
if (sprite_->getPosY() > y2_) {
|
||||
sprite_->setPosY(y2_);
|
||||
} else {
|
||||
sprite_->setPosY(y1_);
|
||||
}
|
||||
|
||||
// Cambia el sentido
|
||||
sprite_->setVelY(sprite_->getVelY() * (-1));
|
||||
|
||||
// Invierte el sprite
|
||||
if (should_flip_) {
|
||||
sprite_->flip();
|
||||
}
|
||||
}
|
||||
}
|
||||
22
source/game/entities/path_enemy.hpp
Normal file
22
source/game/entities/path_enemy.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "game/entities/enemy.hpp"
|
||||
|
||||
class PathEnemy : public Enemy {
|
||||
public:
|
||||
explicit PathEnemy(const Data& data);
|
||||
~PathEnemy() override = default;
|
||||
|
||||
void update(float delta_time) override;
|
||||
#ifdef _DEBUG
|
||||
void resetToInitialPosition(const Data& data) override;
|
||||
#endif
|
||||
|
||||
private:
|
||||
void checkPath(); // Comprueba si ha llegado al limite del recorrido para darse media vuelta
|
||||
|
||||
int x1_{0}; // Limite izquierdo de la ruta en el eje X
|
||||
int x2_{0}; // Limite derecho de la ruta en el eje X
|
||||
int y1_{0}; // Limite superior de la ruta en el eje Y
|
||||
int y2_{0}; // Limite inferior de la ruta en el eje Y
|
||||
};
|
||||
@@ -398,6 +398,9 @@ void Player::checkFalling() {
|
||||
return;
|
||||
}
|
||||
|
||||
// ON_GROUND: si está sobre una plataforma móvil, no comprobar tiles
|
||||
if (on_platform_) { return; }
|
||||
|
||||
// ON_GROUND: comprobar si sigue habiendo suelo
|
||||
float foot_y = (y_ + oy) + HEIGHT;
|
||||
if (!tc.hasGroundBelow(x_ + ox, foot_y, WIDTH)) {
|
||||
@@ -537,6 +540,18 @@ void Player::syncSpriteAndCollider() {
|
||||
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() {
|
||||
sprite_->setPos(x_, y_);
|
||||
}
|
||||
|
||||
@@ -72,6 +72,9 @@ class Player {
|
||||
void setAdjacentRoom(std::shared_ptr<Room> room, Room::Border direction);
|
||||
void clearAdjacentRoom();
|
||||
[[nodiscard]] auto isAlive() const -> bool { return is_alive_; }
|
||||
[[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 setIgnoreInput(bool value) { ignore_input_ = value; }
|
||||
[[nodiscard]] auto getIgnoreInput() const -> bool { return ignore_input_; }
|
||||
@@ -126,6 +129,7 @@ class Player {
|
||||
bool is_alive_ = true;
|
||||
bool is_paused_ = false;
|
||||
bool ignore_input_ = false;
|
||||
bool on_platform_ = false;
|
||||
bool turning_ = false;
|
||||
Direction facing_ = Direction::RIGHT;
|
||||
Room::Border border_ = Room::Border::TOP;
|
||||
|
||||
95
source/game/gameplay/platform_manager.cpp
Normal file
95
source/game/gameplay/platform_manager.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
#include "platform_manager.hpp"
|
||||
|
||||
#include "game/entities/moving_platform.hpp" // Para MovingPlatform
|
||||
|
||||
// Añade una plataforma a la colección
|
||||
void PlatformManager::addPlatform(std::shared_ptr<MovingPlatform> platform) {
|
||||
platforms_.push_back(std::move(platform));
|
||||
}
|
||||
|
||||
// Elimina todas las plataformas
|
||||
void PlatformManager::clear() {
|
||||
platforms_.clear();
|
||||
}
|
||||
|
||||
// Comprueba si no hay plataformas
|
||||
auto PlatformManager::isEmpty() const -> bool {
|
||||
return platforms_.empty();
|
||||
}
|
||||
|
||||
// Actualiza todas las plataformas
|
||||
void PlatformManager::update(float delta_time) {
|
||||
for (const auto& platform : platforms_) {
|
||||
platform->update(delta_time);
|
||||
}
|
||||
}
|
||||
|
||||
// Renderiza todas las plataformas
|
||||
void PlatformManager::render() {
|
||||
for (const auto& platform : platforms_) {
|
||||
platform->render();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Solo actualiza animaciones sin mover plataformas
|
||||
void PlatformManager::updateAnimations(float delta_time) {
|
||||
for (const auto& platform : platforms_) {
|
||||
platform->updateAnimation(delta_time);
|
||||
}
|
||||
}
|
||||
|
||||
// Resetea todas las plataformas a su posición inicial
|
||||
void PlatformManager::resetPositions(const std::vector<MovingPlatform::Data>& platform_data) {
|
||||
const int COUNT = std::min(static_cast<int>(platforms_.size()), static_cast<int>(platform_data.size()));
|
||||
for (int i = 0; i < COUNT; ++i) {
|
||||
platforms_[i]->resetToInitialPosition(platform_data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Número de plataformas
|
||||
auto PlatformManager::getCount() const -> int {
|
||||
return static_cast<int>(platforms_.size());
|
||||
}
|
||||
|
||||
// Acceso a una plataforma por índice
|
||||
auto PlatformManager::getPlatform(int index) -> std::shared_ptr<MovingPlatform>& {
|
||||
return platforms_.at(index);
|
||||
}
|
||||
|
||||
// Elimina la última plataforma
|
||||
void PlatformManager::removeLastPlatform() {
|
||||
if (!platforms_.empty()) {
|
||||
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 de 4px (medio tile) para compensar el movimiento entre frames
|
||||
constexpr float TOLERANCE = 4.0F;
|
||||
if (player_feet >= platform_top && player_feet <= platform_top + TOLERANCE) {
|
||||
return platform.get();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
44
source/game/gameplay/platform_manager.hpp
Normal file
44
source/game/gameplay/platform_manager.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <memory> // Para shared_ptr
|
||||
#include <vector> // Para vector
|
||||
|
||||
#include "game/entities/moving_platform.hpp" // Para MovingPlatform, MovingPlatform::Data
|
||||
|
||||
class PlatformManager {
|
||||
public:
|
||||
PlatformManager() = default;
|
||||
~PlatformManager() = default;
|
||||
|
||||
// Prohibir copia y movimiento
|
||||
PlatformManager(const PlatformManager&) = delete;
|
||||
auto operator=(const PlatformManager&) -> PlatformManager& = delete;
|
||||
PlatformManager(PlatformManager&&) = delete;
|
||||
auto operator=(PlatformManager&&) -> PlatformManager& = delete;
|
||||
|
||||
// Gestión de plataformas
|
||||
void addPlatform(std::shared_ptr<MovingPlatform> platform);
|
||||
void clear();
|
||||
[[nodiscard]] auto isEmpty() const -> bool;
|
||||
|
||||
// Actualización y renderizado
|
||||
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);
|
||||
[[nodiscard]] auto getCount() const -> int;
|
||||
auto getPlatform(int index) -> std::shared_ptr<MovingPlatform>&;
|
||||
void removeLastPlatform();
|
||||
#endif
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<MovingPlatform>> platforms_;
|
||||
};
|
||||
@@ -9,6 +9,7 @@
|
||||
#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/platform_manager.hpp" // Para PlatformManager
|
||||
#include "game/gameplay/room_loader.hpp" // Para RoomLoader
|
||||
#include "game/gameplay/scoreboard.hpp" // Para Scoreboard::Data
|
||||
#include "game/gameplay/tilemap_renderer.hpp" // Para TilemapRenderer
|
||||
@@ -20,9 +21,10 @@ 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);
|
||||
|
||||
// Crea los managers de enemigos e items
|
||||
// Crea los managers de enemigos, items y plataformas
|
||||
enemy_manager_ = std::make_unique<EnemyManager>();
|
||||
item_manager_ = std::make_unique<ItemManager>(room->number, data_);
|
||||
platform_manager_ = std::make_unique<PlatformManager>();
|
||||
|
||||
initializeRoom(*room);
|
||||
// Crea el mapa de colisiones desde el collision_tile_map
|
||||
@@ -55,7 +57,12 @@ void Room::initializeRoom(const Data& room) {
|
||||
|
||||
// Crear los enemigos usando el manager
|
||||
for (const auto& enemy_data : room.enemies) {
|
||||
enemy_manager_->addEnemy(std::make_shared<Enemy>(enemy_data));
|
||||
enemy_manager_->addEnemy(Enemy::create(enemy_data));
|
||||
}
|
||||
|
||||
// Crear las plataformas usando el manager
|
||||
for (const auto& plat_data : room.platforms) {
|
||||
platform_manager_->addPlatform(std::make_shared<MovingPlatform>(plat_data));
|
||||
}
|
||||
|
||||
// Crear los items usando el manager
|
||||
@@ -79,6 +86,11 @@ void Room::renderMap() {
|
||||
tilemap_renderer_->render();
|
||||
}
|
||||
|
||||
// Dibuja las plataformas en pantalla
|
||||
void Room::renderPlatforms() {
|
||||
platform_manager_->render();
|
||||
}
|
||||
|
||||
// Dibuja los enemigos en pantalla
|
||||
void Room::renderEnemies() {
|
||||
enemy_manager_->render();
|
||||
@@ -99,6 +111,7 @@ void Room::redrawMap() {
|
||||
void Room::updateEditorMode(float delta_time) {
|
||||
tilemap_renderer_->update(delta_time);
|
||||
enemy_manager_->updateAnimations(delta_time);
|
||||
platform_manager_->updateAnimations(delta_time);
|
||||
item_manager_->update(delta_time);
|
||||
}
|
||||
|
||||
@@ -184,6 +197,9 @@ void Room::update(float delta_time) { // NOLINT(readability-make-member-functio
|
||||
// Actualiza los enemigos usando el manager
|
||||
enemy_manager_->update(delta_time);
|
||||
|
||||
// Actualiza las plataformas usando el manager
|
||||
platform_manager_->update(delta_time);
|
||||
|
||||
// Actualiza los items usando el manager
|
||||
item_manager_->update(delta_time);
|
||||
}
|
||||
@@ -223,6 +239,10 @@ auto Room::itemCollision(SDL_FRect& rect) -> bool {
|
||||
return item_manager_->checkCollision(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 RoomLoader)
|
||||
auto Room::loadYAML(const std::string& file_path, bool verbose) -> Data { // NOLINT(readability-convert-member-functions-to-static)
|
||||
return RoomLoader::loadYAML(file_path, verbose);
|
||||
|
||||
@@ -6,14 +6,16 @@
|
||||
#include <string> // Para string
|
||||
#include <vector> // Para vector
|
||||
|
||||
#include "game/defaults.hpp" // Para Defaults::Game::Room
|
||||
#include "game/entities/enemy.hpp" // Para EnemyData
|
||||
#include "game/entities/item.hpp" // Para ItemData
|
||||
#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/enemy.hpp" // Para EnemyData
|
||||
#include "game/entities/item.hpp" // Para ItemData
|
||||
#include "game/entities/moving_platform.hpp" // Para MovingPlatform::Data
|
||||
#include "game/gameplay/scoreboard.hpp" // Para Scoreboard::Data
|
||||
#include "utils/defines.hpp" // Para Tile::SIZE, Map::WIDTH, Map::HEIGHT
|
||||
class Surface;
|
||||
class EnemyManager;
|
||||
class ItemManager;
|
||||
class PlatformManager;
|
||||
class CollisionMap;
|
||||
class TileCollider;
|
||||
class TilemapRenderer;
|
||||
@@ -44,6 +46,7 @@ class Room {
|
||||
std::vector<int> collision_tile_map;
|
||||
std::vector<Enemy::Data> enemies;
|
||||
std::vector<Item::Data> items;
|
||||
std::vector<MovingPlatform::Data> platforms;
|
||||
};
|
||||
|
||||
// Constructor y destructor
|
||||
@@ -55,6 +58,7 @@ class Room {
|
||||
[[nodiscard]] auto getBGColor() const -> Uint8 { return bg_color_; }
|
||||
void renderMap();
|
||||
void renderEnemies();
|
||||
void renderPlatforms();
|
||||
void renderItems();
|
||||
#ifdef _DEBUG
|
||||
void redrawMap();
|
||||
@@ -62,6 +66,7 @@ class Room {
|
||||
void resetEnemyPositions(const std::vector<Enemy::Data>& enemy_data);
|
||||
auto getEnemyManager() -> EnemyManager* { return enemy_manager_.get(); }
|
||||
auto getItemManager() -> ItemManager* { return item_manager_.get(); }
|
||||
auto getPlatformManager() -> PlatformManager* { return platform_manager_.get(); }
|
||||
void setItemColors(Uint8 color1, Uint8 color2);
|
||||
void setTile(int index, int tile_value);
|
||||
void setCollisionTile(int index, int value);
|
||||
@@ -75,6 +80,7 @@ class Room {
|
||||
auto getRoom(Border border) -> std::string;
|
||||
auto enemyCollision(SDL_FRect& rect) -> bool;
|
||||
auto itemCollision(SDL_FRect& rect) -> bool;
|
||||
auto checkPlayerOnPlatform(const SDL_FRect& player_collider, float player_vy) -> MovingPlatform*;
|
||||
void setPaused(bool value);
|
||||
[[nodiscard]] auto getConveyorBeltDirection() const -> int { return conveyor_belt_direction_; }
|
||||
[[nodiscard]] auto getTileCollider() const -> const TileCollider&;
|
||||
@@ -89,6 +95,7 @@ class Room {
|
||||
|
||||
std::unique_ptr<EnemyManager> enemy_manager_;
|
||||
std::unique_ptr<ItemManager> item_manager_;
|
||||
std::unique_ptr<PlatformManager> platform_manager_;
|
||||
std::unique_ptr<CollisionMap> collision_map_;
|
||||
std::unique_ptr<TilemapRenderer> tilemap_renderer_;
|
||||
std::shared_ptr<Surface> surface_;
|
||||
|
||||
@@ -204,6 +204,11 @@ void RoomLoader::parseEnemyBoundaries(const fkyaml::node& bounds_node, Enemy::Da
|
||||
auto RoomLoader::parseEnemyData(const fkyaml::node& enemy_node) -> Enemy::Data { // NOLINT(readability-convert-member-functions-to-static)
|
||||
Enemy::Data enemy;
|
||||
|
||||
// Enemy type (default: "path")
|
||||
if (enemy_node.contains("type")) {
|
||||
enemy.type = enemy_node["type"].get_value<std::string>();
|
||||
}
|
||||
|
||||
// Animation path
|
||||
if (enemy_node.contains("animation")) {
|
||||
enemy.animation_path = enemy_node["animation"].get_value<std::string>();
|
||||
@@ -323,6 +328,90 @@ void RoomLoader::parseItems(const fkyaml::node& yaml, Room::Data& room, bool ver
|
||||
}
|
||||
}
|
||||
|
||||
// Parsea los límites de movimiento de una plataforma
|
||||
void RoomLoader::parsePlatformBoundaries(const fkyaml::node& bounds_node, MovingPlatform::Data& platform) {
|
||||
// Formato: position1 y position2
|
||||
if (bounds_node.contains("position1")) {
|
||||
const auto& pos1 = bounds_node["position1"];
|
||||
if (pos1.contains("x")) {
|
||||
platform.x1 = pos1["x"].get_value<int>() * Tile::SIZE;
|
||||
}
|
||||
if (pos1.contains("y")) {
|
||||
platform.y1 = pos1["y"].get_value<int>() * Tile::SIZE;
|
||||
}
|
||||
}
|
||||
if (bounds_node.contains("position2")) {
|
||||
const auto& pos2 = bounds_node["position2"];
|
||||
if (pos2.contains("x")) {
|
||||
platform.x2 = pos2["x"].get_value<int>() * Tile::SIZE;
|
||||
}
|
||||
if (pos2.contains("y")) {
|
||||
platform.y2 = pos2["y"].get_value<int>() * Tile::SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parsea los datos de una plataforma individual
|
||||
auto RoomLoader::parsePlatformData(const fkyaml::node& platform_node) -> MovingPlatform::Data {
|
||||
MovingPlatform::Data platform;
|
||||
|
||||
// Animation path
|
||||
if (platform_node.contains("animation")) {
|
||||
platform.animation_path = platform_node["animation"].get_value<std::string>();
|
||||
}
|
||||
|
||||
// Position (in tiles, convert to pixels)
|
||||
if (platform_node.contains("position")) {
|
||||
const auto& pos = platform_node["position"];
|
||||
if (pos.contains("x")) {
|
||||
platform.x = pos["x"].get_value<float>() * Tile::SIZE;
|
||||
}
|
||||
if (pos.contains("y")) {
|
||||
platform.y = pos["y"].get_value<float>() * Tile::SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
// Velocity (already in pixels/second)
|
||||
if (platform_node.contains("velocity")) {
|
||||
const auto& vel = platform_node["velocity"];
|
||||
if (vel.contains("x")) {
|
||||
platform.vx = vel["x"].get_value<float>();
|
||||
}
|
||||
if (vel.contains("y")) {
|
||||
platform.vy = vel["y"].get_value<float>();
|
||||
}
|
||||
}
|
||||
|
||||
// Boundaries (in tiles, convert to pixels)
|
||||
if (platform_node.contains("boundaries")) {
|
||||
parsePlatformBoundaries(platform_node["boundaries"], platform);
|
||||
}
|
||||
|
||||
// Optional frame
|
||||
platform.frame = platform_node.contains("frame")
|
||||
? platform_node["frame"].get_value_or<int>(-1)
|
||||
: -1;
|
||||
|
||||
return platform;
|
||||
}
|
||||
|
||||
// Parsea la lista de plataformas de la habitación
|
||||
void RoomLoader::parsePlatforms(const fkyaml::node& yaml, Room::Data& room, bool verbose) {
|
||||
if (!yaml.contains("platforms") || yaml["platforms"].is_null()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& platforms_node = yaml["platforms"];
|
||||
|
||||
for (const auto& platform_node : platforms_node) {
|
||||
room.platforms.push_back(parsePlatformData(platform_node));
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
std::cout << "Loaded " << room.platforms.size() << " platforms\n";
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Carga una habitación desde un string YAML (para el editor de mapas, evita el resource pack)
|
||||
auto RoomLoader::loadFromString(const std::string& yaml_content, const std::string& file_name) -> Room::Data {
|
||||
@@ -333,6 +422,7 @@ auto RoomLoader::loadFromString(const std::string& yaml_content, const std::stri
|
||||
parseTilemap(yaml, room, file_name, false);
|
||||
parseEnemies(yaml, room, false);
|
||||
parseItems(yaml, room, false);
|
||||
parsePlatforms(yaml, room, false);
|
||||
} catch (const fkyaml::exception& e) {
|
||||
std::cerr << "YAML parsing error in " << file_name << ": " << e.what() << '\n';
|
||||
} catch (const std::exception& e) {
|
||||
@@ -367,6 +457,7 @@ auto RoomLoader::loadYAML(const std::string& file_path, bool verbose) -> Room::D
|
||||
parseTilemap(yaml, room, FILE_NAME, verbose);
|
||||
parseEnemies(yaml, room, verbose);
|
||||
parseItems(yaml, room, verbose);
|
||||
parsePlatforms(yaml, room, verbose);
|
||||
|
||||
if (verbose) {
|
||||
std::cout << "Room loaded successfully: " << FILE_NAME << '\n';
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
#include <string> // Para string
|
||||
#include <vector> // Para vector
|
||||
|
||||
#include "external/fkyaml_node.hpp" // Para fkyaml::node
|
||||
#include "game/entities/enemy.hpp" // Para Enemy::Data
|
||||
#include "game/entities/item.hpp" // Para Item::Data
|
||||
#include "game/gameplay/room.hpp" // Para Room::Data
|
||||
#include "external/fkyaml_node.hpp" // Para fkyaml::node
|
||||
#include "game/entities/enemy.hpp" // Para Enemy::Data
|
||||
#include "game/entities/item.hpp" // Para Item::Data
|
||||
#include "game/entities/moving_platform.hpp" // Para MovingPlatform::Data
|
||||
#include "game/gameplay/room.hpp" // Para Room::Data
|
||||
|
||||
/**
|
||||
* @brief Cargador de archivos de habitaciones en formato YAML
|
||||
@@ -133,4 +134,8 @@ class RoomLoader {
|
||||
* @return Estructura Item::Data con los datos parseados
|
||||
*/
|
||||
static auto parseItemData(const fkyaml::node& item_node, const Room::Data& room) -> Item::Data;
|
||||
|
||||
static void parsePlatforms(const fkyaml::node& yaml, Room::Data& room, bool verbose);
|
||||
static auto parsePlatformData(const fkyaml::node& platform_node) -> MovingPlatform::Data;
|
||||
static void parsePlatformBoundaries(const fkyaml::node& bounds_node, MovingPlatform::Data& platform);
|
||||
};
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "core/system/event_buffer.hpp" // Para EventBuffer
|
||||
#include "core/system/global_events.hpp" // Para check
|
||||
#include "game/defaults.hpp" // Para Defaults::Game
|
||||
#include "game/entities/moving_platform.hpp" // Para MovingPlatform
|
||||
#include "game/game_control.hpp" // Para GameControl
|
||||
#include "game/gameplay/item_tracker.hpp" // Para ItemTracker
|
||||
#include "game/gameplay/room.hpp" // Para Room, RoomData
|
||||
@@ -305,6 +306,10 @@ void Game::updatePlaying(float delta_time) {
|
||||
updateAdjacentRooms(delta_time);
|
||||
switch (mode_) {
|
||||
case Mode::GAME:
|
||||
// Plataformas: resetear flag y detectar antes de la física del player
|
||||
player_->clearPlatformFlag();
|
||||
checkPlayerAndPlatforms();
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Maneja el arrastre del jugador con el ratón (debug)
|
||||
handleDebugMouseDrag(delta_time);
|
||||
@@ -496,12 +501,14 @@ void Game::renderPlaying() {
|
||||
// Renderizar habitación saliente con su offset
|
||||
Screen::get()->setRenderOffset(old_ox, old_oy);
|
||||
transition_old_room_->renderMap();
|
||||
transition_old_room_->renderPlatforms();
|
||||
transition_old_room_->renderEnemies();
|
||||
transition_old_room_->renderItems();
|
||||
|
||||
// Renderizar habitación entrante + jugador con su offset
|
||||
Screen::get()->setRenderOffset(new_ox, new_oy);
|
||||
room_->renderMap();
|
||||
room_->renderPlatforms();
|
||||
room_->renderEnemies();
|
||||
room_->renderItems();
|
||||
if (mode_ == Mode::GAME) {
|
||||
@@ -514,6 +521,7 @@ void Game::renderPlaying() {
|
||||
} else {
|
||||
// --- Renderizado normal ---
|
||||
room_->renderMap();
|
||||
room_->renderPlatforms();
|
||||
room_->renderEnemies();
|
||||
room_->renderItems();
|
||||
if (mode_ == Mode::GAME) {
|
||||
@@ -895,6 +903,14 @@ auto Game::checkPlayerAndEnemies() -> bool {
|
||||
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
|
||||
void Game::checkPlayerAndItems() {
|
||||
room_->itemCollision(player_->getCollider());
|
||||
|
||||
@@ -74,6 +74,7 @@ class Game {
|
||||
void handleInput(); // Comprueba el teclado
|
||||
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
|
||||
void checkPlayerAndPlatforms(); // Comprueba si el jugador está sobre una plataforma móvil
|
||||
void checkPlayerAndItems(); // Comprueba las colisiones del jugador con los objetos
|
||||
void checkIfPlayerIsAlive(); // Comprueba si el jugador esta vivo
|
||||
void killPlayer(); // Mata al jugador
|
||||
|
||||
Reference in New Issue
Block a user