#include "Collision/TriggerSystem.h" #include "Collision/Collision.h" #include "Core/AABB.h" #include "Rendering/Model.h" void TriggerSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt) { //Currently only players can trigger things. auto players = m_World->GetComponents("Player"); if (players == nullptr) { return; } EntityID tId = component.EntityID; boost::optional triggerBox = Collision::EntityAbsoluteAABB(entity); //The trigger *should* have a bounding box, or something, to test against so it can be triggered. if (!triggerBox) { return; } for (auto& pc : *players) { EntityID pId = pc.EntityID; boost::optional playerBox = Collision::EntityAbsoluteAABB(EntityWrapper(m_World, pId)); //The player can't trigger anything without an AABB. if (!playerBox) { continue; } if (!Collision::AABBVsAABB(*triggerBox, *playerBox)) { //Entity is not touching the trigger, //Throw event if it was previously. if (throwLeaveIfWasInTrigger(m_EntitiesTouchingTrigger[tId], pId, tId)) { continue; } //This only occurs if the entity was completely inside the trigger one frame, //then completely outside the trigger, e.g. when dying and respawning. throwLeaveIfWasInTrigger(m_EntitiesCompletelyInTrigger[tId], pId, tId); } else { //Entity is at least touching the trigger. AABB completelyInsideBox; bool playerFitsInTrigger = glm::all(glm::greaterThan((*triggerBox).Size(), (*playerBox).Size())); if (playerFitsInTrigger) { completelyInsideBox = AABB::FromOriginSize((*triggerBox).Origin(), (*triggerBox).Size() - 2.0f * (*playerBox).Size()); } if (playerFitsInTrigger && Collision::AABBVsAABB(completelyInsideBox, *playerBox)) { //Entity is completely inside the trigger. //If it was only touching before, it is erased. m_EntitiesTouchingTrigger[tId].erase(pId); std::unordered_set& completeSet = m_EntitiesCompletelyInTrigger[tId]; if (completeSet.count(pId) == 0) { //If it wasn't completely in the trigger, throw Enter and add to the set. completeSet.insert(pId); publish(pId, tId); } } else { //Entity is only touching the trigger. std::unordered_set& touchSet = m_EntitiesTouchingTrigger[tId]; std::unordered_set& completeSet = m_EntitiesCompletelyInTrigger[tId]; const auto& it = completeSet.find(pId); //If it was completely inside before. if (it != completeSet.end()) { completeSet.erase(it); touchSet.insert(pId); //If it was completely outside before. } else if (touchSet.count(pId) == 0) { publish(pId, tId); touchSet.insert(pId); } //Else, it was touching the trigger last frame too and nothing is done. } } } } bool TriggerSystem::throwLeaveIfWasInTrigger(std::unordered_set& triggerSet, EntityID pId, EntityID tId) { const auto& it = triggerSet.find(pId); if (it != triggerSet.end()) { //If it was in the trigger, but not anymore, throw leaveEvent and erase from the set. triggerSet.erase(it); publish(pId, tId); return true; } return false; } bool TriggerSystem::OnTouch(const Events::TriggerTouch &event) { LOG_INFO("Player entity %i touched trigger entity %i.", event.Entity, event.Trigger); return true; } bool TriggerSystem::OnEnter(const Events::TriggerEnter &event) { LOG_INFO("Player entity %i entered trigger entity %i.", event.Entity, event.Trigger); return true; } bool TriggerSystem::OnLeave(const Events::TriggerLeave &event) { LOG_INFO("Player entity %i left trigger entity %i.", event.Entity, event.Trigger); return true; }