diff --git a/.gitmodules b/.gitmodules index 6dd82fa..9c14583 100644 --- a/.gitmodules +++ b/.gitmodules @@ -17,3 +17,6 @@ path = docs/doxygen-awesome-css url = https://github.com/jothepro/doxygen-awesome-css.git +[submodule "external/tmxlite"] + path = external/tmxlite + url = https://github.com/fallahn/tmxlite.git diff --git a/include/InteractionComponent.h b/include/InteractionComponent.h new file mode 100644 index 0000000..9482c87 --- /dev/null +++ b/include/InteractionComponent.h @@ -0,0 +1,73 @@ +#pragma once + +#include "Component.h" +#include "Entity.h" + +#include +#include + +class InteractionComponent : public Component +{ +public: + InteractionComponent(bool canInteract, bool isInteractable, int reach); + ~InteractionComponent(); + + void init() override; + void update(); + + /*! + * \brief Checks if two entities can interact with each other + * \param interactor The source of the interaction + * \param interactee The target of the interaction + * \return A boolean if an interaction is possible + */ + bool interact(Entity* interactor, Entity* interactee); + + /*! + * \brief Find the closest entity the interactor could interact with + * \param direction The direction in which the interactor is facing, the interactor can only interact with entities in the direction it is facing + * \param interactor The initiator of the interaction + * \param entities A list of entities + * \return The interactable entity closest to the interactor + */ + Entity* getClosestInteractableEntity(Direction direction, Entity* interactor, std::vector entities); + +private: + bool canInteract; + bool isInteractable; + + int reach; + + /*! + * \brief Find the entity closest to the interactor + * \param entities A list of entities + * \param interactorT TransformComponent of the interactor (contains the coordinates) + * \param direction The direction the interactor is facing + * \return The closest entity + */ + Entity* findClosestEntity(std::vector& entities, TransformComponent& interactorT, Direction direction); + + /*! + * \brief Checks if an entity is in the direction the interactor faces + * \param direction The direction the interactor is facing + * \param interactorT TransformComponent of the interactor (contains the coordinates) + */ + std::function getDirectionalCheck(Direction direction, const TransformComponent& interactorT); + + /*! + * \brief Checks if entity is within reach of the interactor + * \param interactorT TransformComponent of the interactor (contains the coordinates) + * \param entityT TransformComponent of the entity (contains the coordinates) + * @param direction The direction the interactor is facing + * @return + */ + bool isEntityCloseEnough(const TransformComponent& interactorT, const TransformComponent& entityT, Direction direction); + + /*! + * \brief Calculates distance between entity and interactor + * \param iPos Coordinates of the interactor + * \param ePos Coordinates of the entity + * \return The distance between interactor and entity + */ + float calculateDistance(Vector2D iPos, Vector2D ePos); +}; \ No newline at end of file diff --git a/src/InteractionComponent.cpp b/src/InteractionComponent.cpp new file mode 100644 index 0000000..190e3d8 --- /dev/null +++ b/src/InteractionComponent.cpp @@ -0,0 +1,113 @@ +#include "InteractionComponent.h" + +#include +#include + +InteractionComponent::InteractionComponent(bool canInteract, bool isInteractable, int reach) + : canInteract(canInteract), isInteractable(isInteractable), reach(reach) {} + +InteractionComponent::~InteractionComponent() = default; + +void InteractionComponent::init() {} + +void InteractionComponent::update() {} + +bool InteractionComponent::interact(Entity* interactor, Entity* interactee) +{ + if(interactee == nullptr) + return false; + + if(!interactor->hasComponent() || !interactor->getComponent().canInteract) + throw std::logic_error("Interactor entity cannot interact"); + + if(!interactee->hasComponent() || !interactee->getComponent().isInteractable) + return false; + + return true; +} + +Entity* InteractionComponent::getClosestInteractableEntity(Direction direction, Entity* interactor, std::vector entities) +{ + entities.erase(std::remove_if(entities.begin(), entities.end(), [](Entity* e) { + return !e->hasComponent() || !e->getComponent().isInteractable; + }), entities.end()); + + if(entities.empty()) + return nullptr; + + auto& interactorT = interactor->getComponent(); + return findClosestEntity(entities, interactorT, direction); +} + +Entity* InteractionComponent::findClosestEntity(std::vector& entities, TransformComponent& interactorT, Direction direction) +{ + Entity* closestEntity = nullptr; + float closestDistance = std::numeric_limits::max(); + + auto isInDirection = getDirectionalCheck(direction, interactorT); + + entities.erase(std::remove_if(entities.begin(), entities.end(), [&](Entity* e){ + auto& entityT = e->getComponent(); + return !isInDirection(entityT) || !isEntityCloseEnough(interactorT, entityT, direction); + }), entities.end()); + + for(auto e : entities) + { + auto& entityT = e->getComponent(); + float distance = calculateDistance(interactorT.position, entityT.position); + + if (distance < closestDistance) { + closestDistance = distance; + closestEntity = e; + } + } + + return closestEntity; +} + +std::function InteractionComponent::getDirectionalCheck(Direction direction, const TransformComponent& interactorT) +{ + switch(direction) + { + case Direction::LEFT: + return[&interactorT](const TransformComponent& entityT) { + return entityT.position.x < interactorT.position.x; + }; + case Direction::RIGHT: + return[&interactorT](const TransformComponent& entityT) { + return entityT.position.x > interactorT.position.x; + }; + case Direction::UP: + return[&interactorT](const TransformComponent& entityT) { + return entityT.position.y < interactorT.position.y; + }; + case Direction::DOWN: + return[&interactorT](const TransformComponent& entityT) { + return entityT.position.y > interactorT.position.y; + }; + default: + return [](const TransformComponent&) { return false; }; + } +} + +bool InteractionComponent::isEntityCloseEnough(const TransformComponent& interactorT, const TransformComponent& entityT, Direction direction) +{ + switch (direction) + { + case Direction::LEFT: + return (entityT.position.x >= (interactorT.position.x - reach)); + case Direction::RIGHT: + return (entityT.position.x <= (interactorT.position.x + reach)); + case Direction::UP: + return (entityT.position.y >= (interactorT.position.y - reach)); + case Direction::DOWN: + return (entityT.position.y <= (interactorT.position.y + reach)); + default: + return false; + } +} + +float InteractionComponent::calculateDistance(Vector2D iPos, Vector2D ePos) +{ + return std::abs(ePos.x - iPos.x) + std::abs(ePos.y - iPos.y); +} \ No newline at end of file