#include "Systems/SpawnerSystem.h" #include "Collision/Collision.h" SpawnerSystem::SpawnerSystem(SystemParams params) : System(params) { EVENT_SUBSCRIBE_MEMBER(m_OnSpawnerSpawn, &SpawnerSystem::OnSpawnerSpawn); } EntityWrapper SpawnerSystem::Spawn(EntityWrapper spawner, EntityWrapper parent /*= EntityWrapper::Invalid*/, const std::string& dontCollideComponent) { // Load the entity file and parse it const std::string& entityFilePath = spawner["Spawner"]["EntityFile"]; if (!entityFilePath.empty()) { return SpawnEntityFile(entityFilePath, spawner, parent, dontCollideComponent); } else { return EntityWrapper::Invalid; } } EntityWrapper SpawnerSystem::SpawnEntityFile(const std::string& entityFilePath, EntityWrapper spawner, EntityWrapper parent /*= EntityWrapper::Invalid*/, const std::string& dontCollideComponent /*= ""*/) { // Spawn the entity in the parent's world if it exists, otherwise in the spawner's world World* world = parent.World; if (world == nullptr) { world = spawner.World; } EntityWrapper spawnedEntity; try { auto entityFile = ResourceManager::Load(entityFilePath); spawnedEntity = entityFile->MergeInto(world); world->SetParent(spawnedEntity.ID, parent.ID); } catch (const Resource::FailedLoadingException& e) { return EntityWrapper::Invalid; } // If the spawned entity is collidable, then we must not spawn it where it collides with something that // has a dontCollideComponent attached. bool spawnOnCollidable = dontCollideComponent.empty() || !spawnedEntity.HasComponent("Collidable"); if (!spawnOnCollidable) { boost::optional optBox = Collision::EntityAbsoluteAABB(spawnedEntity); //If we can't calculate the box for some reason, then just spawn somewhere anyway. if (!optBox) { spawnOnCollidable = true; } } // Find any SpawnPoints existing as children of spawner auto children = spawner.World->GetDirectChildren(spawner.ID); std::list spawnPoints; for (auto kv = children.first; kv != children.second; ++kv) { const EntityID& child = kv->second; if (spawner.World->HasComponent(child, "SpawnPoint")) { EntityWrapper spawnPoint = EntityWrapper(spawner.World, child); if (spawnOnCollidable || !spawnedEntityIsColliding(spawnedEntity, spawnPoint, dontCollideComponent)) { spawnPoints.push_back(spawnPoint); } } } // Choose a random SpawnPoint // If there are no children, or if they are all blocked, then the entity will be spawned at the spawner itself. EntityWrapper spawnPoint = spawner; if (!spawnPoints.empty()) { if (spawnPoints.size() > 1) { static std::random_device randomDevice; static std::mt19937 randomGenerator(randomDevice()); std::uniform_int_distribution<> distribution(0, static_cast(std::distance(spawnPoints.begin(), spawnPoints.end())) - 1); auto randomSpawnPointIt = spawnPoints.begin(); std::advance(randomSpawnPointIt, distribution(randomGenerator)); spawnPoint = *randomSpawnPointIt; } else { spawnPoint = spawnPoints.front(); } } if (spawnPoint != parent) { transformEntityToSpawnPoint(spawnedEntity, spawnPoint); } return spawnedEntity; } void SpawnerSystem::transformEntityToSpawnPoint(EntityWrapper spawnedEntity, EntityWrapper spawnPoint) { // Set its position and orientation to that of the SpawnPoint spawnedEntity["Transform"]["Position"] = TransformSystem::AbsolutePosition(spawnPoint.World, spawnPoint.ID); // TODO: Quaternions, bitch spawnedEntity["Transform"]["Orientation"] = glm::eulerAngles(TransformSystem::AbsoluteOrientation(spawnPoint)); } bool SpawnerSystem::spawnedEntityIsColliding(EntityWrapper spawnedEntity, EntityWrapper spawnPoint, const std::string& dontCollideComponent) { transformEntityToSpawnPoint(spawnedEntity, spawnPoint); //Check if the spawned entity collides with anything, and if so, continue to the next spawnpoint. EntityAABB spawnedBox = *Collision::EntityAbsoluteAABB(spawnedEntity); auto otherSpawnedEntities = spawnPoint.World->GetComponents(dontCollideComponent); for (const auto& obj : *otherSpawnedEntities) { if (spawnedEntity.ID == obj.EntityID) { continue; } EntityWrapper otherEntity = EntityWrapper(spawnPoint.World, obj.EntityID); if (!otherEntity.HasComponent("Collidable")) { continue; } auto otherBox = Collision::EntityAbsoluteAABB(otherEntity); if (!otherBox) { continue; } if (Collision::AABBVsAABB(spawnedBox, *otherBox)) { if (!spawnedBox.Entity.HasComponent("Model")) { return true; } Model* model = nullptr; try { model = ResourceManager::Load(otherEntity["Model"]["Resource"]); } catch (const std::exception&) { } if (model != nullptr && Collision::AABBvsTriangles( spawnedBox, model->m_Vertices, model->m_Indices, TransformSystem::ModelMatrix(otherEntity))) { return true; } } } return false; } bool SpawnerSystem::OnSpawnerSpawn(Events::SpawnerSpawn& e) { EntityWrapper spawnedEntity = Spawn(e.Spawner, e.Parent); return true; }