diff --git a/assets b/assets index a0d1615e..a3c92ac8 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit a0d1615e515a4d7db0073cc987d1d593ca942471 +Subproject commit a3c92ac876dd061776c36d1594bd82264372f028 diff --git a/include/Engine/Core/EntityWrapper.h b/include/Engine/Core/EntityWrapper.h index 541dfe0d..4cc8bbea 100644 --- a/include/Engine/Core/EntityWrapper.h +++ b/include/Engine/Core/EntityWrapper.h @@ -7,6 +7,11 @@ class World; struct EntityWrapper { + EntityWrapper() + : World(nullptr) + , ID(EntityID_Invalid) + { } + EntityWrapper(::World* world, EntityID id) : World(world) , ID(id) diff --git a/include/Engine/Core/EventBroker.h b/include/Engine/Core/EventBroker.h index dde5242a..dc1babc0 100644 --- a/include/Engine/Core/EventBroker.h +++ b/include/Engine/Core/EventBroker.h @@ -43,7 +43,7 @@ template class EventRelay : public BaseEventRelay { public: - typedef std::function CallbackType; + typedef std::function CallbackType; EventRelay() : m_Callback(nullptr) @@ -65,7 +65,7 @@ template bool EventRelay::Receive(const std::shared_ptr event) { if (m_Callback != nullptr) { - return m_Callback(*static_cast(event.get())); + return m_Callback(*static_cast(event.get())); } else { return false; } diff --git a/include/Engine/Core/World.h b/include/Engine/Core/World.h index 3c666ceb..b201d4ac 100644 --- a/include/Engine/Core/World.h +++ b/include/Engine/Core/World.h @@ -34,6 +34,8 @@ public: EntityID GetParent(EntityID entity); // Change the parent of an entity void SetParent(EntityID entity, EntityID parent); + // Get children of an entity + const std::pair::const_iterator, std::unordered_multimap::const_iterator> GetChildren(EntityID entity); // Get all component pools const std::unordered_map& GetComponentPools() const { return m_ComponentPools; } // Get the entity children map diff --git a/include/Game/Events/ESpawnerSpawn.h b/include/Game/Events/ESpawnerSpawn.h new file mode 100644 index 00000000..7af4f63e --- /dev/null +++ b/include/Game/Events/ESpawnerSpawn.h @@ -0,0 +1,18 @@ +#ifndef ESpawnerSpawn_h__ +#define ESpawnerSpawn_h__ + +#include "Core/Event.h" +#include "Core/EntityWrapper.h" + +namespace Events +{ + +struct SpawnerSpawn : Event +{ + EntityWrapper Spawner; + EntityWrapper Parent; +}; + +} + +#endif \ No newline at end of file diff --git a/include/Game/Systems/PlayerSpawnSystem.h b/include/Game/Systems/PlayerSpawnSystem.h new file mode 100644 index 00000000..414d32d8 --- /dev/null +++ b/include/Game/Systems/PlayerSpawnSystem.h @@ -0,0 +1,17 @@ +#include "Core/System.h" +#include "Input/EInputCommand.h" +#include "Events/ESpawnerSpawn.h" + +class PlayerSpawnSystem : public ImpureSystem +{ +public: + PlayerSpawnSystem(EventBroker* eventBroker); + + virtual void Update(World* world, double dt) override; + +private: + EventRelay m_OnInputCommand; + bool OnInputCommand(const Events::InputCommand& e); + + std::vector m_SpawnRequests; +}; \ No newline at end of file diff --git a/include/Game/Systems/SpawnerSystem.h b/include/Game/Systems/SpawnerSystem.h new file mode 100644 index 00000000..cbb7c100 --- /dev/null +++ b/include/Game/Systems/SpawnerSystem.h @@ -0,0 +1,20 @@ +#include +#include "Common.h" +#include "GLM.h" +#include "Core/System.h" +#include "Events/ESpawnerSpawn.h" +#include "Core/Transform.h" +#include "Core/ResourceManager.h" +#include "Core/EntityFileParser.h" + +class SpawnerSystem : public System +{ +public: + SpawnerSystem(EventBroker* eventBroker); + +private: + EventRelay m_OnSpawnerSpawn; + bool OnSpawnerSpawn(Events::SpawnerSpawn& e); + + void spawnEntity(EntityWrapper spawner, EntityID parent, glm::vec3 position); +}; \ No newline at end of file diff --git a/resources/Schema/Components.xsd b/resources/Schema/Components.xsd index 7d323df6..bcea14e9 100644 --- a/resources/Schema/Components.xsd +++ b/resources/Schema/Components.xsd @@ -12,4 +12,7 @@ + + + \ No newline at end of file diff --git a/resources/Schema/Components/PlayerSpawn.xml b/resources/Schema/Components/PlayerSpawn.xml new file mode 100644 index 00000000..b286e56f --- /dev/null +++ b/resources/Schema/Components/PlayerSpawn.xml @@ -0,0 +1,3 @@ + + 1 + \ No newline at end of file diff --git a/resources/Schema/Components/PlayerSpawn.xsd b/resources/Schema/Components/PlayerSpawn.xsd new file mode 100644 index 00000000..b076de27 --- /dev/null +++ b/resources/Schema/Components/PlayerSpawn.xsd @@ -0,0 +1,18 @@ + + + + + + + + Combined with a Spawner, defines a spawn point for a player team. + + + + + 1 = Spectator, 2 = Red, 3 = Blue + + + + + diff --git a/resources/Schema/Components/SpawnPoint.xml b/resources/Schema/Components/SpawnPoint.xml new file mode 100644 index 00000000..3f392555 --- /dev/null +++ b/resources/Schema/Components/SpawnPoint.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/Schema/Components/SpawnPoint.xsd b/resources/Schema/Components/SpawnPoint.xsd new file mode 100644 index 00000000..67169a9c --- /dev/null +++ b/resources/Schema/Components/SpawnPoint.xsd @@ -0,0 +1,11 @@ + + + + + + + + Defines this entity as a spawn point for a parent Spawner + + + diff --git a/resources/Schema/Components/Spawner.xml b/resources/Schema/Components/Spawner.xml new file mode 100644 index 00000000..f2ac77a8 --- /dev/null +++ b/resources/Schema/Components/Spawner.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/resources/Schema/Components/Spawner.xsd b/resources/Schema/Components/Spawner.xsd new file mode 100644 index 00000000..dcf74aea --- /dev/null +++ b/resources/Schema/Components/Spawner.xsd @@ -0,0 +1,18 @@ + + + + + + + + Randomly selects a child SpawnPoint component and spawns a copy of an entity template when receiving a SpawnerSpawn event. If no SpawnPoint is found it spawns from its own position. + + + + + The entity template to spawn + + + + + diff --git a/resources/Schema/Entities/Player.xml b/resources/Schema/Entities/Player.xml index 6406a795..6c3b39c3 100644 --- a/resources/Schema/Entities/Player.xml +++ b/resources/Schema/Entities/Player.xml @@ -3,6 +3,7 @@ + Models/Core/UnitSphere.obj diff --git a/resources/Schema/Entities/SpawnTest.xml b/resources/Schema/Entities/SpawnTest.xml new file mode 100644 index 00000000..f4af726d --- /dev/null +++ b/resources/Schema/Entities/SpawnTest.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + Schema/Entities/Player.xml + + + + + + + + + + + Models/Core/UnitSphere.obj + + + + + + + + + + + + + Models/Core/UnitSphere.obj + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/Schema/Types/Entity.xsd b/resources/Schema/Types/Entity.xsd index 88237697..e1681407 100644 --- a/resources/Schema/Types/Entity.xsd +++ b/resources/Schema/Types/Entity.xsd @@ -20,6 +20,9 @@ + + + diff --git a/src/Engine/Core/World.cpp b/src/Engine/Core/World.cpp index 811de45f..3104e4e9 100644 --- a/src/Engine/Core/World.cpp +++ b/src/Engine/Core/World.cpp @@ -125,6 +125,11 @@ void World::SetParent(EntityID entity, EntityID parent) m_EntityChildren.insert(std::make_pair(parent, entity)); } +const std::pair::const_iterator, std::unordered_multimap::const_iterator> World::GetChildren(EntityID entity) +{ + return m_EntityChildren.equal_range(entity); +} + void World::SetName(EntityID entity, const std::string& name) { m_EntityNames[entity] = name; diff --git a/src/Game/CMakeLists.txt b/src/Game/CMakeLists.txt index 4be61127..4aaff273 100644 --- a/src/Game/CMakeLists.txt +++ b/src/Game/CMakeLists.txt @@ -10,16 +10,23 @@ include_directories( ${Boost_INCLUDE_DIRS} ) -file(GLOB SOURCE_FILES +file(GLOB SOURCE_FILES_Systems "${INCLUDE_PATH}/Systems/*.h" "Systems/*.cpp" ) source_group(Systems FILES ${SOURCE_FILES_Systems}) +file(GLOB SOURCE_FILES_Events + "${INCLUDE_PATH}/Events/*.h" + "Events/*.cpp" +) +source_group(Events FILES ${SOURCE_FILES_Events}) + set(SOURCE_FILES ${SOURCE_FILES} "Game.cpp" ${SOURCE_FILES_Systems} + ${SOURCE_FILES_Events} ) set(LIBRARIES diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index a3df754d..84b330c4 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -6,6 +6,8 @@ #include "Systems/PlayerSystem.h" #include "Systems/HealthSystem.h" #include "Systems/PlayerMovementSystem.h" +#include "Systems/SpawnerSystem.h" +#include "Systems/PlayerSpawnSystem.h" #include "Core/EntityFileWriter.h" Game::Game(int argc, char* argv[]) @@ -73,6 +75,8 @@ Game::Game(int argc, char* argv[]) m_SystemPipeline->AddSystem(updateOrderLevel, m_Renderer); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); + m_SystemPipeline->AddSystem(updateOrderLevel); + m_SystemPipeline->AddSystem(updateOrderLevel); // Populate Octree with collidables ++updateOrderLevel; m_SystemPipeline->AddSystem(updateOrderLevel, m_OctreeCollision); diff --git a/src/Game/Systems/PlayerSpawnSystem.cpp b/src/Game/Systems/PlayerSpawnSystem.cpp new file mode 100644 index 00000000..a522f679 --- /dev/null +++ b/src/Game/Systems/PlayerSpawnSystem.cpp @@ -0,0 +1,39 @@ +#include "Systems/PlayerSpawnSystem.h" + +PlayerSpawnSystem::PlayerSpawnSystem(EventBroker* eventBroker) + : System(eventBroker) +{ + EVENT_SUBSCRIBE_MEMBER(m_OnInputCommand, &PlayerSpawnSystem::OnInputCommand); +} + +void PlayerSpawnSystem::Update(World* world, double dt) +{ + auto componentPools = world->GetComponentPools(); + auto spawnerPool = componentPools.find("Spawner"); + if (spawnerPool == componentPools.end()) { + return; + } +; + for (auto& team : m_SpawnRequests) { + for (auto& spawner : *spawnerPool->second) { + Events::SpawnerSpawn e; + e.Spawner = EntityWrapper(world, spawner.EntityID); + m_EventBroker->Publish(e); + } + } + m_SpawnRequests.clear(); +} + +bool PlayerSpawnSystem::OnInputCommand(const Events::InputCommand& e) +{ + if (e.Command != "PickTeam") { + return false; + } + + if (e.Value != 0) { + m_SpawnRequests.push_back((int)e.Value); + } + + return true; +} + diff --git a/src/Game/Systems/SpawnerSystem.cpp b/src/Game/Systems/SpawnerSystem.cpp new file mode 100644 index 00000000..c1122d98 --- /dev/null +++ b/src/Game/Systems/SpawnerSystem.cpp @@ -0,0 +1,47 @@ +#include "Systems/SpawnerSystem.h" + +SpawnerSystem::SpawnerSystem(EventBroker* eventBroker) : System(eventBroker) +{ + EVENT_SUBSCRIBE_MEMBER(m_OnSpawnerSpawn, &SpawnerSystem::OnSpawnerSpawn); +} + +bool SpawnerSystem::OnSpawnerSpawn(Events::SpawnerSpawn& e) +{ + EntityWrapper& spawner = e.Spawner; + + auto children = spawner.World->GetChildren(spawner.ID); + std::vector spawnPoints; + for (auto kv = children.first; kv != children.second; ++kv) { + const EntityID& child = kv->second; + if (spawner.World->HasComponent(child, "SpawnPoint")) { + spawnPoints.push_back(child); + } + } + + EntityID spawnPoint = spawner.ID; + if (!spawnPoints.empty()) { + // Select a random spawn point + static std::random_device randomDevice; + static std::mt19937 randomGenerator(randomDevice()); + std::uniform_int_distribution<> distribution(0, std::distance(spawnPoints.begin(), spawnPoints.end()) - 1); + auto randomSpawnPointIt = spawnPoints.begin(); + std::advance(randomSpawnPointIt, distribution(randomGenerator)); + spawnPoint = *randomSpawnPointIt; + } + spawnEntity(spawner, e.Parent.ID, Transform::AbsolutePosition(spawner.World, spawnPoint)); + + return true; +} + +void SpawnerSystem::spawnEntity(EntityWrapper spawner, EntityID parent, glm::vec3 position) +{ + const std::string& entityFilePath = spawner["Spawner"]["EntityFile"]; + auto entityFile = ResourceManager::Load(entityFilePath); + if (entityFile == nullptr) { + return; + } + + EntityFileParser parser(entityFile); + EntityWrapper spawnedEntity(spawner.World, parser.MergeEntities(spawner.World, parent)); + spawnedEntity["Transform"]["Position"] = position; +} \ No newline at end of file