f632b45c9b
# Conflicts: # include/Engine/Rendering/Model.h # src/Engine/Collision/Collision.cpp # src/Engine/Collision/CollisionSystem.cpp # src/Engine/Collision/TriggerSystem.cpp # src/Game/Systems/SpawnerSystem.cpp
137 lines
5.5 KiB
C++
137 lines
5.5 KiB
C++
#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<EntityFile>(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<EntityAABB> 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<EntityWrapper> 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<int>(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<Model, true>(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;
|
|
} |