polimorfise d'enemics

moving platforms
This commit is contained in:
2026-04-08 14:09:28 +02:00
parent 5e02854e7a
commit 73a520bf3c
20 changed files with 632 additions and 106 deletions

View 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;
}

View 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_;
};

View File

@@ -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);

View File

@@ -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_;

View File

@@ -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';

View File

@@ -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);
};