From 8eea0c83953dc84be7e9769d1bb9a080be29e0b1 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Thu, 11 Feb 2016 16:08:19 +0100 Subject: [PATCH 1/5] WIP respawning. --- include/Game/Systems/PlayerSpawnSystem.h | 13 ++- resources/DefaultConfig.ini | 1 + resources/Schema/Entities/MovementTest.xml | 117 --------------------- src/Game/Game.cpp | 3 +- src/Game/Systems/PlayerDeathSystem.cpp | 15 ++- src/Game/Systems/PlayerSpawnSystem.cpp | 76 ++++++++++++- src/Game/Systems/SoundSystem.cpp | 4 +- 7 files changed, 101 insertions(+), 128 deletions(-) diff --git a/include/Game/Systems/PlayerSpawnSystem.h b/include/Game/Systems/PlayerSpawnSystem.h index f5aa7b82..681620bb 100644 --- a/include/Game/Systems/PlayerSpawnSystem.h +++ b/include/Game/Systems/PlayerSpawnSystem.h @@ -13,6 +13,8 @@ public: PlayerSpawnSystem(World* world, EventBroker* eventBroker); virtual void Update(double dt) override; + + static void SetRespawnTime(float respawnTime) { m_RespawnTime = respawnTime; }; private: struct SpawnRequest @@ -23,10 +25,19 @@ private: bool m_NetworkEnabled = false; std::vector m_SpawnRequests; + + //Player ID -> EntityWrapper. std::map m_PlayerEntities; + //EntityWrapper ID -> Player ID. + std::map m_PlayerIDs; + + static float m_RespawnTime; + float m_Timer; EventRelay m_OnInputCommand; - bool OnInputCommand(const Events::InputCommand& e); + bool OnInputCommand(Events::InputCommand& e); EventRelay m_OnPlayerSpawnerd; bool OnPlayerSpawned(Events::PlayerSpawned& e); + EventRelay m_OnPlayerDeath; + bool OnPlayerDeath(Events::PlayerDeath& e); }; \ No newline at end of file diff --git a/resources/DefaultConfig.ini b/resources/DefaultConfig.ini index 12ec06c8..1b4fffc8 100644 --- a/resources/DefaultConfig.ini +++ b/resources/DefaultConfig.ini @@ -4,6 +4,7 @@ LoadMap= ; if true -> Pool allocation is not used when calling Allocate/Free, just use regular dynamic allocation. ; if false -> Use pool allocation. DisableMemoryPool=false +RespawnTime = 8.0 [Editor] CameraSpeed=3 diff --git a/resources/Schema/Entities/MovementTest.xml b/resources/Schema/Entities/MovementTest.xml index b8b68ef1..e7bbf236 100644 --- a/resources/Schema/Entities/MovementTest.xml +++ b/resources/Schema/Entities/MovementTest.xml @@ -120,123 +120,6 @@ - - - - - - - - - - false - - - 5 - - - - - - - - - - - - - - - - - - - - - - - - Fonts/DroidSans.ttf,100 - - false - - - - - - - - - - - - - Models/Widgets/Camera.mesh - - - - - - - - - - - - - - - Models/Widgets/Camera.mesh - false - - - - - - - - - - - - Models/Characters/Assault/AssaultHeadless.mesh - - - - - - - - - - - - Models/Core/UnitCube.mesh - - false - - - - - - - - - - - - Models/Core/UnitCube.mesh - - false - - - - - - - - - diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index 95310952..6d8933da 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -34,6 +34,7 @@ Game::Game(int argc, char* argv[]) ResourceManager::UseThreading = m_Config->Get("Multithreading.ResourceLoading", true); DisableMemoryPool::Value = m_Config->Get("Debug.DisableMemoryPool", false); LOG_LEVEL = static_cast<_LOG_LEVEL>(m_Config->Get("Debug.LogLevel", 1)); + PlayerSpawnSystem::SetRespawnTime(m_Config->Get("Debug.RespawnTime", 15.0f)); // Create the core event broker m_EventBroker = new EventBroker(); @@ -97,7 +98,6 @@ Game::Game(int argc, char* argv[]) m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); - m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel, m_Renderer); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); @@ -109,6 +109,7 @@ Game::Game(int argc, char* argv[]) m_SystemPipeline->AddSystem(updateOrderLevel, m_OctreeFrustrumCulling); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); + m_SystemPipeline->AddSystem(updateOrderLevel); // Collision and TriggerSystem should update after player. ++updateOrderLevel; diff --git a/src/Game/Systems/PlayerDeathSystem.cpp b/src/Game/Systems/PlayerDeathSystem.cpp index 60c31ce5..021056fd 100644 --- a/src/Game/Systems/PlayerDeathSystem.cpp +++ b/src/Game/Systems/PlayerDeathSystem.cpp @@ -34,14 +34,23 @@ void PlayerDeathSystem::createDeathEffect(EntityWrapper player) //components that we need from player auto playerCamera = player.FirstChildByName("Camera"); - auto playerEntityModel = player.FirstChildByName("PlayerModel")["Model"]; - auto playerEntityAnimation = player.FirstChildByName("PlayerModel")["Animation"]; + auto playerModel = player.FirstChildByName("PlayerModel"); + if (!playerCamera.Valid() || !playerModel.Valid()) { + return; + } + if (!playerModel.HasComponent("Model") || !playerModel.HasComponent("Animation")) { + return; + } + auto playerEntityModel = playerModel["Model"]; + auto playerEntityAnimation = playerModel["Animation"]; //copy the data from player to explisioneffectmodel playerEntityModel.Copy(deathEffectEW["Model"]); playerEntityAnimation.Copy(deathEffectEW["Animation"]); //freeze the animation - deathEffectEW["Animation"]["Speed"] = 0.0; + deathEffectEW["Animation"]["Speed1"] = 0.0; + deathEffectEW["Animation"]["Speed2"] = 0.0; + deathEffectEW["Animation"]["Speed3"] = 0.0; //copy the models position,orientation deathEffectEW["Transform"]["Position"] = (glm::vec3)player["Transform"]["Position"]; diff --git a/src/Game/Systems/PlayerSpawnSystem.cpp b/src/Game/Systems/PlayerSpawnSystem.cpp index 5f46cce3..0ca7bbac 100644 --- a/src/Game/Systems/PlayerSpawnSystem.cpp +++ b/src/Game/Systems/PlayerSpawnSystem.cpp @@ -1,20 +1,39 @@ #include "Systems/PlayerSpawnSystem.h" +//This should be set by the config anyway. +float PlayerSpawnSystem::m_RespawnTime = 15.0f; + PlayerSpawnSystem::PlayerSpawnSystem(World* m_World, EventBroker* eventBroker) : System(m_World, eventBroker) + , m_Timer(0.f) { EVENT_SUBSCRIBE_MEMBER(m_OnInputCommand, &PlayerSpawnSystem::OnInputCommand); EVENT_SUBSCRIBE_MEMBER(m_OnPlayerSpawnerd, &PlayerSpawnSystem::OnPlayerSpawned); + EVENT_SUBSCRIBE_MEMBER(m_OnPlayerDeath, &PlayerSpawnSystem::OnPlayerDeath); m_NetworkEnabled = ResourceManager::Load("Config.ini")->Get("Networking.StartNetwork", false); } void PlayerSpawnSystem::Update(double dt) { + //Increase timer. + m_Timer += dt; + if (m_Timer < m_RespawnTime) { + return; + } + //If respawn time has passed, we spawn all players that have requested to be spawned. + m_Timer = 0.f; + + //If there are no spawn requests, return immediately, if we are client the SpawnRequests should always be empty. + if (m_SpawnRequests.size() == 0) { + return; + } + auto playerSpawns = m_World->GetComponents("PlayerSpawn"); if (playerSpawns == nullptr) { return; } + int numSpawnedPlayers = 0; for (auto& req : m_SpawnRequests) { for (auto& cPlayerSpawn : *playerSpawns) { EntityWrapper spawner(m_World, cPlayerSpawn.EntityID); @@ -40,13 +59,19 @@ void PlayerSpawnSystem::Update(double dt) e.Player = player; e.Spawner = spawner; m_EventBroker->Publish(e); - + ++numSpawnedPlayers; + break; } } + if (numSpawnedPlayers != (int)m_SpawnRequests.size()) { + LOG_DEBUG("%i players were supposed to be spawned, but %i was spawned.", (int)m_SpawnRequests.size(), numSpawnedPlayers); + } else { + LOG_DEBUG("%i players were spawned.", numSpawnedPlayers); + } m_SpawnRequests.clear(); } -bool PlayerSpawnSystem::OnInputCommand(const Events::InputCommand& e) +bool PlayerSpawnSystem::OnInputCommand(Events::InputCommand& e) { if (e.Command != "PickTeam") { return false; @@ -58,11 +83,37 @@ bool PlayerSpawnSystem::OnInputCommand(const Events::InputCommand& e) return false; } - if (e.Value != 0) { + if (e.Value == 0) { + return false; + } + + //TODO: Spectating? + if (e.Player.Valid() && e.Player.HasComponent("Team")) { + ComponentWrapper cTeam = e.Player["Team"]; + if ((ComponentInfo::EnumType)e.Value == cTeam["Team"].Enum("Spectator")) { + return false; + } + } + + auto iter = m_SpawnRequests.begin(); + for (; iter != m_SpawnRequests.end(); ++iter) { + if (iter->PlayerID == e.PlayerID) { + break; + } + } + + if (iter != m_SpawnRequests.end()) { + //If player is in queue to spawn, then change their team affiliation. + iter->Team = (ComponentInfo::EnumType)e.Value; + } else if (m_PlayerEntities.count(e.PlayerID) == 0 || !m_PlayerEntities[e.PlayerID].Valid()) { + //If player is not in queue to spawn, then create a spawn request, + //but only if they are spectating and/or just connected. SpawnRequest req; req.PlayerID = e.PlayerID; req.Team = (ComponentInfo::EnumType)e.Value; m_SpawnRequests.push_back(req); + } else { + return false; } return true; @@ -82,6 +133,7 @@ bool PlayerSpawnSystem::OnPlayerSpawned(Events::PlayerSpawned& e) // Store the player for future reference m_PlayerEntities[e.PlayerID] = e.Player; + m_PlayerIDs[e.Player.ID] = e.PlayerID; // Set the camera to the correct entity EntityWrapper cameraEntity = e.Player.FirstChildByName("Camera"); @@ -110,4 +162,20 @@ bool PlayerSpawnSystem::OnPlayerSpawned(Events::PlayerSpawned& e) } return true; -} \ No newline at end of file +} + +bool PlayerSpawnSystem::OnPlayerDeath(Events::PlayerDeath& e) +{ + if (!e.Player.HasComponent("Team")) { + return false; + } + ComponentWrapper cTeam = e.Player["Team"]; + //A spectator can't die anyway + if ((ComponentInfo::EnumType)cTeam["Team"] == cTeam["Team"].Enum("Spectator")) { + return false; + } + SpawnRequest req; + req.PlayerID = m_PlayerIDs[e.Player.ID]; + req.Team = cTeam["Team"]; + m_SpawnRequests.push_back(req); +} diff --git a/src/Game/Systems/SoundSystem.cpp b/src/Game/Systems/SoundSystem.cpp index e3aa49b2..7026bfd3 100644 --- a/src/Game/Systems/SoundSystem.cpp +++ b/src/Game/Systems/SoundSystem.cpp @@ -57,10 +57,10 @@ bool SoundSystem::OnInputCommand(const Events::InputCommand & e) return true; } } - if (e.Command == "TakeDamage" && e.Value > 0) { + if (e.Command == "TakeDamage" && e.Value > 0 && m_LocalPlayer.Valid()) { Events::PlayerDamage ev; ev.Player = m_LocalPlayer; - ev.Damage = 1.0; + ev.Damage = e.Value; m_EventBroker->Publish(ev); } From 14054ad8987ebfd5041a52951da822bcf5b57872 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Thu, 11 Feb 2016 17:08:45 +0100 Subject: [PATCH 2/5] WIP respawning. --- src/Game/Systems/PlayerSpawnSystem.cpp | 14 +++++++++----- src/Game/Systems/SoundSystem.cpp | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Game/Systems/PlayerSpawnSystem.cpp b/src/Game/Systems/PlayerSpawnSystem.cpp index aaca4551..0d84bb5d 100644 --- a/src/Game/Systems/PlayerSpawnSystem.cpp +++ b/src/Game/Systems/PlayerSpawnSystem.cpp @@ -95,6 +95,7 @@ bool PlayerSpawnSystem::OnInputCommand(Events::InputCommand& e) } } + //Check if the player already requested spawn. auto iter = m_SpawnRequests.begin(); for (; iter != m_SpawnRequests.end(); ++iter) { if (iter->PlayerID == e.PlayerID) { @@ -103,7 +104,7 @@ bool PlayerSpawnSystem::OnInputCommand(Events::InputCommand& e) } if (iter != m_SpawnRequests.end()) { - //If player is in queue to spawn, then change their team affiliation. + //If player is in queue to spawn, then change their team affiliation in the request. iter->Team = (ComponentInfo::EnumType)e.Value; } else if (m_PlayerEntities.count(e.PlayerID) == 0 || !m_PlayerEntities[e.PlayerID].Valid()) { //If player is not in queue to spawn, then create a spawn request, @@ -121,6 +122,10 @@ bool PlayerSpawnSystem::OnInputCommand(Events::InputCommand& e) bool PlayerSpawnSystem::OnPlayerSpawned(Events::PlayerSpawned& e) { + // Store the player for future reference + m_PlayerEntities[e.PlayerID] = e.Player; + m_PlayerIDs[e.Player.ID] = e.PlayerID; + // When a player is actually spawned (since the actual spawning is handled on the server) if (!IsClient) { return false; @@ -134,10 +139,6 @@ bool PlayerSpawnSystem::OnPlayerSpawned(Events::PlayerSpawned& e) } } - // Store the player for future reference - m_PlayerEntities[e.PlayerID] = e.Player; - m_PlayerIDs[e.Player.ID] = e.PlayerID; - // Set the camera to the correct entity EntityWrapper cameraEntity = e.Player.FirstChildByName("Camera"); if (cameraEntity.Valid()) { @@ -169,6 +170,9 @@ bool PlayerSpawnSystem::OnPlayerSpawned(Events::PlayerSpawned& e) bool PlayerSpawnSystem::OnPlayerDeath(Events::PlayerDeath& e) { + if (!IsServer && m_NetworkEnabled) { + return false; + } if (!e.Player.HasComponent("Team")) { return false; } diff --git a/src/Game/Systems/SoundSystem.cpp b/src/Game/Systems/SoundSystem.cpp index 9788817f..a9b1ccaf 100644 --- a/src/Game/Systems/SoundSystem.cpp +++ b/src/Game/Systems/SoundSystem.cpp @@ -52,7 +52,7 @@ bool SoundSystem::OnInputCommand(const Events::InputCommand & e) return true; } } - if (e.Command == "TakeDamage" && e.Value > 0 && m_LocalPlayer.Valid()) { + if (e.Command == "TakeDamage" && e.Value > 0 && LocalPlayer.Valid()) { Events::PlayerDamage ev; ev.Player = LocalPlayer; ev.Damage = e.Value; From dd131e7652a93623baa0723a7a2967076b970df7 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Thu, 11 Feb 2016 22:49:31 +0100 Subject: [PATCH 3/5] Respawning may work, not been tested over network yet. --- src/Game/Systems/PlayerSpawnSystem.cpp | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/Game/Systems/PlayerSpawnSystem.cpp b/src/Game/Systems/PlayerSpawnSystem.cpp index 0d84bb5d..65184384 100644 --- a/src/Game/Systems/PlayerSpawnSystem.cpp +++ b/src/Game/Systems/PlayerSpawnSystem.cpp @@ -78,8 +78,8 @@ bool PlayerSpawnSystem::OnInputCommand(Events::InputCommand& e) } // Team picks should be processed ONLY server-side! - // Don't make a spawn request if PlayerID is -1, i.e. we're the client. - if (e.PlayerID == -1 && m_NetworkEnabled) { + // Don't make a spawn request if we're the client. + if (!IsServer && m_NetworkEnabled) { return false; } @@ -88,11 +88,10 @@ bool PlayerSpawnSystem::OnInputCommand(Events::InputCommand& e) } //TODO: Spectating? - if (e.Player.Valid() && e.Player.HasComponent("Team")) { - ComponentWrapper cTeam = e.Player["Team"]; - if ((ComponentInfo::EnumType)e.Value == cTeam["Team"].Enum("Spectator")) { - return false; - } + //Right now, return if someone picks spectator. + //1 signifies spectator here, could not get Playerteam component since it may be invalid or without team comp. + if ((ComponentInfo::EnumType)e.Value == 1) { + return false; } //Check if the player already requested spawn. @@ -131,13 +130,6 @@ bool PlayerSpawnSystem::OnPlayerSpawned(Events::PlayerSpawned& e) return false; } - // Check if a player already exists - if (m_PlayerEntities.count(e.PlayerID) != 0) { - // TODO: Disallow infinite respawning here - if (m_PlayerEntities[e.PlayerID].Valid()) { - m_World->DeleteEntity(m_PlayerEntities[e.PlayerID].ID); - } - } // Set the camera to the correct entity EntityWrapper cameraEntity = e.Player.FirstChildByName("Camera"); @@ -170,6 +162,7 @@ bool PlayerSpawnSystem::OnPlayerSpawned(Events::PlayerSpawned& e) bool PlayerSpawnSystem::OnPlayerDeath(Events::PlayerDeath& e) { + //Only spawn request if network is disabled or we are server. if (!IsServer && m_NetworkEnabled) { return false; } @@ -182,7 +175,7 @@ bool PlayerSpawnSystem::OnPlayerDeath(Events::PlayerDeath& e) return false; } SpawnRequest req; - req.PlayerID = m_PlayerIDs[e.Player.ID]; + req.PlayerID = m_PlayerIDs.at(e.Player.ID); req.Team = cTeam["Team"]; m_SpawnRequests.push_back(req); } From f1c2413cf930c7d05601bda62d6bee0deb4072a4 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Fri, 12 Feb 2016 00:39:27 +0100 Subject: [PATCH 4/5] Respawning and team picking seems to be working well. --- src/Game/Systems/DamageIndicatorSystem.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Game/Systems/DamageIndicatorSystem.cpp b/src/Game/Systems/DamageIndicatorSystem.cpp index a29309b5..8d586ac1 100644 --- a/src/Game/Systems/DamageIndicatorSystem.cpp +++ b/src/Game/Systems/DamageIndicatorSystem.cpp @@ -22,6 +22,10 @@ bool DamageIndicatorSystem::OnPlayerDamage(Events::PlayerDamage& e) return false; } + if (!e.Inflictor.Valid() || !e.Victim.Valid()) { + return false; + } + //grab players direction auto playerOrientation = glm::quat((glm::vec3)e.Victim["Transform"]["Orientation"]); From 40ae2b8cbbe5bb72d5ed24578930ad6ef396e5e2 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Fri, 12 Feb 2016 00:52:58 +0100 Subject: [PATCH 5/5] DefaultInput key 'K' to kill player, since we can't respawn when alive. Also moved TakeDamage InputCommand to HealthSystem. --- include/Game/Systems/HealthSystem.h | 5 ++++- resources/DefaultInput.ini | 3 ++- src/Game/Systems/HealthSystem.cpp | 12 ++++++++++++ src/Game/Systems/SoundSystem.cpp | 6 ------ 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/include/Game/Systems/HealthSystem.h b/include/Game/Systems/HealthSystem.h index b66d8eb0..21a52330 100644 --- a/include/Game/Systems/HealthSystem.h +++ b/include/Game/Systems/HealthSystem.h @@ -9,6 +9,7 @@ #include "Core/EPlayerDamage.h" #include "Core/EPlayerHealthPickup.h" #include "Core/EPlayerDeath.h" +#include "../Engine/Input/EInputCommand.h" #include #include @@ -28,7 +29,9 @@ private: bool HealthSystem::OnPlayerDamaged(Events::PlayerDamage& e); EventRelay m_EPlayerHealthPickup; bool HealthSystem::OnPlayerHealthPickup(Events::PlayerHealthPickup& e); - + EventRelay m_InputCommand; + bool HealthSystem::OnInputCommand(Events::InputCommand& e); + //vector which will keep track of health changes std::vector> m_DeltaHealthVector; diff --git a/resources/DefaultInput.ini b/resources/DefaultInput.ini index 683d48e2..d5489c3a 100644 --- a/resources/DefaultInput.ini +++ b/resources/DefaultInput.ini @@ -24,4 +24,5 @@ F1=ToggleEditor C=ConnectToServer N=SwitchToServer M=SwitchToClient -P=SwitchToPlayer \ No newline at end of file +P=SwitchToPlayer +K=TakeDamage,1500 \ No newline at end of file diff --git a/src/Game/Systems/HealthSystem.cpp b/src/Game/Systems/HealthSystem.cpp index 29d8790d..0fc113b9 100644 --- a/src/Game/Systems/HealthSystem.cpp +++ b/src/Game/Systems/HealthSystem.cpp @@ -7,6 +7,7 @@ HealthSystem::HealthSystem(SystemParams params) //subscribe/listenTo playerdamage,healthpickup events (using the eventBroker) EVENT_SUBSCRIBE_MEMBER(m_EPlayerDamage, &HealthSystem::OnPlayerDamaged); EVENT_SUBSCRIBE_MEMBER(m_EPlayerHealthPickup, &HealthSystem::OnPlayerHealthPickup); + EVENT_SUBSCRIBE_MEMBER(m_InputCommand, &HealthSystem::OnInputCommand); } void HealthSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt) @@ -29,6 +30,17 @@ bool HealthSystem::OnPlayerDamaged(Events::PlayerDamage& e) return true; } +bool HealthSystem::OnInputCommand(Events::InputCommand& e) +{ + if (e.Command == "TakeDamage" && e.Value > 0 && LocalPlayer.Valid()) { + Events::PlayerDamage ev; + ev.Victim = LocalPlayer; + ev.Damage = e.Value; + m_EventBroker->Publish(ev); + } + return true; +} + bool HealthSystem::OnPlayerHealthPickup(Events::PlayerHealthPickup& e) { ComponentWrapper cHealth = e.Player["Health"]; diff --git a/src/Game/Systems/SoundSystem.cpp b/src/Game/Systems/SoundSystem.cpp index db3575ab..59fd4a7f 100644 --- a/src/Game/Systems/SoundSystem.cpp +++ b/src/Game/Systems/SoundSystem.cpp @@ -52,12 +52,6 @@ bool SoundSystem::OnInputCommand(const Events::InputCommand & e) return true; } } - if (e.Command == "TakeDamage" && e.Value > 0 && LocalPlayer.Valid()) { - Events::PlayerDamage ev; - ev.Victim = LocalPlayer; - ev.Damage = e.Value; - m_EventBroker->Publish(ev); - } return false; }