Merge branch 'master' into Importer
# Conflicts: # src/Engine/Collision/Collision.cpp # src/Engine/Collision/CollisionSystem.cpp # src/Engine/Rendering/DrawFinalPass.cpp # src/Game/Systems/SpawnerSystem.cpp
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
#include "Game/Systems/AbilityCooldownHUDSystem.h"
|
||||
|
||||
void AbilityCooldownHUDSystem::Update(double dt)
|
||||
{
|
||||
//HUD element for tracking cooldown on the parent entity with Dashability TODO: Make sure it support other abilities when they are made.
|
||||
|
||||
auto abilityHUDs = m_World->GetComponents("AbilityCooldownHUD");
|
||||
if (abilityHUDs == nullptr)
|
||||
return;
|
||||
|
||||
for (auto& abilityHUDC : *abilityHUDs) {
|
||||
EntityWrapper entity = EntityWrapper(m_World, abilityHUDC.EntityID);
|
||||
EntityWrapper abilityEntity = entity.FirstParentWithComponent("DashAbility");
|
||||
std::string abilityName = "";
|
||||
|
||||
if (!abilityEntity.Valid()) {
|
||||
//If we dont have a dash ability on player, we check for Sprint ability
|
||||
abilityEntity = entity.FirstParentWithComponent("SprintAbility");
|
||||
|
||||
if (!abilityEntity.Valid()) {
|
||||
//If we dont have a sprint ability on player, we check for shield ability
|
||||
abilityEntity = entity.FirstParentWithComponent("ShieldAbility");
|
||||
|
||||
if (!abilityEntity.Valid()) {
|
||||
//If we dont have a shield ability, we return, since we cannot do anything.
|
||||
if (entity.HasComponent("Sprite")) {
|
||||
(Field<std::string>)entity["Sprite"]["DiffuseTexture"] = "Textures/Test/aM4ME4GR.png";
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
//If we have a shield ability, we set the right icon
|
||||
abilityName = "ShieldAbility";
|
||||
if (entity.HasComponent("Sprite")) {
|
||||
(Field<std::string>)entity["Sprite"]["DiffuseTexture"] = "Textures/Icons/Abilities/Long/SheildDots-01.png";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//If we do have a sprint ability, we change the icon
|
||||
abilityName = "SprintAbility";
|
||||
if (entity.HasComponent("Sprite")) {
|
||||
(Field<std::string>)entity["Sprite"]["DiffuseTexture"] = "Textures/Icons/Abilities/Long/Dash-01.png";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//If we have a dash ability, we set the icon to the correct one.
|
||||
abilityName = "DashAbility";
|
||||
if (entity.HasComponent("Sprite")) {
|
||||
(Field<std::string>)entity["Sprite"]["DiffuseTexture"] = "Textures/Icons/Abilities/Long/Superman-01.png";
|
||||
}
|
||||
}
|
||||
|
||||
EntityWrapper cooldownTextEntity = entity.FirstChildByName("Cooldown");
|
||||
//TODO: Fix so this track correctly for shield and sprint depending on how they work.
|
||||
double maxAbilityCD, currentAbilityCD;
|
||||
|
||||
if (abilityName == "DashAbility") {
|
||||
maxAbilityCD = (double)abilityEntity[abilityName]["CoolDownMaxTimer"];
|
||||
currentAbilityCD = (double)abilityEntity[abilityName]["CoolDownTimer"];
|
||||
currentAbilityCD = currentAbilityCD >= 0.0 ? currentAbilityCD : 0.0;
|
||||
|
||||
if (cooldownTextEntity.Valid()) {
|
||||
if (cooldownTextEntity.HasComponent("Text")) {
|
||||
(Field<std::string>)cooldownTextEntity["Text"]["Content"] = std::to_string(currentAbilityCD).substr(0, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (abilityName == "ShieldAbility") {
|
||||
maxAbilityCD = 1.0;
|
||||
currentAbilityCD = 1 - (bool)abilityEntity[abilityName]["Active"];
|
||||
}
|
||||
|
||||
if (abilityName == "SprintAbility") {
|
||||
maxAbilityCD = 1.0;
|
||||
currentAbilityCD = 1 - (bool)abilityEntity[abilityName]["Active"];
|
||||
}
|
||||
|
||||
if (entity.HasComponent("Fill")) {
|
||||
(Field<double>)entity["Fill"]["Percentage"] = currentAbilityCD/maxAbilityCD;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ AmmoPickupSystem::AmmoPickupSystem(SystemParams params)
|
||||
{
|
||||
if (IsServer) {
|
||||
EVENT_SUBSCRIBE_MEMBER(m_ETriggerTouch, &AmmoPickupSystem::OnTriggerTouch);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_ETriggerLeave, &AmmoPickupSystem::OnTriggerLeave);
|
||||
}
|
||||
if (IsClient) {
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EAmmoPickup, &AmmoPickupSystem::OnAmmoPickup);
|
||||
@@ -14,77 +15,118 @@ AmmoPickupSystem::AmmoPickupSystem(SystemParams params)
|
||||
void AmmoPickupSystem::Update(double dt)
|
||||
{
|
||||
if (IsServer) {
|
||||
for (auto it = m_ETriggerTouchVector.begin(); it != m_ETriggerTouchVector.end(); ++it) {
|
||||
auto& ammoPickupPosition = *it;
|
||||
//set the double timer value (value 3)
|
||||
ammoPickupPosition.DecreaseThisRespawnTimer -= dt;
|
||||
if (ammoPickupPosition.DecreaseThisRespawnTimer < 0.0) {
|
||||
//spawn and delete the vector item
|
||||
auto it = m_ETriggerTouchVector.begin();
|
||||
while (it != m_ETriggerTouchVector.end()) {
|
||||
auto& somePickup = *it;
|
||||
somePickup.DecreaseThisRespawnTimer -= dt;
|
||||
if (somePickup.DecreaseThisRespawnTimer < 0.0) {
|
||||
auto entityFile = ResourceManager::Load<EntityFile>("Schema/Entities/AmmoPickup.xml");
|
||||
EntityFileParser parser(entityFile);
|
||||
EntityID ammoPickupID = parser.MergeEntities(m_World);
|
||||
EntityWrapper ammoPickup = entityFile->MergeInto(m_World);
|
||||
|
||||
//let the world know a pickup has spawned (graphics effects, etc)
|
||||
//let the world know a pickup has spawned
|
||||
Events::PickupSpawned ePickupSpawned;
|
||||
ePickupSpawned.Pickup = EntityWrapper(m_World, ammoPickupID);
|
||||
ePickupSpawned.Pickup = ammoPickup;
|
||||
m_EventBroker->Publish(ePickupSpawned);
|
||||
|
||||
//set values from the old entity to the new entity
|
||||
auto& newAmmoPickupEntity = EntityWrapper(m_World, ammoPickupID);
|
||||
newAmmoPickupEntity["Transform"]["Position"] = ammoPickupPosition.Pos;
|
||||
newAmmoPickupEntity["AmmoPickup"]["AmmoGain"] = ammoPickupPosition.AmmoGain;
|
||||
newAmmoPickupEntity["AmmoPickup"]["RespawnTimer"] = ammoPickupPosition.RespawnTimer;
|
||||
m_World->SetParent(newAmmoPickupEntity.ID, ammoPickupPosition.parentID);
|
||||
//copy values from the old entity to the new entity
|
||||
auto& newAmmoPickupEntity = ammoPickup;
|
||||
newAmmoPickupEntity["Transform"]["Position"] = somePickup.Pos;
|
||||
newAmmoPickupEntity["AmmoPickup"]["AmmoGain"] = somePickup.AmmoGain;
|
||||
newAmmoPickupEntity["AmmoPickup"]["RespawnTimer"] = somePickup.RespawnTimer;
|
||||
m_World->SetParent(newAmmoPickupEntity.ID, somePickup.parentID);
|
||||
|
||||
//erase the current element (AmmoPickupPosition)
|
||||
m_ETriggerTouchVector.erase(it);
|
||||
//erase the current element (somePickup)
|
||||
it = m_ETriggerTouchVector.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
//still touching m_PickupAtMaximum?
|
||||
for (auto& it = m_PickupAtMaximum.begin(); it != m_PickupAtMaximum.end(); ++it) {
|
||||
if (!it->player.Valid()) {
|
||||
m_PickupAtMaximum.erase(it);
|
||||
break;
|
||||
}
|
||||
if (!DoesPlayerHaveMaxAmmo(it->player)) {
|
||||
DoPickup(it->player, it->trigger);
|
||||
m_PickupAtMaximum.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bool AmmoPickupSystem::DoesPlayerHaveMaxAmmo(EntityWrapper &player) {
|
||||
PlayerClass playerClass = DetermineClass(player);
|
||||
if (playerClass == PlayerClass::Defender) {
|
||||
return !((int)player["DefenderWeapon"]["Ammo"] < (int)player["DefenderWeapon"]["MaxAmmo"]);
|
||||
} else if (playerClass == PlayerClass::Sniper) {
|
||||
return !((int)player["SniperWeapon"]["Ammo"] < (int)player["SniperWeapon"]["MaxAmmo"]);
|
||||
} else if (playerClass == PlayerClass::Assault) {
|
||||
return !((int)player["AssaultWeapon"]["Ammo"] < (int)player["AssaultWeapon"]["MaxAmmo"]);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void AmmoPickupSystem::SetPlayerAmmo(EntityWrapper &player, int ammoGain) {
|
||||
int maxWeaponAmmo = GetPlayerMaxAmmo(player);
|
||||
|
||||
PlayerClass playerClass = DetermineClass(player);
|
||||
if (playerClass == PlayerClass::Defender) {
|
||||
(Field<int>)player["DefenderWeapon"]["Ammo"] = std::min((int)player["DefenderWeapon"]["Ammo"] + ammoGain, maxWeaponAmmo);
|
||||
} else if (playerClass == PlayerClass::Sniper) {
|
||||
(Field<int>)player["SniperWeapon"]["Ammo"] = std::min((int)player["SniperWeapon"]["Ammo"] + ammoGain, maxWeaponAmmo);
|
||||
} else if (playerClass == PlayerClass::Assault) {
|
||||
(Field<int>)player["AssaultWeapon"]["Ammo"] = std::min((int)player["AssaultWeapon"]["Ammo"] + ammoGain, maxWeaponAmmo);
|
||||
} else {
|
||||
//unknown class - ignore
|
||||
}
|
||||
}
|
||||
int AmmoPickupSystem::GetPlayerMaxAmmo(EntityWrapper &player) {
|
||||
PlayerClass playerClass = DetermineClass(player);
|
||||
if (playerClass == PlayerClass::Defender) {
|
||||
return (int)player["DefenderWeapon"]["MaxAmmo"];
|
||||
} else if (playerClass == PlayerClass::Sniper) {
|
||||
return (int)player["SniperWeapon"]["MaxAmmo"];
|
||||
} else if (playerClass == PlayerClass::Assault) {
|
||||
return (int)player["AssaultWeapon"]["MaxAmmo"];
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
AmmoPickupSystem::PlayerClass AmmoPickupSystem::DetermineClass(EntityWrapper &player)
|
||||
{
|
||||
//determine the class based on what component the inflictor-player has
|
||||
if (m_World->HasComponent(player.ID, "DashAbility")) {
|
||||
return PlayerClass::Assault;
|
||||
}
|
||||
if (m_World->HasComponent(player.ID, "ShieldAbility")) {
|
||||
return PlayerClass::Defender;
|
||||
}
|
||||
if (m_World->HasComponent(player.ID, "SprintAbility")) {
|
||||
return PlayerClass::Sniper;
|
||||
}
|
||||
return PlayerClass::None;
|
||||
}
|
||||
|
||||
bool AmmoPickupSystem::OnTriggerTouch(Events::TriggerTouch& e)
|
||||
{
|
||||
/*if (e.Entity != LocalPlayer) {
|
||||
return false;
|
||||
}*/
|
||||
|
||||
if (!e.Entity.Valid()) {
|
||||
return false;
|
||||
}
|
||||
//TODO: add other weapontypes
|
||||
if (!e.Entity.HasComponent("AssaultWeapon")) {
|
||||
if (DetermineClass(e.Entity) == PlayerClass::None) {
|
||||
return false;
|
||||
}
|
||||
if (!e.Trigger.HasComponent("AmmoPickup")) {
|
||||
return false;
|
||||
}
|
||||
int maxWeaponAmmo = (int)e.Entity["AssaultWeapon"]["MaxAmmo"];
|
||||
int& currentAmmo = (int)e.Entity["AssaultWeapon"]["Ammo"];
|
||||
|
||||
int ammoGiven = 0.01*(double)e.Trigger["AmmoPickup"]["AmmoGain"] * maxWeaponAmmo;
|
||||
//cant pick up ammopacks if you are already at MaxAmmo
|
||||
if (currentAmmo >= maxWeaponAmmo) {
|
||||
//if at maxammo, save the trigger-touch to a vector since standing inside it will not re-trigger the trigger
|
||||
if (DoesPlayerHaveMaxAmmo(e.Entity)) {
|
||||
m_PickupAtMaximum.push_back({ e.Entity, e.Trigger });
|
||||
return false;
|
||||
}
|
||||
|
||||
//personEntered = e.Entity, thingEntered = e.Trigger
|
||||
Events::AmmoPickup ePlayerAmmoPickup;
|
||||
ePlayerAmmoPickup.AmmoGain = ammoGiven;
|
||||
ePlayerAmmoPickup.Player = e.Entity;
|
||||
m_EventBroker->Publish(ePlayerAmmoPickup);
|
||||
//immediately give the player the ammo
|
||||
//currentAmmo = std::min(currentAmmo + ammoGiven, maxWeaponAmmo);
|
||||
|
||||
//copy position, ammogain, respawntimer (twice since one of the values will be counted down to 0, the other will be set in the new object)
|
||||
//we need to copy all values since each value can be different for each ammoPickup
|
||||
m_ETriggerTouchVector.push_back({ e.Trigger["Transform"]["Position"], e.Trigger["AmmoPickup"]["AmmoGain"],
|
||||
e.Trigger["AmmoPickup"]["RespawnTimer"], e.Trigger["AmmoPickup"]["RespawnTimer"], m_World->GetParent(e.Trigger.ID) });
|
||||
|
||||
//delete the ammopickup
|
||||
m_World->DeleteEntity(e.Trigger.ID);
|
||||
DoPickup(e.Entity, e.Trigger);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -94,16 +136,50 @@ bool AmmoPickupSystem::OnAmmoPickup(Events::AmmoPickup & e)
|
||||
return false;
|
||||
}
|
||||
//TODO: add other weapontypes
|
||||
if (!e.Player.HasComponent("AssaultWeapon")) {
|
||||
if (DetermineClass(e.Player) == PlayerClass::None) {
|
||||
return false;
|
||||
}
|
||||
int maxWeaponAmmo = (int)e.Player["AssaultWeapon"]["MaxAmmo"];
|
||||
int& currentAmmo = (int)e.Player["AssaultWeapon"]["Ammo"];
|
||||
//cant pick up ammopacks if you are already at MaxAmmo
|
||||
if (currentAmmo >= maxWeaponAmmo) {
|
||||
if (DoesPlayerHaveMaxAmmo(e.Player)) {
|
||||
return false;
|
||||
}
|
||||
SetPlayerAmmo(e.Player, e.AmmoGain);
|
||||
|
||||
currentAmmo = std::min(currentAmmo + e.AmmoGain, maxWeaponAmmo);
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AmmoPickupSystem::OnTriggerLeave(Events::TriggerLeave& e) {
|
||||
if (!e.Trigger.HasComponent("AmmoPickup")) {
|
||||
return false;
|
||||
}
|
||||
//triggerleave erases possible m_PickupAtMaximum
|
||||
for (auto& it = m_PickupAtMaximum.begin(); it != m_PickupAtMaximum.end(); ++it) {
|
||||
if (it->trigger.ID == e.Trigger.ID && it->player.ID == e.Entity.ID) {
|
||||
m_PickupAtMaximum.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void AmmoPickupSystem::DoPickup(EntityWrapper &player, EntityWrapper &trigger) {
|
||||
//trigger should be valid but if it isnt we just return (to avoid crash)
|
||||
if (!trigger.Valid()) {
|
||||
return;
|
||||
}
|
||||
int maxWeaponAmmo = GetPlayerMaxAmmo(player);
|
||||
int ammoGiven = 0.01*(double)trigger["AmmoPickup"]["AmmoGain"] * maxWeaponAmmo;
|
||||
|
||||
Events::AmmoPickup ePlayerAmmoPickup;
|
||||
ePlayerAmmoPickup.AmmoGain = ammoGiven;
|
||||
ePlayerAmmoPickup.Player = player;
|
||||
m_EventBroker->Publish(ePlayerAmmoPickup);
|
||||
|
||||
//copy position, ammogain, respawntimer (twice since one of the values will be counted down to 0, the other will be set in the new object)
|
||||
//we need to copy all values since each value can be different for each ammoPickup
|
||||
m_ETriggerTouchVector.push_back({ (glm::vec3)trigger["Transform"]["Position"], trigger["AmmoPickup"]["AmmoGain"],
|
||||
trigger["AmmoPickup"]["RespawnTimer"], trigger["AmmoPickup"]["RespawnTimer"], m_World->GetParent(trigger.ID) });
|
||||
|
||||
//delete the ammopickup
|
||||
m_World->DeleteEntity(trigger.ID);
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
#include "Game/Systems/AmmunitionHUDSystem.h"
|
||||
|
||||
void AmmunitionHUDSystem::Update(double dt)
|
||||
{
|
||||
//Hud element for tracking ammunition from parent with AssaultWeapon component.Child with the name "MagazineAmmo" tracks clip ammunition.Child with the name "Ammo" tracks ammo.</xs:documentation>
|
||||
|
||||
auto ammunitionHUDs = m_World->GetComponents("AmmunitionHUD");
|
||||
if (ammunitionHUDs == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& ammunitionHUDComponent : *ammunitionHUDs) {
|
||||
EntityWrapper entity = EntityWrapper(m_World, ammunitionHUDComponent.EntityID);
|
||||
|
||||
EntityWrapper playerEntity = entity.FirstParentWithComponent("AssaultWeapon");
|
||||
|
||||
if (!playerEntity.Valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
EntityWrapper magazineAmmo = entity.FirstChildByName("MagazineAmmo");
|
||||
if(magazineAmmo.Valid()) {
|
||||
if(magazineAmmo.HasComponent("Text")) {
|
||||
(std::string&)magazineAmmo["Text"]["Content"] = std::to_string((int)playerEntity["AssaultWeapon"]["MagazineAmmo"]);
|
||||
}
|
||||
}
|
||||
|
||||
EntityWrapper ammo = entity.FirstChildByName("Ammo");
|
||||
if (ammo.Valid()) {
|
||||
if (ammo.HasComponent("Text")) {
|
||||
(std::string&)ammo["Text"]["Content"] = std::to_string((int)playerEntity["AssaultWeapon"]["Ammo"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#include "Game/Systems/BoostIconsHUDSystem.h"
|
||||
|
||||
void BoostIconsHUDSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& capturePoint, double dt)
|
||||
{
|
||||
EntityWrapper assaultEntity = entity.FirstChildByName("Assault");
|
||||
EntityWrapper defenderEntity = entity.FirstChildByName("Defender");
|
||||
EntityWrapper sniperEntity = entity.FirstChildByName("Sniper");
|
||||
EntityWrapper player = entity.FirstParentWithComponent("Player");
|
||||
|
||||
|
||||
if(assaultEntity.Valid()) {
|
||||
if (assaultEntity.HasComponent("Fill")) {
|
||||
EntityWrapper assaultBoost = player.FirstChildByName("BoostAssault");
|
||||
if (assaultBoost.Valid()) {
|
||||
(Field<double>)assaultEntity["Fill"]["Percentage"] = 1.0;
|
||||
} else {
|
||||
(Field<double>)assaultEntity["Fill"]["Percentage"] = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (defenderEntity.Valid()) {
|
||||
if (defenderEntity.HasComponent("Fill")) {
|
||||
EntityWrapper defenderBoost = player.FirstChildByName("BoostDefender");
|
||||
if (defenderBoost.Valid()) {
|
||||
(Field<double>)defenderEntity["Fill"]["Percentage"] = 1.0;
|
||||
} else {
|
||||
(Field<double>)defenderEntity["Fill"]["Percentage"] = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sniperEntity.Valid()) {
|
||||
if (sniperEntity.HasComponent("Fill")) {
|
||||
EntityWrapper sniperBoost = player.FirstChildByName("BoostSniper");
|
||||
if (sniperBoost.Valid()) {
|
||||
(Field<double>)sniperEntity["Fill"]["Percentage"] = 1.0;
|
||||
} else {
|
||||
(Field<double>)sniperEntity["Fill"]["Percentage"] = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
#include "Systems/BoostSystem.h"
|
||||
|
||||
BoostSystem::BoostSystem(SystemParams params)
|
||||
: System(params)
|
||||
{
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayerDamage, &BoostSystem::OnPlayerDamage);
|
||||
}
|
||||
|
||||
bool BoostSystem::OnPlayerDamage(Events::PlayerDamage& e)
|
||||
{
|
||||
|
||||
|
||||
if (e.Victim == e.Inflictor) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!e.Victim.Valid() || !e.Inflictor.Valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//if its not friendly fire, return
|
||||
if ((int)m_World->GetComponent(e.Inflictor.ID, "Team")["Team"] != (int)m_World->GetComponent(e.Victim.ID, "Team")["Team"]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//determine the inflictors class
|
||||
auto className = DetermineClass(e.Inflictor);
|
||||
if (className == "") {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (className == "SidearmWeapon") { // give ammo
|
||||
giveAmmo(e.Inflictor, e.Victim);
|
||||
} else { //give boost
|
||||
if (!IsServer) {
|
||||
return false;
|
||||
}
|
||||
//get the XML file, example: "Schema/Entities/BoostclassName.xml"
|
||||
std::string classXML = "Schema/Entities/" + className + ".xml";
|
||||
|
||||
//check if player already has a child with the component, if so delete that child
|
||||
auto playerBoostAssaultEntity = e.Victim.FirstChildByName(className);
|
||||
if (playerBoostAssaultEntity.Valid()) {
|
||||
m_World->DeleteEntity(playerBoostAssaultEntity.ID);
|
||||
}
|
||||
//load boost XML file, set it entity parented with the victim player
|
||||
auto entityFile = ResourceManager::Load<EntityFile>(classXML);
|
||||
EntityWrapper boostAssaultEntity = entityFile->MergeInto(m_World);
|
||||
m_World->SetName(boostAssaultEntity.ID, className);
|
||||
m_World->SetParent(boostAssaultEntity.ID, e.Victim.ID);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string BoostSystem::DetermineClass(EntityWrapper inflictorPlayer)
|
||||
{
|
||||
//determine the class based on what component the inflictor-player has
|
||||
if (inflictorPlayer.HasComponent("Player")) {
|
||||
if ((std::string)inflictorPlayer["Player"]["CurrentWeapon"] == "AssaultWeapon") {
|
||||
return "BoostAssault";
|
||||
}
|
||||
if ((std::string)inflictorPlayer["Player"]["CurrentWeapon"] == "DefenderWeapon") {
|
||||
return "BoostDefender";
|
||||
}
|
||||
if ((std::string)inflictorPlayer["Player"]["CurrentWeapon"] == "SniperWeapon") {
|
||||
return "BoostSniper";
|
||||
}
|
||||
if ((std::string)inflictorPlayer["Player"]["CurrentWeapon"] == "SidearmWeapon") {
|
||||
return "SidearmWeapon";
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void BoostSystem::giveAmmo(EntityWrapper giver, EntityWrapper receiver)
|
||||
{
|
||||
if (receiver.HasComponent("AssaultWeapon")) {
|
||||
int magazineSize = receiver["AssaultWeapon"]["MagazineSize"];
|
||||
Field<int> magazineAmmo = receiver["AssaultWeapon"]["MagazineAmmo"];
|
||||
int prevMagazineAmmo = receiver["AssaultWeapon"]["MagazineAmmo"];
|
||||
int maxAmmo = receiver["AssaultWeapon"]["MaxAmmo"];
|
||||
Field<int> ammo = receiver["AssaultWeapon"]["Ammo"];
|
||||
|
||||
int givenAmmo = 20;
|
||||
|
||||
magazineAmmo = glm::clamp(magazineAmmo + givenAmmo, 0, magazineSize);
|
||||
if (magazineAmmo > prevMagazineAmmo) {
|
||||
givenAmmo = prevMagazineAmmo + givenAmmo - magazineSize;
|
||||
}
|
||||
if (givenAmmo > 0) {
|
||||
ammo = glm::clamp(ammo + givenAmmo, 0, maxAmmo);
|
||||
}
|
||||
} else if (receiver.HasComponent("DefenderWeapon")) {
|
||||
int magazineSize = receiver["DefenderWeapon"]["MagazineSize"];
|
||||
Field<int> magazineAmmo = receiver["DefenderWeapon"]["MagazineAmmo"];
|
||||
int prevMagazineAmmo = receiver["DefenderWeapon"]["MagazineAmmo"];
|
||||
int maxAmmo = receiver["DefenderWeapon"]["MaxAmmo"];
|
||||
Field<int> ammo = receiver["DefenderWeapon"]["Ammo"];
|
||||
|
||||
int givenAmmo = 3;
|
||||
|
||||
magazineAmmo = glm::clamp(magazineAmmo + givenAmmo, 0, magazineSize);
|
||||
if (magazineAmmo > prevMagazineAmmo) {
|
||||
givenAmmo = prevMagazineAmmo + givenAmmo - magazineSize;
|
||||
}
|
||||
if (givenAmmo > 0) {
|
||||
ammo = glm::clamp(ammo + givenAmmo, 0, maxAmmo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
#include "Systems/CapturePointArrowHUDSystem.h"
|
||||
|
||||
CapturePointArrowHUDSystem::CapturePointArrowHUDSystem(SystemParams params)
|
||||
: System(params)
|
||||
, ImpureSystem()
|
||||
{
|
||||
EVENT_SUBSCRIBE_MEMBER(m_ECapturedEvent, &CapturePointArrowHUDSystem::OnCapturePointCaptured);
|
||||
}
|
||||
|
||||
|
||||
void CapturePointArrowHUDSystem::Update(double dt)
|
||||
{
|
||||
bool loadCheck = true;
|
||||
int redTeamEnum;
|
||||
int blueTeamEnum;
|
||||
int spectatorTeamEnum;
|
||||
|
||||
//Get list for all CapturePointArrowHUDComponents
|
||||
auto arrowHUDs = m_World->GetComponents("CapturePointArrowHUD");
|
||||
auto capturePoints = m_World->GetComponents("CapturePoint");
|
||||
if(arrowHUDs == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(auto& cArrowHUD : *arrowHUDs) {
|
||||
//Get what team the current arrow corresponds to
|
||||
EntityWrapper arrowEntity = EntityWrapper(m_World, cArrowHUD.EntityID);
|
||||
if (!arrowEntity.Valid()) {
|
||||
continue;
|
||||
}
|
||||
if(!arrowEntity.HasComponent("Team")) {
|
||||
continue;
|
||||
}
|
||||
auto cTeam = arrowEntity["Team"];
|
||||
int currentTeam = (int)cTeam["Team"];
|
||||
|
||||
if (loadCheck) {
|
||||
redTeamEnum = (int)cTeam["Team"].Enum("Red");
|
||||
blueTeamEnum = (int)cTeam["Team"].Enum("Blue");
|
||||
spectatorTeamEnum = (int)cTeam["Team"].Enum("Spectator");
|
||||
loadCheck = false;
|
||||
|
||||
|
||||
if (!m_InitialtargetsSet) {
|
||||
std::unordered_map<int, glm::vec3> blueTargets, redTargets;
|
||||
EntityWrapper homeBlue, homeRed;
|
||||
int lastCP = -INFINITY;
|
||||
int firstCP = INFINITY;
|
||||
|
||||
for (auto& cCP : *capturePoints) {
|
||||
auto homePointTeam = (int)cCP["HomePointForTeam"];
|
||||
EntityWrapper capturePointEntity = EntityWrapper(m_World, cCP.EntityID);
|
||||
int capturePointID = (int)capturePointEntity["CapturePoint"]["CapturePointNumber"];
|
||||
|
||||
if(capturePointID < firstCP) {
|
||||
firstCP = capturePointID;
|
||||
}
|
||||
|
||||
if(capturePointID > lastCP) {
|
||||
lastCP = capturePointID;
|
||||
}
|
||||
|
||||
if (!capturePointEntity.HasComponent("Team")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int currentOwner = (int)capturePointEntity["Team"]["Team"];
|
||||
|
||||
if(currentOwner != redTeamEnum) {
|
||||
//This capturePoint is not owned by the red team and is therefor an eligible target for red team
|
||||
glm::vec3 targetPos = TransformSystem::AbsolutePosition(capturePointEntity);
|
||||
redTargets.insert(std::pair<int, glm::vec3>(capturePointID, targetPos));
|
||||
}
|
||||
if(currentOwner != blueTeamEnum) {
|
||||
//This capturePoint is not owned by the blue team and is therefor an eligible target for blue team
|
||||
glm::vec3 targetPos = TransformSystem::AbsolutePosition(capturePointEntity);
|
||||
blueTargets.insert(std::pair<int, glm::vec3>(capturePointID, targetPos));
|
||||
}
|
||||
|
||||
if(homePointTeam == blueTeamEnum) {
|
||||
//CP is the home point for blue team.
|
||||
homeBlue = capturePointEntity;
|
||||
} else if (homePointTeam == redTeamEnum) {
|
||||
//CP is the home point for red team.
|
||||
homeRed = capturePointEntity;
|
||||
}
|
||||
}
|
||||
|
||||
if(!homeRed.Valid() || !homeBlue.Valid()) {
|
||||
//One or both teams have no home point, cant continue
|
||||
return;
|
||||
}
|
||||
|
||||
std::unordered_map<int, glm::vec3>::const_iterator got;
|
||||
//Find next target for red team.
|
||||
if((int)homeRed["CapturePoint"]["CapturePointNumber"] == lastCP) {
|
||||
//Red home base is the last capture point, count back from lastCP and find next target
|
||||
for (int i = lastCP; i >= firstCP; i--) {
|
||||
got = redTargets.find(i);
|
||||
if(got == redTargets.end()) {
|
||||
continue;
|
||||
} else {
|
||||
m_RedTeamCurrentTarget = got->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if ((int)homeRed["CapturePoint"]["CapturePointNumber"] == firstCP) {
|
||||
//Red home is the first capture point, count forward from firstCP and find next target.
|
||||
for (int i = firstCP; i <= lastCP; i++) {
|
||||
got = redTargets.find(i);
|
||||
if(got == redTargets.end()) {
|
||||
//Target was not found, try the next one after that.
|
||||
continue;
|
||||
} else {
|
||||
m_RedTeamCurrentTarget = got->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Find next target for blue team
|
||||
if ((int)homeBlue["CapturePoint"]["CapturePointNumber"] == lastCP) {
|
||||
//Red home base is the last capture point, count back from lastCP and find next target
|
||||
for (int i = lastCP; i >= firstCP; i--) {
|
||||
got = blueTargets.find(i);
|
||||
if (got == blueTargets.end()) {
|
||||
continue;
|
||||
} else {
|
||||
m_BlueTeamCurrentTarget = got->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if ((int)homeBlue["CapturePoint"]["CapturePointNumber"] == firstCP) {
|
||||
//Red home is the first capture point, count forward from firstCP and find next target.
|
||||
for (int i = firstCP; i <= lastCP; i++) {
|
||||
got = blueTargets.find(i);
|
||||
if (got == blueTargets.end()) {
|
||||
//Target was not found, try the next one after that.
|
||||
continue;
|
||||
} else {
|
||||
m_BlueTeamCurrentTarget = got->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//if red team, get red team next point, otherwise blue team next point.
|
||||
//Untill this is awailable we will just use the hardcoded value in the component.
|
||||
//This will also give us a position, so we wont need to loop through all capturePoints.
|
||||
glm::vec3 pos;
|
||||
if(currentTeam == redTeamEnum) {
|
||||
pos = m_RedTeamCurrentTarget;
|
||||
} else if (currentTeam == blueTeamEnum) {
|
||||
pos = m_BlueTeamCurrentTarget;
|
||||
}
|
||||
|
||||
Field<glm::vec3> arrowOri = arrowEntity["Transform"]["Orientation"];
|
||||
glm::vec3 lookVector = glm::normalize(TransformSystem::AbsolutePosition(arrowEntity) - pos); //Maybe should be player instead
|
||||
float pitch = std::asin(-lookVector.y);
|
||||
float yaw = std::atan2(lookVector.x, lookVector.z);
|
||||
arrowOri.x(pitch);
|
||||
arrowOri.y(yaw);
|
||||
arrowOri.z(0.f);
|
||||
EntityWrapper parent = arrowEntity.Parent();
|
||||
if (parent.Valid()) {
|
||||
arrowOri -= TransformSystem::AbsoluteOrientationEuler(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CapturePointArrowHUDSystem::OnCapturePointCaptured(Events::Captured& e)
|
||||
{
|
||||
if(!e.BlueTeamNextCapturePoint.Valid() || !e.RedTeamNextCapturePoint.Valid()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
m_RedTeamCurrentTarget = TransformSystem::AbsolutePosition(e.RedTeamNextCapturePoint);
|
||||
m_BlueTeamCurrentTarget = TransformSystem::AbsolutePosition(e.BlueTeamNextCapturePoint);
|
||||
|
||||
m_InitialtargetsSet = true;
|
||||
return 0;
|
||||
}
|
||||
@@ -9,7 +9,7 @@ CapturePointHUDSystem::CapturePointHUDSystem(SystemParams params)
|
||||
|
||||
void CapturePointHUDSystem::Update(double dt)
|
||||
{
|
||||
bool LoadCheck = true;
|
||||
bool loadCheck = true;
|
||||
int redTeam;
|
||||
int blueTeam;
|
||||
int spectatorTeam;
|
||||
@@ -35,11 +35,11 @@ void CapturePointHUDSystem::Update(double dt)
|
||||
//Check if the HUD corresponds to the Capture Point Number
|
||||
if (HUD_ID == (int)entityCP["CapturePoint"]["CapturePointNumber"]) {
|
||||
ComponentWrapper& teamComponent = entityCP["Team"];
|
||||
if (LoadCheck) {
|
||||
if (loadCheck) {
|
||||
redTeam = (int)teamComponent["Team"].Enum("Red");
|
||||
blueTeam = (int)teamComponent["Team"].Enum("Blue");
|
||||
spectatorTeam = (int)teamComponent["Team"].Enum("Spectator");
|
||||
LoadCheck = false;
|
||||
loadCheck = false;
|
||||
}
|
||||
//Color hud with team color
|
||||
auto capturePointTeam = (int)teamComponent["Team"];
|
||||
@@ -49,7 +49,8 @@ void CapturePointHUDSystem::Update(double dt)
|
||||
double currentCaptureTime = (double)entityCP["CapturePoint"]["CaptureTimer"];
|
||||
double progress = glm::abs(currentCaptureTime)/15.0;
|
||||
int currentCapturingTeam = currentCaptureTime > 0 ? redTeam : currentCaptureTime < 0 ? blueTeam : spectatorTeam;
|
||||
((glm::vec3&)entityHUD["Transform"]["Orientation"]).z = currentCapturingTeam == redTeam ? glm::half_pi<float>()+glm::pi<float>() : glm::half_pi<float>();
|
||||
Field<glm::vec3> orientation = entityHUD["Transform"]["Orientation"];
|
||||
orientation.z(currentCapturingTeam == redTeam ? glm::half_pi<float>()+glm::pi<float>() : glm::half_pi<float>());
|
||||
glm::vec4 fillColor = currentCapturingTeam == redTeam ? glm::vec4(1, 0.f, 0, 0.7f) : glm::vec4(0, 0.2f, 1, 0.7f);
|
||||
entityHUD["Fill"]["Color"] = fillColor;
|
||||
entityHUD["Fill"]["Percentage"] = progress;
|
||||
|
||||
@@ -10,8 +10,26 @@ CapturePointSystem::CapturePointSystem(SystemParams params)
|
||||
EVENT_SUBSCRIBE_MEMBER(m_ETriggerTouch, &CapturePointSystem::OnTriggerTouch);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_ETriggerLeave, &CapturePointSystem::OnTriggerLeave);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_ECaptured, &CapturePointSystem::OnCaptured);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EReset, &CapturePointSystem::OnReset);
|
||||
Init();
|
||||
}
|
||||
}
|
||||
|
||||
void CapturePointSystem::Init()
|
||||
{
|
||||
m_WinnerWasFound = false;
|
||||
//need to track these variables for the captureSystem to work as per design!
|
||||
m_RedTeamNextPossibleCapturePoint = m_NotACapturePoint;
|
||||
m_BlueTeamNextPossibleCapturePoint = m_NotACapturePoint;
|
||||
m_RedTeamHomeCapturePoint = m_NotACapturePoint;
|
||||
m_BlueTeamHomeCapturePoint = m_NotACapturePoint;
|
||||
m_NumberOfCapturePoints = 0;
|
||||
m_ResetTimers = false;
|
||||
m_RecentlyCapturedNeedNextCapturePointNow = false;
|
||||
m_CapturePointNumberToEntityMap.clear();
|
||||
//vectors which will keep track of enter/leave changes
|
||||
m_ETriggerTouchVector.clear();
|
||||
m_ETriggerLeaveVector.clear();
|
||||
}
|
||||
|
||||
//here all capturepoints will update their component
|
||||
@@ -24,6 +42,9 @@ void CapturePointSystem::UpdateComponent(EntityWrapper& capturePointEntity, Comp
|
||||
if (m_WinnerWasFound) {
|
||||
return;
|
||||
}
|
||||
if (!capturePointEntity.Valid()) {
|
||||
return;
|
||||
}
|
||||
const int capturePointNumber = cCapturePoint["CapturePointNumber"];
|
||||
const bool hasTeamComponent = capturePointEntity.HasComponent("Team");
|
||||
|
||||
@@ -60,7 +81,7 @@ void CapturePointSystem::UpdateComponent(EntityWrapper& capturePointEntity, Comp
|
||||
}
|
||||
|
||||
//if we havent received all capturepoints yet, just return
|
||||
if (m_NumberOfCapturePoints == 0 || m_NumberOfCapturePoints != m_CapturePointNumberToEntityMap.size()) {
|
||||
if (m_NumberOfCapturePoints == 0 || m_NumberOfCapturePoints > m_CapturePointNumberToEntityMap.size()) {
|
||||
m_CapturePointNumberToEntityMap.insert(std::make_pair(capturePointNumber, capturePointEntity));
|
||||
return;
|
||||
}
|
||||
@@ -69,6 +90,7 @@ void CapturePointSystem::UpdateComponent(EntityWrapper& capturePointEntity, Comp
|
||||
int ownedBy = teamComponent["Team"];
|
||||
int redTeamPlayersStandingInside = 0;
|
||||
int blueTeamPlayersStandingInside = 0;
|
||||
//note: old color system
|
||||
if (capturePointEntity.HasComponent("Model")) {
|
||||
//Now sets team color to the capturepoint, or white if it is uncaptured.
|
||||
capturePointEntity["Model"]["Color"] = ownedBy == blueTeam ? glm::vec4(0, 0.0f, 1, 0.3) : ownedBy == redTeam ? glm::vec4(1, 0.0f, 0, 0.3) : glm::vec4(1, 1, 1, 0.3);
|
||||
@@ -102,10 +124,20 @@ void CapturePointSystem::UpdateComponent(EntityWrapper& capturePointEntity, Comp
|
||||
nextPossibleCapturePoint["Blue"] = i - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_RecentlyCapturedNeedNextCapturePointNow) {
|
||||
m_CapturedEvent.NextCapturePoint = m_CapturedEvent.TeamNumberThatCapturedCapturePoint == blueTeam ?
|
||||
m_CapturePointNumberToEntityMap[nextPossibleCapturePoint["Blue"]] :
|
||||
m_CapturePointNumberToEntityMap[nextPossibleCapturePoint["Red"]];
|
||||
//change what model is displaying (change all in case 2 capturepoints has been captured on the same frame)
|
||||
for (int i = 0; i < m_NumberOfCapturePoints; i++) {
|
||||
auto owner = (int)m_CapturePointNumberToEntityMap[i]["Team"]["Team"];
|
||||
if (m_CapturePointNumberToEntityMap[i].FirstChildByName("Red").Valid()) {
|
||||
ChangeCapturePointModelsVisibility(m_CapturePointNumberToEntityMap[i].FirstChildByName("Red"), owner == redTeam);
|
||||
ChangeCapturePointModelsVisibility(m_CapturePointNumberToEntityMap[i].FirstChildByName("Blue"), owner == blueTeam);
|
||||
ChangeCapturePointModelsVisibility(m_CapturePointNumberToEntityMap[i].FirstChildByName("Spectator"), owner == spectatorTeam);
|
||||
}
|
||||
}
|
||||
//save the next cap points and publish the captured event
|
||||
m_CapturedEvent.BlueTeamNextCapturePoint = m_CapturePointNumberToEntityMap[nextPossibleCapturePoint["Blue"]];
|
||||
m_CapturedEvent.RedTeamNextCapturePoint = m_CapturePointNumberToEntityMap[nextPossibleCapturePoint["Red"]];
|
||||
m_EventBroker->Publish(m_CapturedEvent);
|
||||
m_RecentlyCapturedNeedNextCapturePointNow = false;
|
||||
}
|
||||
@@ -221,6 +253,19 @@ void CapturePointSystem::UpdateComponent(EntityWrapper& capturePointEntity, Comp
|
||||
|
||||
}
|
||||
|
||||
void CapturePointSystem::ChangeCapturePointModelsVisibility(EntityWrapper &capturePointModels, bool isOwner)
|
||||
{
|
||||
(Field<bool>)capturePointModels["Model"]["Visible"] = isOwner;
|
||||
for (auto& capModel : capturePointModels.ChildrenWithComponent("Transform")) {
|
||||
if (capModel.HasComponent("Model")) {
|
||||
(Field<bool>)capModel["Model"]["Visible"] = isOwner;
|
||||
}
|
||||
if (capModel.HasComponent("PointLight")) {
|
||||
(Field<bool>)capModel["PointLight"]["Visible"] = isOwner;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CapturePointSystem::OnTriggerTouch(const Events::TriggerTouch& e)
|
||||
{
|
||||
//personEntered = e.Entity, thingEntered = e.Trigger
|
||||
@@ -245,3 +290,9 @@ bool CapturePointSystem::OnCaptured(const Events::Captured& e)
|
||||
m_ResetTimers = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CapturePointSystem::OnReset(const Events::Reset& e)
|
||||
{
|
||||
Init();
|
||||
return true;
|
||||
}
|
||||
@@ -8,12 +8,13 @@ DamageIndicatorSystem::DamageIndicatorSystem(SystemParams params)
|
||||
EVENT_SUBSCRIBE_MEMBER(m_ESetCamera, &DamageIndicatorSystem::OnSetCamera);
|
||||
|
||||
//load texture to cache
|
||||
auto texture = CommonFunctions::LoadTexture("Textures/DamageIndicator.png", false);
|
||||
auto entityFile = ResourceManager::Load<EntityFile>("Schema/Entities/DamageIndicator.xml");
|
||||
auto texture = CommonFunctions::TryLoadResource<Texture, false>("Textures/DamageIndicator.png");
|
||||
auto entityFile = ResourceManager::Load<EntityXMLFile>("Schema/Entities/DamageIndicator.xml");
|
||||
m_NetworkEnabled = ResourceManager::Load<ConfigFile>("Config.ini")->Get("Networking.StartNetwork", false);
|
||||
}
|
||||
|
||||
void DamageIndicatorSystem::Update(double dt) {
|
||||
if (!IsServer && LocalPlayer.Valid()) {
|
||||
if ((!IsServer || !m_NetworkEnabled) && LocalPlayer.Valid()) {
|
||||
for (auto& iter = updateDamageIndicatorVector.begin(); iter != updateDamageIndicatorVector.end(); iter++) {
|
||||
if (!iter->spriteEntity.Valid()) {
|
||||
updateDamageIndicatorVector.erase(iter);
|
||||
@@ -40,25 +41,28 @@ bool DamageIndicatorSystem::OnPlayerDamage(Events::PlayerDamage& e)
|
||||
return false;
|
||||
}
|
||||
|
||||
//friendly fire - return
|
||||
if (e.Damage < 0.1f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
glm::vec3 inflictorPos = e.Inflictor["Transform"]["Position"];
|
||||
//if testing
|
||||
#ifdef INDICATOR_TEST
|
||||
inflictorPos = DamageIndicatorTest(e.Victim);
|
||||
inflictorPos = DamageIndicatorTest(e.Victim);
|
||||
#endif
|
||||
|
||||
float angleBetweenVectors = CalculateAngle(e.Victim, inflictorPos);
|
||||
|
||||
//load & set the "2d" sprite
|
||||
auto entityFile = ResourceManager::Load<EntityFile>("Schema/Entities/DamageIndicator.xml");
|
||||
EntityFileParser parser(entityFile);
|
||||
EntityID spriteID = parser.MergeEntities(m_World);
|
||||
m_World->SetParent(spriteID, m_CurrentCamera);
|
||||
auto spriteWrapper = EntityWrapper(m_World, spriteID);
|
||||
EntityWrapper sprite = entityFile->MergeInto(m_World);
|
||||
m_World->SetParent(sprite.ID, m_CurrentCamera);
|
||||
//simply set the rotation z-wise to the angleBetweenVectors
|
||||
spriteWrapper["Transform"]["Orientation"] = glm::vec3(0, 0, angleBetweenVectors);
|
||||
sprite["Transform"]["Orientation"] = glm::vec3(0, 0, angleBetweenVectors);
|
||||
|
||||
if (!IsServer) {
|
||||
updateDamageIndicatorVector.emplace_back(spriteWrapper, inflictorPos);
|
||||
if (!IsServer || !m_NetworkEnabled) {
|
||||
updateDamageIndicatorVector.emplace_back(sprite, inflictorPos);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -83,7 +87,7 @@ float DamageIndicatorSystem::CalculateAngle(EntityWrapper player, glm::vec3 enem
|
||||
auto enemyPlayerVector = glm::normalize(playerPosition - enemyPosition);
|
||||
|
||||
//get the rotationvector relative to the z-axis
|
||||
auto rotationVectorVec3 = glm::vec3(glm::toMat4(Transform::AbsoluteOrientation(player))*glm::vec4(0, 0, 1, 0));
|
||||
auto rotationVectorVec3 = glm::vec3(glm::toMat4(TransformSystem::AbsoluteOrientation(player))*glm::vec4(0, 0, 1, 0));
|
||||
//rotate the direction-vector 90 degrees to get the players side-vector
|
||||
auto playerSideVector = glm::vec3(glm::rotateY(rotationVectorVec3, 1.57f));
|
||||
|
||||
@@ -124,11 +128,11 @@ glm::vec3 DamageIndicatorSystem::DamageIndicatorTest(EntityWrapper player) {
|
||||
}
|
||||
m_TestVar++;
|
||||
|
||||
auto inflictorPos = glm::vec3(currentPos.x + testVar*6.0f, currentPos.y, currentPos.z + testVar2*6.0f);
|
||||
auto inflictorPos = glm::vec3(currentPos.x + testVar*6.0f, currentPos.y, currentPos.z + testVar2*6.0f);
|
||||
|
||||
//load the explosioneffect XML
|
||||
auto deathEffect = ResourceManager::Load<EntityFile>("Schema/Entities/PlayerDeathExplosionWithCamera.xml");
|
||||
EntityFileParser parser(deathEffect);
|
||||
auto deathEffect = ResourceManager::Load<EntityXMLFile>("Schema/Entities/PlayerDeathExplosionWithCamera.xml");
|
||||
EntityXMLFileParser parser(deathEffect);
|
||||
EntityID deathEffectID = parser.MergeEntities(m_World);
|
||||
EntityWrapper deathEffectEW = EntityWrapper(m_World, deathEffectID);
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
#include "Systems/EndScreenSystem.h"
|
||||
|
||||
|
||||
EndScreenSystem::EndScreenSystem(SystemParams params)
|
||||
: System(params)
|
||||
, ImpureSystem()
|
||||
{
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EWin, &EndScreenSystem::OnWin);
|
||||
}
|
||||
|
||||
void EndScreenSystem::Update(double dt)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool EndScreenSystem::OnWin(const Events::Win& e)
|
||||
{
|
||||
auto endScreenCameras = m_World->GetComponents("EndScreen");
|
||||
if(endScreenCameras == nullptr) {
|
||||
LOG_ERROR("Win event recieved but no Endscreen camera is present.");
|
||||
return 1;
|
||||
}
|
||||
for(auto& camera : *endScreenCameras) {
|
||||
EntityWrapper entity = EntityWrapper(m_World, camera.EntityID);
|
||||
|
||||
Events::SetCamera event;
|
||||
event.CameraEntity = entity;
|
||||
m_EventBroker->Publish(event);
|
||||
|
||||
EntityWrapper redSprite = entity.FirstChildByName("SpriteRed");
|
||||
EntityWrapper blueSprite = entity.FirstChildByName("SpriteBlue");
|
||||
EntityWrapper redText = entity.FirstChildByName("TextRed");
|
||||
EntityWrapper blueText = entity.FirstChildByName("TextBlue");
|
||||
|
||||
if (e.TeamThatWon == 2) {
|
||||
//Red team won
|
||||
if(redSprite.HasComponent("Sprite"))
|
||||
(Field<bool>)redSprite["Sprite"]["Visible"] = true;
|
||||
if (redText.HasComponent("Text"))
|
||||
(Field<bool>)redText["Text"]["Visible"] = true;
|
||||
|
||||
if (blueSprite.HasComponent("Sprite"))
|
||||
(Field<bool>)blueSprite["Sprite"]["Visible"] = false;
|
||||
if (blueText.HasComponent("Text"))
|
||||
(Field<bool>)blueText["Text"]["Visible"] = false;
|
||||
|
||||
}else if (e.TeamThatWon == 3) {
|
||||
//Blue team won
|
||||
if (redSprite.HasComponent("Sprite"))
|
||||
(Field<bool>)redSprite["Sprite"]["Visible"] = false;
|
||||
if (redText.HasComponent("Text"))
|
||||
(Field<bool>)redText["Text"]["Visible"] = false;
|
||||
|
||||
if (blueSprite.HasComponent("Sprite"))
|
||||
(Field<bool>)blueSprite["Sprite"]["Visible"] = true;
|
||||
if (blueText.HasComponent("Text"))
|
||||
(Field<bool>)blueText["Text"]["Visible"] = true;
|
||||
} else {
|
||||
//No team won?
|
||||
if (redSprite.HasComponent("Sprite"))
|
||||
(Field<bool>)redSprite["Sprite"]["Visible"] = false;
|
||||
if (blueSprite.HasComponent("Sprite"))
|
||||
(Field<bool>)blueSprite["Sprite"]["Visible"] = false;
|
||||
|
||||
if (redText.HasComponent("Text"))
|
||||
(Field<bool>)redText["Text"]["Visible"] = false;
|
||||
if (blueText.HasComponent("Text"))
|
||||
(Field<bool>)blueText["Text"]["Visible"] = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -2,13 +2,20 @@
|
||||
|
||||
void ExplosionEffectSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt)
|
||||
{
|
||||
if ((double)component["TimeSinceDeath"] > (double)component["ExplosionDuration"]) {
|
||||
(double)component["TimeSinceDeath"] = 0.f;
|
||||
Field<double> delay = component["Delay"];
|
||||
if (delay > 0) {
|
||||
delay = std::max(0.0, delay - dt);
|
||||
}
|
||||
(double&)component["TimeSinceDeath"] += dt;
|
||||
|
||||
//if ((bool)Component["Gravity"] == true) {
|
||||
// (bool)Component["ExponentialAccelaration"] = false;
|
||||
//}
|
||||
if (delay <= 0) {
|
||||
Field<double> timeSinceDeath = component["TimeSinceDeath"];
|
||||
timeSinceDeath += (Field<double>)component["Speed"] * dt;
|
||||
if (timeSinceDeath < 0) {
|
||||
timeSinceDeath = 0.0;
|
||||
}
|
||||
else if (timeSinceDeath >(const double&)component["ExplosionDuration"]) {
|
||||
timeSinceDeath = (const double&)component["ExplosionDuration"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
#include "Systems/FadeSystem.h"
|
||||
|
||||
void FadeSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cFade, double dt)
|
||||
{
|
||||
double dTime = dt;
|
||||
if (dTime == 0.0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entity.Valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((bool)cFade["Out"]) {
|
||||
dTime *= -1;
|
||||
}
|
||||
|
||||
if((bool)cFade["Reverse"]) {
|
||||
dTime *= 2;
|
||||
}
|
||||
|
||||
double fadeTime = cFade["FadeTime"];
|
||||
Field<double> currentTime = cFade["Time"];
|
||||
currentTime += dTime;
|
||||
|
||||
if(currentTime > fadeTime) {
|
||||
currentTime = 0.0;
|
||||
if ((bool)cFade["Reverse"]) {
|
||||
(Field<bool>)cFade["Out"] = !(bool)cFade["Out"];
|
||||
currentTime = fadeTime;
|
||||
}
|
||||
}
|
||||
if(currentTime < 0.0) {
|
||||
currentTime = fadeTime;
|
||||
if ((bool)cFade["Reverse"]) {
|
||||
(Field<bool>)cFade["Out"] = !(bool)cFade["Out"];
|
||||
currentTime = 0.0;
|
||||
}
|
||||
}
|
||||
double ratio = currentTime/fadeTime;
|
||||
|
||||
if(entity.HasComponent("Model")){
|
||||
((Field<glm::vec4>)entity["Model"]["Color"]).w(ratio);
|
||||
}
|
||||
if (entity.HasComponent("Sprite")) {
|
||||
((Field<glm::vec4>)entity["Sprite"]["Color"]).w(ratio);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -22,19 +22,23 @@ void HealthHUDSystem::Update(double dt)
|
||||
if (entityIDParent.HasComponent("Health")) {
|
||||
|
||||
if (entity.HasComponent("Text")) {
|
||||
double health = (const double&)entityIDParent["Health"]["Health"];
|
||||
double maxHealth = (const double&)entityIDParent["Health"]["MaxHealth"];
|
||||
std::string s = "";
|
||||
s = s + std::to_string((int)(double)entityIDParent["Health"]["Health"]);
|
||||
s = s + std::to_string((int)health);
|
||||
s = s + "/";
|
||||
s = s + std::to_string((int)(double)entityIDParent["Health"]["MaxHealth"]);
|
||||
float healthPercentage = (double)entityIDParent["Health"]["Health"]/(double)entityIDParent["Health"]["MaxHealth"];
|
||||
//(glm::vec4&)entity["Text"]["Color"] = glm::vec4(1.0 - healthPercentage, 0.f, healthPercentage, glm::vec4(entity["Text"]["Color"]).a);
|
||||
s = s + std::to_string((int)maxHealth);
|
||||
float healthPercentage = health/maxHealth;
|
||||
//(Field<glm::vec4>)entity["Text"]["Color"] = glm::vec4(1.0 - healthPercentage, 0.f, healthPercentage, glm::vec4(entity["Text"]["Color"]).a);
|
||||
entity["Text"]["Content"] = s;
|
||||
}
|
||||
|
||||
if(entity.HasComponent("Fill")) {
|
||||
float healthPercentage = (double)entityIDParent["Health"]["Health"]/(double)entityIDParent["Health"]["MaxHealth"];
|
||||
(glm::vec4&)entity["Fill"]["Color"] = glm::vec4(1.0 - healthPercentage, 0.f, healthPercentage, glm::vec4(entity["Fill"]["Color"]).a);
|
||||
(double&)entity["Fill"]["Percentage"] = healthPercentage;
|
||||
double health = (const double&)entityIDParent["Health"]["Health"];
|
||||
double maxHealth = (const double&)entityIDParent["Health"]["MaxHealth"];
|
||||
float healthPercentage = health/maxHealth;
|
||||
entity["Fill"]["Color"] = glm::vec4(1.0 - healthPercentage, 0.f, healthPercentage, glm::vec4(entity["Fill"]["Color"]).a);
|
||||
entity["Fill"]["Percentage"] = (double)healthPercentage;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,15 +12,7 @@ HealthSystem::HealthSystem(SystemParams params)
|
||||
}
|
||||
|
||||
void HealthSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cHealth, double dt)
|
||||
{
|
||||
double& health = cHealth["Health"];
|
||||
if (health <= 0.0) {
|
||||
Events::PlayerDeath ePlayerDeath;
|
||||
ePlayerDeath.Player = entity;
|
||||
m_EventBroker->Publish(ePlayerDeath);
|
||||
//Note: we will delete the entity in PlayerDeathSystem
|
||||
}
|
||||
}
|
||||
{ }
|
||||
|
||||
bool HealthSystem::OnPlayerDamaged(Events::PlayerDamage& e)
|
||||
{
|
||||
@@ -29,8 +21,22 @@ bool HealthSystem::OnPlayerDamaged(Events::PlayerDamage& e)
|
||||
}
|
||||
|
||||
ComponentWrapper cHealth = e.Victim["Health"];
|
||||
double& health = cHealth["Health"];
|
||||
health -= e.Damage;
|
||||
Field<double> health = cHealth["Health"];
|
||||
//if player has the boost from a defender, subtract the damage taken by StrengthOfEffect amount
|
||||
auto playerBoostDefenderEntity = e.Victim.FirstChildByName("BoostDefender");
|
||||
if (playerBoostDefenderEntity.Valid()) {
|
||||
e.Damage -= (double)playerBoostDefenderEntity["BoostDefender"]["StrengthOfEffect"];
|
||||
}
|
||||
if (health > 0) {
|
||||
health -= e.Damage;
|
||||
if (health <= 0.0) {
|
||||
Events::PlayerDeath ePlayerDeath;
|
||||
ePlayerDeath.Player = e.Victim;
|
||||
ePlayerDeath.Killer = e.Inflictor;
|
||||
m_EventBroker->Publish(ePlayerDeath);
|
||||
//Note: we will delete the entity in PlayerDeathSystem
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -50,9 +56,9 @@ bool HealthSystem::OnInputCommand(Events::InputCommand& e)
|
||||
bool HealthSystem::OnPlayerHealthPickup(Events::PlayerHealthPickup& e)
|
||||
{
|
||||
ComponentWrapper cHealth = e.Player["Health"];
|
||||
double& health = cHealth["Health"];
|
||||
Field<double> health = cHealth["Health"];
|
||||
health += e.HealthAmount;
|
||||
health = std::min(health, (double)cHealth["MaxHealth"]);
|
||||
health = std::min(*health, (const double&)cHealth["MaxHealth"]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ void InterpolationSystem::Update(double dt)
|
||||
continue;
|
||||
}
|
||||
auto& iPosition = kv.second;
|
||||
glm::vec3& position = iPosition.Component[iPosition.Field];
|
||||
Field<glm::tvec3<float, glm::highp>> position = iPosition.Component[iPosition.Field];
|
||||
|
||||
iPosition.Alpha += dt;
|
||||
float alpha = glm::min(iPosition.Alpha / m_SnapshotInterval, 1.0);
|
||||
@@ -31,7 +31,7 @@ void InterpolationSystem::Update(double dt)
|
||||
continue;
|
||||
}
|
||||
auto& iOrientation = kv.second;
|
||||
glm::vec3& orientation = iOrientation.Component[iOrientation.Field];
|
||||
Field<glm::vec3> orientation = iOrientation.Component[iOrientation.Field];
|
||||
|
||||
iOrientation.Alpha += dt / m_SnapshotInterval;
|
||||
iOrientation.Alpha = glm::min(iOrientation.Alpha, 1.0);
|
||||
@@ -45,7 +45,7 @@ void InterpolationSystem::Update(double dt)
|
||||
continue;
|
||||
}
|
||||
auto& iVelocity = kv.second;
|
||||
glm::vec3& position = iVelocity.Component[iVelocity.Field];
|
||||
Field<glm::vec3> position = iVelocity.Component[iVelocity.Field];
|
||||
|
||||
iVelocity.Alpha += dt;
|
||||
float alpha = glm::min(iVelocity.Alpha / m_SnapshotInterval, 1.0);
|
||||
@@ -72,8 +72,8 @@ bool InterpolationSystem::OnInterpolate(Events::Interpolate& e)
|
||||
Interpolation<glm::quat> iOrientation(
|
||||
cTransform,
|
||||
"Orientation",
|
||||
glm::quat((glm::vec3&)cTransform["Orientation"]),
|
||||
glm::quat((glm::vec3&)e.Component["Orientation"])
|
||||
glm::quat((Field<glm::vec3>)cTransform["Orientation"]),
|
||||
glm::quat((Field<glm::vec3>)e.Component["Orientation"])
|
||||
);
|
||||
m_InterpolateOrientation.erase(e.Entity);
|
||||
m_InterpolateOrientation.insert(std::make_pair(e.Entity, iOrientation));
|
||||
|
||||
@@ -7,19 +7,20 @@ void KillFeedSystem::Update(double dt)
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto it = m_DeathQueue.begin(); it != m_DeathQueue.end(); it++) {
|
||||
(*it).TimeToLive -= dt;
|
||||
}
|
||||
|
||||
for (auto& killFeedComponent : *killFeeds) {
|
||||
EntityWrapper entity = EntityWrapper(m_World, killFeedComponent.EntityID);
|
||||
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
EntityWrapper child = entity.FirstChildByName("KillFeed" + std::to_string(i));
|
||||
if (child.HasComponent("Text")) {
|
||||
(std::string&)child["Text"]["Content"] = "";
|
||||
(Field<std::string>)child["Text"]["Content"] = "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int feedIndex = 1;
|
||||
for (auto it = m_DeathQueue.begin(); it != m_DeathQueue.end(); ) {
|
||||
bool remove = false;
|
||||
@@ -27,14 +28,19 @@ void KillFeedSystem::Update(double dt)
|
||||
EntityWrapper child = entity.FirstChildByName("KillFeed" + std::to_string(feedIndex));
|
||||
|
||||
if (child.HasComponent("Text")) {
|
||||
(std::string&)child["Text"]["Content"] = (*it).Content;
|
||||
(glm::vec4&)child["Text"]["Color"] = (*it).Color;
|
||||
|
||||
(*it).TimeToLive -= dt;
|
||||
std::string str = "";
|
||||
//Add killer to the start of string.
|
||||
str = (*it).KillerColor + "\\" + std::to_string((*it).KillerClass) + "\\CFFFFFF" + " " + (*it).KillerName + " ";
|
||||
//Add Weapon to middle of string.
|
||||
str += "\\" + std::to_string((*it).KillerClass+3) + " ";
|
||||
//Add victim to the end of string
|
||||
str += (*it).VictimColor + "\\" + std::to_string((*it).VictimClass) + "\\CFFFFFF" + " " + (*it).VictimName;
|
||||
|
||||
(Field<std::string>)child["Text"]["Content"] = str;
|
||||
|
||||
if ((*it).TimeToLive <= 0.f) {
|
||||
(std::string&)child["Text"]["Content"] = "";
|
||||
(glm::vec4&)child["Text"]["Color"] = (*it).Color;
|
||||
(Field<std::string>)child["Text"]["Content"] = "";
|
||||
remove = true;
|
||||
}
|
||||
}
|
||||
@@ -52,29 +58,30 @@ void KillFeedSystem::Update(double dt)
|
||||
}
|
||||
}
|
||||
|
||||
bool KillFeedSystem::OnPlayerDeath(Events::PlayerDeath& e)
|
||||
bool KillFeedSystem::OnPlayerKillDeath(Events::KillDeath& e)
|
||||
{
|
||||
KillFeedInfo kfInfo;
|
||||
KillFeedInfo info;
|
||||
|
||||
if (e.Player.HasComponent("Team")) {
|
||||
int red = e.Player["Team"].Enum("Team", "Red");
|
||||
int blue = e.Player["Team"].Enum("Team", "Blue");
|
||||
|
||||
if ((int)e.Player["Team"]["Team"] == red) {
|
||||
kfInfo.Content = "Blue Player killed Red Player";
|
||||
kfInfo.Color = glm::vec4(0.f, 0.2f, 1.f, 0.8f);
|
||||
m_DeathQueue.push_back(kfInfo);
|
||||
} else if ((int)e.Player["Team"]["Team"] == blue) {
|
||||
kfInfo.Content = "Red Player killed blue Player";
|
||||
kfInfo.Color = glm::vec4(1.f, 0.f, 0.f, 0.8f);
|
||||
m_DeathQueue.push_back(kfInfo);
|
||||
}
|
||||
info.KillerName = e.KillerName;
|
||||
info.KillerID = e.Killer;
|
||||
info.KillerClass = e.KillerClass;
|
||||
info.KillerTeam = e.KillerTeam;
|
||||
if(info.KillerTeam == 2){
|
||||
info.KillerColor = m_BlueColor;
|
||||
} else if (info.KillerTeam == 3) {
|
||||
info.KillerColor = m_RedColor;
|
||||
}
|
||||
|
||||
|
||||
if(m_DeathQueue.size() > 3) {
|
||||
m_DeathQueue.pop_front();
|
||||
info.VictimName = e.CasualtyName;
|
||||
info.VictimID = e.Casualty;
|
||||
info.VictimClass = e.CasualtyClass;
|
||||
info.VictimTeam = e.CasualtyTeam;
|
||||
if (info.VictimTeam == 2) {
|
||||
info.VictimColor = m_BlueColor;
|
||||
} else if (info.VictimTeam == 3) {
|
||||
info.VictimColor = m_RedColor;
|
||||
}
|
||||
|
||||
return true;
|
||||
m_DeathQueue.push_back(info);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ void LifetimeSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cL
|
||||
return;
|
||||
}
|
||||
|
||||
double& lifetime = cLifetime["Lifetime"];
|
||||
Field<double> lifetime = cLifetime["Lifetime"];
|
||||
lifetime -= dt;
|
||||
|
||||
if (lifetime <= 0.0) {
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
#include "../Game/Systems/MainMenuSystem.h"
|
||||
|
||||
MainMenuSystem::MainMenuSystem(SystemParams params, IRenderer* renderer)
|
||||
: System(params)
|
||||
, ImpureSystem()
|
||||
, m_Renderer(renderer)
|
||||
{
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPressed, &MainMenuSystem::OnButtonPress);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EReleased, &MainMenuSystem::OnButtonRelease);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EClicked, &MainMenuSystem::OnButtonClick);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &MainMenuSystem::OnInputCommand);
|
||||
}
|
||||
|
||||
void MainMenuSystem::Update(double dt)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void MainMenuSystem::OpenSubMenu(const Events::InputCommand& e)
|
||||
{
|
||||
auto menus = m_World->GetComponents("Menu");
|
||||
if (menus == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_OpenSubMenu == EntityWrapper::Invalid) {
|
||||
//No submenu is open, open one.
|
||||
for (auto& menu : *menus) {
|
||||
EntityWrapper menuEntity = EntityWrapper(m_World, menu.EntityID);
|
||||
auto spawner = menuEntity.FirstChildByName(e.Command + "Spawner");
|
||||
if (!spawner.HasComponent("Spawner")) {
|
||||
return;
|
||||
}
|
||||
m_OpenSubMenu = SpawnerSystem::Spawn(spawner, spawner);
|
||||
if (e.Command == "Play") {
|
||||
Events::SearchForServers event;
|
||||
m_EventBroker->Publish(event);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (m_OpenSubMenu.Name().compare(e.Command) != 0) {
|
||||
//Menu is open, but not the right one, delete the old one and open a new one.
|
||||
m_World->DeleteEntity(m_OpenSubMenu.ID);
|
||||
m_OpenSubMenu = EntityWrapper::Invalid;
|
||||
m_World->DeleteEntity(m_DropDown.ID);
|
||||
m_DropDown = EntityWrapper::Invalid;
|
||||
|
||||
for (auto& menu : *menus) {
|
||||
EntityWrapper menuEntity = EntityWrapper(m_World, menu.EntityID);
|
||||
auto Spawner = menuEntity.FirstChildByName(e.Command + "Spawner");
|
||||
if (!Spawner.HasComponent("Spawner")) {
|
||||
return;
|
||||
}
|
||||
m_OpenSubMenu = SpawnerSystem::Spawn(Spawner, Spawner);
|
||||
if(e.Command == "Play") {
|
||||
Events::SearchForServers event;
|
||||
m_EventBroker->Publish(event);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
//wanted submenu is open, close it.
|
||||
m_World->DeleteEntity(m_OpenSubMenu.ID);
|
||||
m_OpenSubMenu = EntityWrapper::Invalid;
|
||||
m_World->DeleteEntity(m_DropDown.ID);
|
||||
m_DropDown = EntityWrapper::Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MainMenuSystem::OpenDropDown(const Events::InputCommand& e)
|
||||
{
|
||||
auto menus = m_World->GetComponents("Menu");
|
||||
if (menus == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_DropDown == EntityWrapper::Invalid) {
|
||||
//No submenu is open, open one.
|
||||
for (auto& menu : *menus) {
|
||||
EntityWrapper menuEntity = EntityWrapper(m_World, menu.EntityID);
|
||||
auto spawner = menuEntity.FirstChildByName(e.Command + "Spawner");
|
||||
if (!spawner.HasComponent("Spawner")) {
|
||||
return;
|
||||
}
|
||||
m_DropDown = SpawnerSystem::Spawn(spawner, spawner);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
m_World->DeleteEntity(m_DropDown.ID);
|
||||
m_DropDown = EntityWrapper::Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
bool MainMenuSystem::OnButtonClick(const Events::ButtonClicked& e)
|
||||
{
|
||||
EntityWrapper entity = e.Entity;
|
||||
if (entity.Name() == "ServerIdentityConnect") {
|
||||
EntityWrapper serverIdentityEntity = entity.FirstParentWithComponent("ServerIdentity");
|
||||
if(serverIdentityEntity.Valid()) {
|
||||
Events::ConnectRequest event;
|
||||
event.IP = (std::string)serverIdentityEntity["ServerIdentity"]["IP"];
|
||||
event.Port = (int)serverIdentityEntity["ServerIdentity"]["Port"];
|
||||
printf("\n ----Request Server Connect----\nIP: %s\nPort: %i\n ------------------------------", event.IP, event.Port);
|
||||
m_EventBroker->Publish(event);
|
||||
}
|
||||
} else if (entity.HasComponent("ConfigBtnResolution")) {
|
||||
|
||||
m_Renderer->SetResolution(Rectangle((int)entity["ConfigBtnResolution"]["Width"], (int)entity["ConfigBtnResolution"]["Height"]));
|
||||
m_World->DeleteEntity(m_DropDown.ID);
|
||||
m_DropDown = EntityWrapper::Invalid;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MainMenuSystem::OnButtonRelease(const Events::ButtonReleased& e)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MainMenuSystem::OnButtonPress(const Events::ButtonPressed& e)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MainMenuSystem::OnInputCommand(const Events::InputCommand& e)
|
||||
{
|
||||
if(e.Command == "Play" && e.Value == 1) {
|
||||
OpenSubMenu(e);
|
||||
} else if (e.Command == "RefreshServerList" && e.Value == 1){
|
||||
Events::SearchForServers event;
|
||||
m_EventBroker->Publish(event);
|
||||
} else if (e.Command == "Options" && e.Value == 1) {
|
||||
OpenSubMenu(e);
|
||||
} else if (e.Command == "Resolution" && e.Value == 1) {
|
||||
OpenDropDown(e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -5,66 +5,101 @@ PickupSpawnSystem::PickupSpawnSystem(SystemParams params)
|
||||
{
|
||||
if (IsServer) {
|
||||
EVENT_SUBSCRIBE_MEMBER(m_ETriggerTouch, &PickupSpawnSystem::OnTriggerTouch);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_ETriggerLeave, &PickupSpawnSystem::OnTriggerLeave);
|
||||
}
|
||||
}
|
||||
|
||||
void PickupSpawnSystem::Update(double dt)
|
||||
{
|
||||
if (IsServer) {
|
||||
for (auto it = m_ETriggerTouchVector.begin(); it != m_ETriggerTouchVector.end(); ++it) {
|
||||
auto& healthPickupPosition = *it;
|
||||
//set the double timer value (value 3)
|
||||
healthPickupPosition.DecreaseThisRespawnTimer -= dt;
|
||||
if (healthPickupPosition.DecreaseThisRespawnTimer < 0) {
|
||||
//spawn and delete the vector item
|
||||
auto it = m_ETriggerTouchVector.begin();
|
||||
while (it != m_ETriggerTouchVector.end()) {
|
||||
auto& somePickup = *it;
|
||||
somePickup.DecreaseThisRespawnTimer -= dt;
|
||||
if (somePickup.DecreaseThisRespawnTimer < 0.0) {
|
||||
//spawn the new healthPickup
|
||||
auto entityFile = ResourceManager::Load<EntityFile>("Schema/Entities/HealthPickup.xml");
|
||||
EntityFileParser parser(entityFile);
|
||||
EntityID healthPickupID = parser.MergeEntities(m_World);
|
||||
EntityWrapper healthPickup = entityFile->MergeInto(m_World);
|
||||
|
||||
//let the world know a pickup has spawned (graphics effects, etc)
|
||||
Events::PickupSpawned ePickupSpawned;
|
||||
ePickupSpawned.Pickup = EntityWrapper(m_World, healthPickupID);
|
||||
ePickupSpawned.Pickup = healthPickup;
|
||||
m_EventBroker->Publish(ePickupSpawned);
|
||||
|
||||
//set values from the old entity to the new entity
|
||||
auto& newHealthPickupEntity = EntityWrapper(m_World, healthPickupID);
|
||||
newHealthPickupEntity["Transform"]["Position"] = healthPickupPosition.Pos;
|
||||
newHealthPickupEntity["HealthPickup"]["HealthGain"] = healthPickupPosition.HealthGain;
|
||||
newHealthPickupEntity["HealthPickup"]["RespawnTimer"] = healthPickupPosition.RespawnTimer;
|
||||
m_World->SetParent(newHealthPickupEntity.ID, healthPickupPosition.parentID);
|
||||
//copy values from the old entity to the new entity
|
||||
auto& newHealthPickupEntity = healthPickup;
|
||||
newHealthPickupEntity["Transform"]["Position"] = somePickup.Pos;
|
||||
newHealthPickupEntity["HealthPickup"]["HealthGain"] = somePickup.HealthGain;
|
||||
newHealthPickupEntity["HealthPickup"]["RespawnTimer"] = somePickup.RespawnTimer;
|
||||
m_World->SetParent(newHealthPickupEntity.ID, somePickup.parentID);
|
||||
|
||||
//erase the current element (healthPickupPosition)
|
||||
m_ETriggerTouchVector.erase(it);
|
||||
//erase the current element (somePickup)
|
||||
it = m_ETriggerTouchVector.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
//still touching PickupAtMaximum?
|
||||
for (auto& it = m_PickupAtMaximum.begin(); it != m_PickupAtMaximum.end(); ++it) {
|
||||
if (!it->player.Valid()) {
|
||||
m_PickupAtMaximum.erase(it);
|
||||
break;
|
||||
}
|
||||
if ((const double&)it->player["Health"]["Health"] < (const double&)it->player["Health"]["MaxHealth"]) {
|
||||
DoPickup(it->player, it->trigger);
|
||||
m_PickupAtMaximum.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool PickupSpawnSystem::OnTriggerTouch(Events::TriggerTouch& e)
|
||||
{
|
||||
if (!e.Trigger.HasComponent("HealthPickup")) {
|
||||
return false;
|
||||
}
|
||||
double healthGiven = 0.01*(double)e.Trigger["HealthPickup"]["HealthGain"] * (double)e.Entity["Health"]["MaxHealth"];
|
||||
//cant pick up healthpacks if you are already at MaxHealth
|
||||
if ((double)e.Entity["Health"]["Health"] >= (double)e.Entity["Health"]["MaxHealth"]) {
|
||||
//if at maxhealth, save the trigger-touch to a vector since standing inside it will not re-trigger the trigger
|
||||
if ((const double&)e.Entity["Health"]["Health"] >= (const double&)e.Entity["Health"]["MaxHealth"]) {
|
||||
m_PickupAtMaximum.push_back({ e.Entity, e.Trigger });
|
||||
return false;
|
||||
}
|
||||
|
||||
//personEntered = e.Entity, thingEntered = e.Trigger
|
||||
DoPickup(e.Entity, e.Trigger);
|
||||
return true;
|
||||
}
|
||||
bool PickupSpawnSystem::OnTriggerLeave(Events::TriggerLeave& e)
|
||||
{
|
||||
if (!e.Trigger.HasComponent("HealthPickup")) {
|
||||
return false;
|
||||
}
|
||||
//triggerleave erases possible m_PickupAtMaximum
|
||||
for (auto& it = m_PickupAtMaximum.begin(); it != m_PickupAtMaximum.end(); ++it) {
|
||||
if (it->trigger.ID == e.Trigger.ID && it->player.ID == e.Entity.ID) {
|
||||
m_PickupAtMaximum.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void PickupSpawnSystem::DoPickup(EntityWrapper &player, EntityWrapper &trigger)
|
||||
{
|
||||
//trigger should be valid but if it isnt we just return (to avoid crash)
|
||||
if (!trigger.Valid()) {
|
||||
return;
|
||||
}
|
||||
double healthGiven = 0.01*(const double&)trigger["HealthPickup"]["HealthGain"] * (const double&)player["Health"]["MaxHealth"];
|
||||
|
||||
//only the server will increase the players hp and set it in the next delta
|
||||
Events::PlayerHealthPickup ePlayerHealthPickup;
|
||||
ePlayerHealthPickup.HealthAmount = healthGiven;
|
||||
ePlayerHealthPickup.Player = e.Entity;
|
||||
ePlayerHealthPickup.Player = player;
|
||||
m_EventBroker->Publish(ePlayerHealthPickup);
|
||||
|
||||
//copy position, healthgain, respawntimer (twice since one of the values will be counted down to 0, the other will be set in the new object)
|
||||
//we need to copy all values since each value can be different for each healthPickup
|
||||
m_ETriggerTouchVector.push_back({ (glm::vec3)e.Trigger["Transform"]["Position"], e.Trigger["HealthPickup"]["HealthGain"],
|
||||
e.Trigger["HealthPickup"]["RespawnTimer"], e.Trigger["HealthPickup"]["RespawnTimer"], m_World->GetParent(e.Trigger.ID) });
|
||||
m_ETriggerTouchVector.push_back({ (glm::vec3)trigger["Transform"]["Position"], trigger["HealthPickup"]["HealthGain"],
|
||||
trigger["HealthPickup"]["RespawnTimer"], trigger["HealthPickup"]["RespawnTimer"], m_World->GetParent(trigger.ID) });
|
||||
|
||||
//delete the healthpickup
|
||||
m_World->DeleteEntity(e.Trigger.ID);
|
||||
return true;
|
||||
m_World->DeleteEntity(trigger.ID);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#include "Systems/PlayerDeathSystem.h"
|
||||
#include "Core/ELockMouse.h"
|
||||
|
||||
PlayerDeathSystem::PlayerDeathSystem(SystemParams params)
|
||||
: System(params)
|
||||
{
|
||||
EVENT_SUBSCRIBE_MEMBER(m_OnPlayerDeath, &PlayerDeathSystem::OnPlayerDeath);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EEntityDeleted, &PlayerDeathSystem::OnEntityDeleted);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &PlayerDeathSystem::OnInputCommand);
|
||||
}
|
||||
|
||||
void PlayerDeathSystem::Update(double dt)
|
||||
@@ -27,17 +30,15 @@ bool PlayerDeathSystem::OnPlayerDeath(Events::PlayerDeath& e)
|
||||
void PlayerDeathSystem::createDeathEffect(EntityWrapper player)
|
||||
{
|
||||
//load the explosioneffect XML
|
||||
auto deathEffect = ResourceManager::Load<EntityFile>("Schema/Entities/PlayerDeathExplosionWithCamera.xml");
|
||||
EntityFileParser parser(deathEffect);
|
||||
EntityID deathEffectID = parser.MergeEntities(m_World);
|
||||
EntityWrapper deathEffectEW = EntityWrapper(m_World, deathEffectID);
|
||||
auto entityFile = ResourceManager::Load<EntityFile>("Schema/Entities/PlayerDeathExplosionWithCamera.xml");
|
||||
EntityWrapper deathEffectEW = entityFile->MergeInto(m_World);
|
||||
|
||||
//components that we need from player
|
||||
auto playerModel = player.FirstChildByName("PlayerModel");
|
||||
if (!playerModel.Valid()) {
|
||||
return;
|
||||
}
|
||||
if (!playerModel.HasComponent("Model") || !playerModel.HasComponent("Animation")) {
|
||||
if (player == LocalPlayer) {
|
||||
setSpectatorCamera();
|
||||
}
|
||||
return;
|
||||
}
|
||||
auto playerEntityModel = playerModel["Model"];
|
||||
@@ -46,10 +47,6 @@ void PlayerDeathSystem::createDeathEffect(EntityWrapper player)
|
||||
//copy the data from player to explosioneffectmodel
|
||||
playerEntityModel.Copy(deathEffectEW["Model"]);
|
||||
playerEntityAnimation.Copy(deathEffectEW["Animation"]);
|
||||
//freeze the animation
|
||||
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"];
|
||||
@@ -59,9 +56,49 @@ void PlayerDeathSystem::createDeathEffect(EntityWrapper player)
|
||||
|
||||
//camera (with lifetime) behind the player
|
||||
if (player == LocalPlayer) {
|
||||
m_LocalPlayerDeathEffect = deathEffectEW;
|
||||
auto cam = deathEffectEW.FirstChildByName("Camera");
|
||||
Events::SetCamera eSetCamera;
|
||||
eSetCamera.CameraEntity = cam;
|
||||
m_EventBroker->Publish(eSetCamera);
|
||||
}
|
||||
}
|
||||
|
||||
bool PlayerDeathSystem::OnEntityDeleted(Events::EntityDeleted& e)
|
||||
{
|
||||
// We only care about when the local players death effect is removed.
|
||||
if (m_LocalPlayerDeathEffect != e.DeletedEntity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the player hasn't spawned already, activate the spectator camera.
|
||||
if (!LocalPlayer.Valid()) {
|
||||
setSpectatorCamera();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PlayerDeathSystem::setSpectatorCamera()
|
||||
{
|
||||
// Look for the spectator camera entity in the level.
|
||||
EntityWrapper spectatorCam = m_World->GetFirstEntityByName("SpectatorCamera");
|
||||
if (!spectatorCam.HasComponent("Camera")) {
|
||||
return;
|
||||
}
|
||||
Events::SetCamera eSetCamera;
|
||||
eSetCamera.CameraEntity = spectatorCam;
|
||||
m_EventBroker->Publish(eSetCamera);
|
||||
Events::UnlockMouse unlock;
|
||||
m_EventBroker->Publish(unlock);
|
||||
}
|
||||
|
||||
bool PlayerDeathSystem::OnInputCommand(Events::InputCommand& e)
|
||||
{
|
||||
if (e.Value == 0 || e.Command != "SwapToTeamPick" && e.Command != "SwapToClassPick") {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure that we don't set spectator camera if the player deliberately changes to class/team pick.
|
||||
m_LocalPlayerDeathEffect = EntityWrapper::Invalid;
|
||||
return true;
|
||||
}
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
PlayerMovementSystem::PlayerMovementSystem(SystemParams params)
|
||||
: System(params)
|
||||
, m_SprintEffectTimer(0.f)
|
||||
{
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &PlayerMovementSystem::OnPlayerSpawned);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EDoubleJump, &PlayerMovementSystem::OnDoubleJump);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EDashAbility, &PlayerMovementSystem::OnDashAbility);
|
||||
}
|
||||
|
||||
PlayerMovementSystem::~PlayerMovementSystem()
|
||||
@@ -17,14 +19,51 @@ PlayerMovementSystem::~PlayerMovementSystem()
|
||||
void PlayerMovementSystem::Update(double dt)
|
||||
{
|
||||
updateMovementControllers(dt);
|
||||
if (IsServer) {
|
||||
for (auto& kv : m_PlayerInputControllers) {
|
||||
updateVelocity(kv.first, dt);
|
||||
}
|
||||
} else {
|
||||
if (LocalPlayer.Valid()) {
|
||||
// Only do physics calculations on client and only for themselves.
|
||||
if (IsClient) {
|
||||
if (LocalPlayer.Valid()){
|
||||
updateVelocity(LocalPlayer, dt);
|
||||
}
|
||||
|
||||
|
||||
m_SprintEffectTimer += dt;
|
||||
if (m_SprintEffectTimer < 0.016f) {
|
||||
return;
|
||||
}
|
||||
m_SprintEffectTimer = 0.f;
|
||||
auto pool = m_World->GetComponents("SprintAbility");
|
||||
if (pool == nullptr) {
|
||||
return;
|
||||
}
|
||||
for (auto cSprint : *pool) {
|
||||
if (cSprint.EntityID != LocalPlayer.ID && (bool)cSprint["Active"]) {
|
||||
// Spawn one afterimage for each player that sprints.
|
||||
EntityWrapper player(m_World, cSprint.EntityID);
|
||||
auto entityFile = ResourceManager::Load<EntityFile>("Schema/Entities/SprintEffect.xml");
|
||||
EntityWrapper sprintEffect = entityFile->MergeInto(m_World);
|
||||
auto playerModel = player.FirstChildByName("PlayerModel");
|
||||
if (!playerModel.Valid()) {
|
||||
continue;
|
||||
}
|
||||
if (!playerModel.HasComponent("Model")) {
|
||||
continue;
|
||||
}
|
||||
if (!playerModel.HasComponent("Animation")) {
|
||||
continue;
|
||||
}
|
||||
auto playerEntityModel = playerModel["Model"];
|
||||
auto playerEntityAnimation = playerModel["Animation"];
|
||||
playerEntityModel.Copy(sprintEffect["Model"]);
|
||||
playerEntityAnimation.Copy(sprintEffect["Animation"]);
|
||||
sprintEffect["ExplosionEffect"]["EndColor"] = (glm::vec4)playerEntityModel["Color"];
|
||||
((Field<glm::vec4>)sprintEffect["ExplosionEffect"]["EndColor"]).w(0.f);
|
||||
sprintEffect["Animation"]["Speed1"] = 0.0;
|
||||
sprintEffect["Animation"]["Speed2"] = 0.0;
|
||||
sprintEffect["Animation"]["Speed3"] = 0.0;
|
||||
sprintEffect["Transform"]["Position"] = (glm::vec3)player["Transform"]["Position"];
|
||||
sprintEffect["Transform"]["Orientation"] = (glm::vec3)player["Transform"]["Orientation"];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,33 +79,51 @@ void PlayerMovementSystem::updateMovementControllers(double dt)
|
||||
// Aim pitch
|
||||
EntityWrapper cameraEntity = player.FirstChildByName("Camera");
|
||||
if (cameraEntity.Valid()) {
|
||||
glm::vec3& cameraOrientation = cameraEntity["Transform"]["Orientation"];
|
||||
cameraOrientation.x += controller->Rotation().x;
|
||||
Field<glm::vec3> cameraOrientation = cameraEntity["Transform"]["Orientation"];
|
||||
cameraOrientation.x(cameraOrientation.x() + controller->Rotation().x);
|
||||
// Limit camera pitch so we don't break our necks
|
||||
cameraOrientation.x = glm::clamp(cameraOrientation.x, -glm::half_pi<float>(), glm::half_pi<float>());
|
||||
cameraOrientation.x(glm::clamp(cameraOrientation.x(), -glm::half_pi<float>(), glm::half_pi<float>()));
|
||||
|
||||
float pitch = cameraOrientation.x();
|
||||
double time = ((pitch + glm::half_pi<float>()) / glm::pi<float>());
|
||||
|
||||
// Set third person model aim pitch
|
||||
EntityWrapper playerModel = player.FirstChildByName("PlayerModel");
|
||||
if (playerModel.Valid()) {
|
||||
ComponentWrapper cAnimationOffset = playerModel["AnimationOffset"];
|
||||
float pitch = cameraOrientation.x + 0.2f;
|
||||
double time = (pitch + glm::half_pi<float>()) / glm::pi<float>();
|
||||
cAnimationOffset["Time"] = time;
|
||||
setAim(playerModel, "SidearmWeapon", time);
|
||||
setAim(playerModel, "AssaultWeapon", time);
|
||||
setAim(playerModel, "DefenderWeapon", time);
|
||||
}
|
||||
}
|
||||
|
||||
ComponentWrapper& cTransform = player["Transform"];
|
||||
glm::vec3& ori = cTransform["Orientation"];
|
||||
ori.y += controller->Rotation().y;
|
||||
Field<glm::vec3> ori = cTransform["Orientation"];
|
||||
ori.y(ori.y() + controller->Rotation().y);
|
||||
|
||||
float playerMovementSpeed = player["Player"]["MovementSpeed"];
|
||||
float playerCrouchSpeed = player["Player"]["CrouchSpeed"];
|
||||
glm::vec3& wishDirection = player["Player"]["CurrentWishDirection"];
|
||||
Field<glm::vec3> wishDirection = player["Player"]["CurrentWishDirection"];
|
||||
auto playerBoostAssaultEntity = player.FirstChildByName("BoostAssault");
|
||||
if (playerBoostAssaultEntity.Valid()) {
|
||||
playerMovementSpeed *= (double)playerBoostAssaultEntity["BoostAssault"]["StrengthOfEffect"];
|
||||
playerCrouchSpeed *= (double)playerBoostAssaultEntity["BoostAssault"]["StrengthOfEffect"];
|
||||
}
|
||||
bool sniperSprinting = false;
|
||||
if (player.HasComponent("SprintAbility")) {
|
||||
(bool)player["SprintAbility"]["Active"] = controller->SpecialAbilityKeyDown();
|
||||
if (controller->SpecialAbilityKeyDown()) {
|
||||
playerMovementSpeed *= (double)player["SprintAbility"]["StrengthOfEffect"];
|
||||
playerCrouchSpeed *= (double)player["SprintAbility"]["StrengthOfEffect"];
|
||||
sniperSprinting = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (player.HasComponent("Physics")) {
|
||||
ComponentWrapper cPhysics = player["Physics"];
|
||||
//Assault Dash Check
|
||||
if (player.HasComponent("DashAbility")) {
|
||||
controller->AssaultDashCheck(dt, ((glm::vec3)cPhysics["Velocity"]).y != 0.0f, player["DashAbility"]["CoolDownMaxTimer"], player["DashAbility"]["CoolDownTimer"]);
|
||||
Field<double> coolDownTimer = player["DashAbility"]["CoolDownTimer"];
|
||||
controller->AssaultDashCheck(dt, ((glm::vec3)cPhysics["Velocity"]).y != 0.0f, player["DashAbility"]["CoolDownMaxTimer"], coolDownTimer, player.ID);
|
||||
}
|
||||
wishDirection = controller->Movement() * glm::inverse(glm::quat(ori));
|
||||
//this makes sure you can only dash in the 4 directions: forw,backw,left,right
|
||||
@@ -80,21 +137,21 @@ void PlayerMovementSystem::updateMovementControllers(double dt)
|
||||
wishSpeed = playerMovementSpeed;
|
||||
}
|
||||
if (player.ID == m_LocalPlayer.ID) {
|
||||
if (glm::length(wishDirection) == 0) {
|
||||
if (glm::length((glm::vec3)wishDirection) == 0) {
|
||||
// If no key is pressed, reset the distance moved since last step.
|
||||
m_DistanceMoved = 0;
|
||||
}
|
||||
}
|
||||
glm::vec3& velocity = cPhysics["Velocity"];
|
||||
Field<glm::vec3> velocity = cPhysics["Velocity"];
|
||||
bool isOnGround = (bool)cPhysics["IsOnGround"];
|
||||
//ImGui::Text(isOnGround ? "On ground" : "In air");
|
||||
//ImGui::Text("velocity: (%f, %f, %f) |%f|", velocity.x, velocity.y, velocity.z, glm::length(velocity));
|
||||
glm::vec3 groundVelocity(0.f, 0.f, 0.f);
|
||||
groundVelocity.x = velocity.x;
|
||||
groundVelocity.z = velocity.z;
|
||||
groundVelocity.x = velocity.x();
|
||||
groundVelocity.z = velocity.z();
|
||||
//ImGui::Text("groundVelocity: (%f, %f, %f) |%f|", groundVelocity.x, groundVelocity.y, groundVelocity.z, glm::length(groundVelocity));
|
||||
//ImGui::Text("wishDirection: (%f, %f, %f) |%f|", wishDirection.x, wishDirection.y, wishDirection.z, glm::length(wishDirection));
|
||||
float currentSpeedProj = glm::dot(groundVelocity, wishDirection);
|
||||
float currentSpeedProj = glm::dot(groundVelocity, (glm::vec3)wishDirection);
|
||||
float addSpeed = wishSpeed - currentSpeedProj;
|
||||
//ImGui::Text("currentSpeedProj: %f", currentSpeedProj);
|
||||
//ImGui::Text("wishSpeed: %f", wishSpeed);
|
||||
@@ -112,8 +169,15 @@ void PlayerMovementSystem::updateMovementControllers(double dt)
|
||||
//if doubleTapped do Assault Dash - but only boost maximum 50.0f
|
||||
float doubleTapDashBoost = controller->AssaultDashDoubleTapped() ? 40.0f : 1.0f;
|
||||
accelerationSpeed = glm::min(doubleTapDashBoost*glm::min(accelerationSpeed, addSpeed), 50.0f);
|
||||
velocity += accelerationSpeed * wishDirection;
|
||||
ImGui::Text("velocity: (%f, %f, %f) |%f|", velocity.x, velocity.y, velocity.z, glm::length(velocity));
|
||||
//if player has Boost from an Assault class, accelerate the player faster
|
||||
if (playerBoostAssaultEntity.Valid()) {
|
||||
accelerationSpeed *= (double)playerBoostAssaultEntity["BoostAssault"]["StrengthOfEffect"];
|
||||
}
|
||||
if (sniperSprinting) {
|
||||
accelerationSpeed *= (double)player["SprintAbility"]["StrengthOfEffect"];
|
||||
}
|
||||
velocity += accelerationSpeed * (glm::vec3)wishDirection;
|
||||
ImGui::Text("velocity: (%f, %f, %f) |%f|", velocity.x(), velocity.y(), velocity.z(), glm::length((glm::vec3)velocity));
|
||||
}
|
||||
|
||||
if (isOnGround) {
|
||||
@@ -123,11 +187,65 @@ void PlayerMovementSystem::updateMovementControllers(double dt)
|
||||
if (controller->Jumping() && !controller->Crouching()) {
|
||||
if (isOnGround) {
|
||||
(bool)cPhysics["IsOnGround"] = false;
|
||||
velocity.y = player["Player"]["JumpSpeed"];
|
||||
velocity.y(player["Player"]["JumpSpeed"]);
|
||||
|
||||
|
||||
if (player.Valid()) {
|
||||
EntityWrapper playerModel = player.FirstChildByName("PlayerModel");
|
||||
if (playerModel.Valid()) {
|
||||
{
|
||||
Events::AutoAnimationBlend aeb;
|
||||
aeb.Duration = 0.15;
|
||||
aeb.NodeName = "Jump";
|
||||
aeb.RootNode = playerModel;
|
||||
aeb.Start = true;
|
||||
aeb.Restart = true;
|
||||
m_EventBroker->Publish(aeb);
|
||||
}
|
||||
{
|
||||
Events::AutoAnimationBlend aeb;
|
||||
aeb.Duration = 0.25;
|
||||
aeb.NodeName = "MovementBlend";
|
||||
aeb.RootNode = playerModel;
|
||||
aeb.Start = true;
|
||||
aeb.Restart = false;
|
||||
aeb.AnimationEntity = playerModel.FirstChildByName("Jump");
|
||||
m_EventBroker->Publish(aeb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} else if (player.HasComponent("DoubleJump") && !controller->DoubleJumping()) {
|
||||
//Enter here if player can double jump and is doing so.
|
||||
(bool)cPhysics["IsOnGround"] = false;
|
||||
velocity.y = player["DoubleJump"]["DoubleJumpSpeed"];
|
||||
velocity.y(player["DoubleJump"]["DoubleJumpSpeed"]);
|
||||
|
||||
if (player.Valid()) {
|
||||
EntityWrapper playerModel = player.FirstChildByName("PlayerModel");
|
||||
if (playerModel.Valid()) {
|
||||
{
|
||||
Events::AutoAnimationBlend aeb;
|
||||
aeb.Duration = 0.15;
|
||||
aeb.NodeName = "Jump";
|
||||
aeb.RootNode = playerModel;
|
||||
aeb.Start = true;
|
||||
aeb.Restart = true;
|
||||
m_EventBroker->Publish(aeb);
|
||||
}
|
||||
{
|
||||
Events::AutoAnimationBlend aeb;
|
||||
aeb.Duration = 0.25;
|
||||
aeb.NodeName = "MovementBlend";
|
||||
aeb.RootNode = playerModel;
|
||||
aeb.Start = true;
|
||||
aeb.Restart = false;
|
||||
aeb.AnimationEntity = playerModel.FirstChildByName("BlendTreeLower").FirstChildByName("Jump");
|
||||
m_EventBroker->Publish(aeb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If IsServer and network is off this will not work
|
||||
if (IsClient) {
|
||||
//put a hexagon at the players feet
|
||||
@@ -142,94 +260,25 @@ void PlayerMovementSystem::updateMovementControllers(double dt)
|
||||
}
|
||||
|
||||
if (player.HasComponent("AABB")) {
|
||||
glm::vec3& size = player["AABB"]["Size"];
|
||||
Field<glm::vec3> size = player["AABB"]["Size"];
|
||||
if (controller->Crouching()) {
|
||||
size = glm::vec3(1.f, 1.f, 1.f);
|
||||
} else {
|
||||
size = glm::vec3(1.f, 1.6f, 1.f);
|
||||
}
|
||||
}
|
||||
|
||||
// Animations
|
||||
EntityWrapper playerModel = player.FirstChildByName("PlayerModel");
|
||||
if (playerModel.Valid()) {
|
||||
ComponentWrapper cAnimation = playerModel["Animation"];
|
||||
std::string& animationName1 = cAnimation["AnimationName1"];
|
||||
std::string& animationName2 = cAnimation["AnimationName2"];
|
||||
double& animationTime1 = cAnimation["Time1"];
|
||||
double& animationTime2 = cAnimation["Time2"];
|
||||
double& animationSpeed1 = cAnimation["Speed1"];
|
||||
double& animationSpeed2 = cAnimation["Speed2"];
|
||||
double& animationWeight1 = cAnimation["Weight1"];
|
||||
double& animationWeight2 = cAnimation["Weight2"];
|
||||
|
||||
float movementLength = glm::length(groundVelocity);
|
||||
//TODO: add assault dash animation here
|
||||
if (glm::length(controller->Movement()) > 0.f) {
|
||||
double forwardMovement = controller->Movement().z;
|
||||
double strafeMovement = controller->Movement().x;
|
||||
|
||||
if (controller->Crouching() && animationName1 != "CrouchWalk") {
|
||||
animationName1 = "CrouchWalk";
|
||||
animationSpeed1 = 1.0 * -glm::sign(controller->Movement().z);
|
||||
} else {
|
||||
if (glm::abs(forwardMovement) > 0) {
|
||||
if (animationName1 != "Run") {
|
||||
animationName1 = "Run";
|
||||
if (animationName2 == "StrafeLeft" || animationName2 == "StrafeRight") {
|
||||
animationTime1 = animationTime2;
|
||||
} else {
|
||||
animationTime1 = 0.0;
|
||||
}
|
||||
}
|
||||
animationSpeed1 = 2.f * -glm::sign(forwardMovement);
|
||||
}
|
||||
|
||||
if (glm::abs(strafeMovement) > 0) {
|
||||
if (animationName2 != "StrafeLeft" && animationName2 != "StrafeRight") {
|
||||
if (strafeMovement < 0) {
|
||||
animationName2 = "StrafeLeft";
|
||||
}
|
||||
if (strafeMovement > 0) {
|
||||
animationName2 = "StrafeRight";
|
||||
}
|
||||
if (animationName1 == "Run") {
|
||||
animationTime2 = animationTime1;
|
||||
} else {
|
||||
animationTime2 = 0.0;
|
||||
}
|
||||
}
|
||||
animationSpeed2 = 2.f * glm::abs(strafeMovement);
|
||||
}
|
||||
|
||||
double strafeWeight = glm::abs(strafeMovement) / (glm::abs(forwardMovement) + glm::abs(strafeMovement));
|
||||
animationWeight2 = strafeWeight;
|
||||
animationWeight1 = 1.0 - strafeWeight;
|
||||
}
|
||||
} else {
|
||||
if (controller->Crouching()) {
|
||||
animationName1 = "Crouch";
|
||||
animationName2 = "";
|
||||
animationSpeed1 = 1.0;
|
||||
animationSpeed2 = 0.0;
|
||||
animationWeight1 = 1.0;
|
||||
animationWeight2 = 0.0;
|
||||
} else {
|
||||
animationName1 = "Idle";
|
||||
animationName2 = "";
|
||||
animationSpeed1 = 1.f;
|
||||
animationSpeed2 = 0.0;
|
||||
animationWeight1 = 1.0;
|
||||
animationWeight2 = 0.0;
|
||||
//cAnimation["AnimationName2"] = "Idle";
|
||||
if (controller->CrouchingLastFrame() && isOnGround) {
|
||||
// The collision should resolve this anyway, but
|
||||
// this is more reliable, since the box gets larger.
|
||||
Field<glm::vec3> pos = cTransform["Position"];
|
||||
pos.y(pos.y() + 0.3f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Animations
|
||||
}
|
||||
|
||||
playerStep(dt, player);
|
||||
controller->Reset();
|
||||
}
|
||||
playerStep(dt);
|
||||
}
|
||||
|
||||
|
||||
@@ -238,11 +287,11 @@ void PlayerMovementSystem::updateVelocity(EntityWrapper player, double dt)
|
||||
// Only apply velocity to local player
|
||||
ComponentWrapper& cTransform = player["Transform"];
|
||||
ComponentWrapper& cPhysics = player["Physics"];
|
||||
glm::vec3& velocity = cPhysics["Velocity"];
|
||||
Field<glm::vec3> velocity = cPhysics["Velocity"];
|
||||
bool isOnGround = (bool)cPhysics["IsOnGround"];
|
||||
|
||||
// Ground friction
|
||||
float speed = glm::length(velocity);
|
||||
float speed = glm::length((glm::vec3)velocity);
|
||||
static float groundFriction = 7.f;
|
||||
ImGui::InputFloat("groundFriction", &groundFriction);
|
||||
static float airFriction = 2.f;
|
||||
@@ -252,25 +301,44 @@ void PlayerMovementSystem::updateVelocity(EntityWrapper player, double dt)
|
||||
if (speed > 0) {
|
||||
float drop = speed * friction * (float)dt;
|
||||
float multiplier = glm::max(speed - drop, 0.f) / speed;
|
||||
velocity.x *= multiplier;
|
||||
velocity.z *= multiplier;
|
||||
velocity.x(velocity.x() * multiplier);
|
||||
velocity.z(velocity.z() * multiplier);
|
||||
}
|
||||
|
||||
// Gravity
|
||||
if (cPhysics["Gravity"]) {
|
||||
velocity.y -= 9.82f * (float)dt;
|
||||
velocity.y(velocity.y() - (9.82f * (float)dt));
|
||||
}
|
||||
|
||||
glm::vec3& position = cTransform["Position"];
|
||||
Field<glm::vec3> position = cTransform["Position"];
|
||||
position += velocity * (float)dt;
|
||||
}
|
||||
|
||||
void PlayerMovementSystem::playerStep(double dt)
|
||||
|
||||
void PlayerMovementSystem::setAim(EntityWrapper root, std::string weaponNodeName, double time)
|
||||
{
|
||||
if (!m_LocalPlayer.Valid()) {
|
||||
if (root.Valid()) {
|
||||
EntityWrapper blendTreeUpper = root.FirstChildByName("BlendTreeUpper");
|
||||
if (blendTreeUpper.Valid()) {
|
||||
EntityWrapper weapon = blendTreeUpper.FirstChildByName(weaponNodeName);
|
||||
if(weapon.Valid()) {
|
||||
EntityWrapper aim = weapon.FirstChildByName("Aim");
|
||||
if (aim.Valid()) {
|
||||
if (aim.HasComponent("Animation")) {
|
||||
(Field<double>)aim["Animation"]["Time"] = time;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerMovementSystem::playerStep(double dt, EntityWrapper player)
|
||||
{
|
||||
// Position of the local player, used see how far a player has moved.
|
||||
if(!IsClient) {
|
||||
return;
|
||||
}
|
||||
// Position of the local player, used see how far a player has moved.
|
||||
glm::vec3 pos = (glm::vec3)m_World->GetComponent(m_LocalPlayer.ID, "Transform")["Position"];
|
||||
// Used to see if a player is airborne.
|
||||
bool grounded = (bool)m_World->GetComponent(m_LocalPlayer.ID, "Physics")["IsOnGround"];
|
||||
@@ -281,8 +349,8 @@ void PlayerMovementSystem::playerStep(double dt)
|
||||
// Player moved a step's distance
|
||||
// Create footstep sound
|
||||
Events::PlaySoundOnEntity e;
|
||||
e.EmitterID = m_LocalPlayer.ID;
|
||||
e.FilePath = m_LeftFoot ? "Audio/footstep/footstep2.wav" : "Audio/footstep/footstep3.wav";
|
||||
e.Emitter = m_LocalPlayer;
|
||||
e.FilePath = m_LeftFoot ? "Audio/Footstep/Footstep2.wav" : "Audio/Footstep/Footstep3.wav";
|
||||
m_LeftFoot = !m_LeftFoot;
|
||||
m_EventBroker->Publish(e);
|
||||
m_DistanceMoved = 0.f;
|
||||
@@ -292,7 +360,7 @@ void PlayerMovementSystem::playerStep(double dt)
|
||||
bool PlayerMovementSystem::OnPlayerSpawned(Events::PlayerSpawned& e)
|
||||
{
|
||||
// When a player spawns, create an input controller for them
|
||||
m_PlayerInputControllers[e.Player] = new FirstPersonInputController<PlayerMovementSystem>(m_EventBroker, e.PlayerID);
|
||||
m_PlayerInputControllers[e.Player] = new FirstPersonInputController<PlayerMovementSystem>(m_EventBroker, e.PlayerID, e.Player);
|
||||
if (e.PlayerID == -1) {
|
||||
// Keep track of the local player
|
||||
m_LocalPlayer = e.Player;
|
||||
@@ -303,11 +371,11 @@ bool PlayerMovementSystem::OnPlayerSpawned(Events::PlayerSpawned& e)
|
||||
bool PlayerMovementSystem::OnDoubleJump(Events::DoubleJump & e)
|
||||
{
|
||||
// If entity does not exist, exit
|
||||
if (!EntityWrapper(m_World, e.entityID).Valid()) {
|
||||
if (!EntityWrapper(m_World, e.entityID).Valid()) {
|
||||
return false;
|
||||
}
|
||||
// If entity IsLocalPlayer, exit
|
||||
if (e.entityID == m_LocalPlayer.ID) {
|
||||
if (e.entityID == m_LocalPlayer.ID) {
|
||||
return false;
|
||||
}
|
||||
spawnHexagon(EntityWrapper(m_World, e.entityID));
|
||||
@@ -315,11 +383,150 @@ bool PlayerMovementSystem::OnDoubleJump(Events::DoubleJump & e)
|
||||
}
|
||||
|
||||
void PlayerMovementSystem::spawnHexagon(EntityWrapper target)
|
||||
{
|
||||
{
|
||||
//put a hexagon at the entitys... feet?
|
||||
auto hexagonEffect = ResourceManager::Load<EntityFile>("Schema/Entities/DoubleJumpHexagon.xml");
|
||||
EntityFileParser parser(hexagonEffect);
|
||||
EntityID hexagonEffectID = parser.MergeEntities(m_World);
|
||||
EntityWrapper hexagonEW = EntityWrapper(m_World, hexagonEffectID);
|
||||
auto entityFile = ResourceManager::Load<EntityFile>("Schema/Entities/DoubleJumpHexagon.xml");
|
||||
EntityWrapper hexagonEW = entityFile->MergeInto(m_World);
|
||||
hexagonEW["Transform"]["Position"] = (glm::vec3)target["Transform"]["Position"];
|
||||
}
|
||||
}
|
||||
|
||||
bool PlayerMovementSystem::OnDashAbility(Events::DashAbility & e)
|
||||
{
|
||||
EntityWrapper eventPlayer(m_World, e.Player);
|
||||
if (!eventPlayer.Valid()){// || IsServer || eventPlayer.ID == LocalPlayer.ID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// auto entityFile = ResourceManager::Load<EntityFile>("Schema/Entities/DashEffect.xml");
|
||||
// EntityWrapper dashEffect = entityFile->MergeInto(m_World);
|
||||
EntityWrapper playerModel = eventPlayer.FirstChildByName("PlayerModel");
|
||||
|
||||
for (auto& kv : m_PlayerInputControllers) {
|
||||
EntityWrapper player = kv.first;
|
||||
if(player != eventPlayer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
auto& controller = kv.second;
|
||||
|
||||
if (!player.Valid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (player.Valid()) {
|
||||
EntityWrapper playerModel = player.FirstChildByName("PlayerModel");
|
||||
if (playerModel.Valid()) {
|
||||
if (glm::abs(controller->Movement().x) > glm::abs(controller->Movement().z)) {
|
||||
if (controller->Movement().x > 0) {
|
||||
{
|
||||
Events::AutoAnimationBlend aeb;
|
||||
aeb.Duration = 0.1;
|
||||
aeb.NodeName = "DashRight";
|
||||
aeb.RootNode = playerModel;
|
||||
aeb.Restart = true;
|
||||
aeb.Start = true;
|
||||
m_EventBroker->Publish(aeb);
|
||||
}
|
||||
{
|
||||
Events::AutoAnimationBlend aeb;
|
||||
aeb.Duration = 0.2;
|
||||
aeb.NodeName = "MovementBlend";
|
||||
aeb.RootNode = playerModel;
|
||||
aeb.Start = true;
|
||||
aeb.Restart = false;
|
||||
aeb.AnimationEntity = playerModel.FirstChildByName("DashRight");
|
||||
m_EventBroker->Publish(aeb);
|
||||
}
|
||||
} else {
|
||||
{
|
||||
Events::AutoAnimationBlend aeb;
|
||||
aeb.Duration = 0.1;
|
||||
aeb.NodeName = "DashLeft";
|
||||
aeb.RootNode = playerModel;
|
||||
aeb.Restart = true;
|
||||
aeb.Start = true;
|
||||
m_EventBroker->Publish(aeb);
|
||||
}
|
||||
{
|
||||
Events::AutoAnimationBlend aeb;
|
||||
aeb.Duration = 0.2;
|
||||
aeb.NodeName = "MovementBlend";
|
||||
aeb.RootNode = playerModel;
|
||||
aeb.Start = true;
|
||||
aeb.Restart = false;
|
||||
aeb.AnimationEntity = playerModel.FirstChildByName("DashLeft");
|
||||
m_EventBroker->Publish(aeb);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (controller->Movement().z < 0) {
|
||||
{
|
||||
Events::AutoAnimationBlend aeb;
|
||||
aeb.Duration = 0.1;
|
||||
aeb.NodeName = "DashForward";
|
||||
aeb.RootNode = playerModel;
|
||||
aeb.Restart = true;
|
||||
aeb.Start = true;
|
||||
m_EventBroker->Publish(aeb);
|
||||
}
|
||||
{
|
||||
Events::AutoAnimationBlend aeb;
|
||||
aeb.Duration = 0.2;
|
||||
aeb.NodeName = "MovementBlend";
|
||||
aeb.RootNode = playerModel;
|
||||
aeb.Start = true;
|
||||
aeb.Restart = false;
|
||||
aeb.AnimationEntity = playerModel.FirstChildByName("DashForward");
|
||||
m_EventBroker->Publish(aeb);
|
||||
}
|
||||
} else {
|
||||
{
|
||||
Events::AutoAnimationBlend aeb;
|
||||
aeb.Duration = 0.1;
|
||||
aeb.NodeName = "DashBackward";
|
||||
aeb.RootNode = playerModel;
|
||||
aeb.Restart = true;
|
||||
aeb.Start = true;
|
||||
m_EventBroker->Publish(aeb);
|
||||
}
|
||||
{
|
||||
Events::AutoAnimationBlend aeb;
|
||||
aeb.Duration = 0.2;
|
||||
aeb.NodeName = "MovementBlend";
|
||||
aeb.RootNode = playerModel;
|
||||
aeb.Start = true;
|
||||
aeb.Restart = false;
|
||||
aeb.AnimationEntity = playerModel.FirstChildByName("DashBackward");
|
||||
m_EventBroker->Publish(aeb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
EntityWrapper dashEffectModel;
|
||||
dashEffectModel = playerModel.Clone();
|
||||
player["Transform"].Copy(dashEffectModel["Transform"]);
|
||||
|
||||
|
||||
dashEffectModel.AttachComponent("ExplosionEffect");
|
||||
dashEffectModel["ExplosionEffect"]["EndColor"] = (glm::vec4)playerModel["Model"]["Color"];
|
||||
((Field<glm::vec4>)dashEffectModel["ExplosionEffect"]["EndColor"]).w = 0.f;
|
||||
(Field<double>)dashEffectModel["ExplosionEffect"]["ExplosionDuration"] = dashEffect["Lifetime"]["Lifetime"];
|
||||
(Field<glm::vec3>)dashEffectModel["ExplosionEffect"]["ExplosionOrigin"] = glm::vec3(0, 1, 0) + (0.2f * controller->Movement());
|
||||
|
||||
|
||||
|
||||
|
||||
auto animationChildren = dashEffectModel.ChildrenWithComponent("Animation");
|
||||
|
||||
for (auto animationEntity : animationChildren) {
|
||||
(Field<bool>)animationEntity["Animation"]["Play"] = false;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "Systems/PlayerSpawnSystem.h"
|
||||
#include "Core/ELockMouse.h"
|
||||
|
||||
PlayerSpawnSystem::PlayerSpawnSystem(SystemParams params)
|
||||
: System(params)
|
||||
@@ -10,7 +11,7 @@ PlayerSpawnSystem::PlayerSpawnSystem(SystemParams params)
|
||||
ConfigFile* config = ResourceManager::Load<ConfigFile>("Config.ini");
|
||||
m_NetworkEnabled = config->Get("Networking.StartNetwork", false);
|
||||
m_ForcedRespawnTime = config->Get("Debug.RespawnTime", -1.0f);
|
||||
m_DbgConfigForceRespawn = m_ForcedRespawnTime > 0;
|
||||
m_DbgConfigForceRespawn = m_ForcedRespawnTime > 0 && IsServer;
|
||||
}
|
||||
|
||||
void PlayerSpawnSystem::Update(double dt)
|
||||
@@ -24,9 +25,23 @@ void PlayerSpawnSystem::Update(double dt)
|
||||
// Take the first CapturePointGameMode component found.
|
||||
ComponentWrapper& modeComponent = *pool->begin();
|
||||
// Increase timer.
|
||||
double& timer = (double&)modeComponent["RespawnTime"];
|
||||
Field<double> timer = modeComponent["RespawnTime"];
|
||||
timer += dt;
|
||||
double maxRespawnTime = m_DbgConfigForceRespawn ? m_ForcedRespawnTime : (double)modeComponent["MaxRespawnTime"];
|
||||
if (m_DbgConfigForceRespawn) {
|
||||
(Field<double>)modeComponent["MaxRespawnTime"] = m_ForcedRespawnTime;
|
||||
}
|
||||
double maxRespawnTime = (double)modeComponent["MaxRespawnTime"];
|
||||
EntityWrapper spectatorCam = m_World->GetFirstEntityByName("SpectatorCamera");
|
||||
if (spectatorCam.Valid()) {
|
||||
EntityWrapper HUD = spectatorCam.FirstChildByName("SpectatorHUD");
|
||||
if (HUD.Valid()) {
|
||||
EntityWrapper respawnTimer = spectatorCam.FirstChildByName("RespawnTimer");
|
||||
if (respawnTimer.Valid()) {
|
||||
//Update respawn time in the HUD element.
|
||||
respawnTimer["Text"]["Content"] = std::to_string(1 + (int)(maxRespawnTime - timer));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (timer < maxRespawnTime) {
|
||||
return;
|
||||
}
|
||||
@@ -45,7 +60,15 @@ void PlayerSpawnSystem::Update(double dt)
|
||||
}
|
||||
|
||||
int numSpawnedPlayers = 0;
|
||||
for (auto& req : m_SpawnRequests) {
|
||||
int playersSpectating = 0;
|
||||
const int numRequestsToHandle = (int)m_SpawnRequests.size();
|
||||
for (auto it = m_SpawnRequests.begin(); it != m_SpawnRequests.end(); ++it) {
|
||||
// It is valid if they didn't pick class yet
|
||||
// but don't spawn anything, goto next spawnrequest.
|
||||
if (it->Class == PlayerClass::None) {
|
||||
++playersSpectating;
|
||||
continue;
|
||||
}
|
||||
for (auto& cPlayerSpawn : *playerSpawns) {
|
||||
EntityWrapper spawner(m_World, cPlayerSpawn.EntityID);
|
||||
if (!spawner.HasComponent("Spawner")) {
|
||||
@@ -54,37 +77,63 @@ void PlayerSpawnSystem::Update(double dt)
|
||||
|
||||
// If the spawner has a team affiliation, check it
|
||||
if (spawner.HasComponent("Team")) {
|
||||
if ((int)spawner["Team"]["Team"] != req.Team) {
|
||||
continue;
|
||||
auto cSpawnerTeam = spawner["Team"];
|
||||
if ((int)cSpawnerTeam["Team"] != it->Team) {
|
||||
// If they somehow has a valid class as spectator, don't spawn them.
|
||||
if (it->Team == cSpawnerTeam["Team"].Enum("Spectator")) {
|
||||
++playersSpectating;
|
||||
break;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Choose a different spawner depending on class picked?
|
||||
std::string playerEntityFile;
|
||||
if (it->Class == PlayerClass::Assault) {
|
||||
playerEntityFile = (const std::string&)cPlayerSpawn["AssaultFile"];
|
||||
} else if (it->Class == PlayerClass::Defender) {
|
||||
playerEntityFile = (const std::string&)cPlayerSpawn["DefenderFile"];
|
||||
} else if (it->Class == PlayerClass::Sniper) {
|
||||
playerEntityFile = (const std::string&)cPlayerSpawn["SniperFile"];
|
||||
}
|
||||
|
||||
// Spawn the player!
|
||||
EntityWrapper player = SpawnerSystem::Spawn(spawner, EntityWrapper::Invalid, "Player");
|
||||
EntityWrapper player = SpawnerSystem::SpawnEntityFile(playerEntityFile, spawner, EntityWrapper::Invalid, "Player");
|
||||
// Set the player team affiliation
|
||||
player["Team"]["Team"] = req.Team;
|
||||
player["Team"]["Team"] = it->Team;
|
||||
|
||||
// Publish a PlayerSpawned event
|
||||
Events::PlayerSpawned e;
|
||||
e.PlayerID = req.PlayerID;
|
||||
e.PlayerID = it->PlayerID;
|
||||
e.Player = player;
|
||||
e.Spawner = spawner;
|
||||
m_EventBroker->Publish(e);
|
||||
++numSpawnedPlayers;
|
||||
it = m_SpawnRequests.erase(it);
|
||||
break;
|
||||
}
|
||||
if (it == m_SpawnRequests.end()) {
|
||||
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);
|
||||
if (numSpawnedPlayers != numRequestsToHandle - playersSpectating) {
|
||||
LOG_DEBUG("%i players were supposed to be spawned, but only %i was successfully.", numRequestsToHandle - playersSpectating, numSpawnedPlayers);
|
||||
} else {
|
||||
LOG_DEBUG("%i players were spawned.", numSpawnedPlayers);
|
||||
std::string dbg = numSpawnedPlayers != 0 ? std::to_string(numSpawnedPlayers) + " players were spawned. " : "";
|
||||
dbg += playersSpectating != 0 ? std::to_string(playersSpectating) + " players are spectating/picking class. " : "";
|
||||
LOG_DEBUG(dbg.c_str());
|
||||
}
|
||||
m_SpawnRequests.clear();
|
||||
}
|
||||
|
||||
bool PlayerSpawnSystem::OnInputCommand(Events::InputCommand& e)
|
||||
{
|
||||
if (e.Command != "PickTeam") {
|
||||
if (e.Command != "PickTeam" && e.Command != "PickClass" && e.Command != "SwapToTeamPick" && e.Command != "SwapToClassPick") {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (e.Value == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -94,34 +143,42 @@ bool PlayerSpawnSystem::OnInputCommand(Events::InputCommand& e)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (e.Value == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//TODO: Spectating?
|
||||
//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.
|
||||
// Check if the player already requested spawn.
|
||||
auto iter = m_SpawnRequests.begin();
|
||||
for (; iter != m_SpawnRequests.end(); ++iter) {
|
||||
if (iter->PlayerID == e.PlayerID) {
|
||||
// If player wants to switch team or class , remove their selected class so they don't spawn.
|
||||
if (e.Command == "SwapToTeamPick" || e.Command == "SwapToClassPick") {
|
||||
iter->Class = PlayerClass::None;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//If we get here we got a PickTeam or PickClass, so add or alter a spawn request.
|
||||
|
||||
if (iter != m_SpawnRequests.end()) {
|
||||
//If player is in queue to spawn, then change their team affiliation in the request.
|
||||
iter->Team = (ComponentInfo::EnumType)e.Value;
|
||||
// If player is in queue to spawn, then change their team affiliation or class in the request.
|
||||
if (e.Command == "PickTeam") {
|
||||
iter->Team = (ComponentInfo::EnumType)e.Value;
|
||||
} else {
|
||||
iter->Class = static_cast<PlayerClass>((int)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.
|
||||
// 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;
|
||||
if (e.Command == "PickTeam") {
|
||||
req.Team = (ComponentInfo::EnumType)e.Value;
|
||||
req.Class = PlayerClass::None;
|
||||
} else {
|
||||
// Should never get here, since you should have picked a team before you ever get a chance to pick class.
|
||||
LOG_WARNING("Sequence error: Should not be able to pick class before team");
|
||||
req.Team = 1; // TODO: 1 Signifies spectator, should probably have real enum here later.
|
||||
req.Class = static_cast<PlayerClass>((int)e.Value);
|
||||
}
|
||||
m_SpawnRequests.push_back(req);
|
||||
} else {
|
||||
return false;
|
||||
@@ -156,18 +213,8 @@ bool PlayerSpawnSystem::OnPlayerSpawned(Events::PlayerSpawned& e)
|
||||
Events::SetCamera e;
|
||||
e.CameraEntity = cameraEntity;
|
||||
m_EventBroker->Publish(e);
|
||||
}
|
||||
|
||||
// HACK: Set the player model color to team color
|
||||
EntityWrapper playerModel = e.Player.FirstChildByName("PlayerModel");
|
||||
if (playerModel.Valid() && e.Player.HasComponent("Team")) {
|
||||
ComponentWrapper cTeam = e.Player["Team"];
|
||||
ComponentWrapper cModel = playerModel["Model"];
|
||||
if ((ComponentInfo::EnumType)cTeam["Team"] == cTeam["Team"].Enum("Red")) {
|
||||
cModel["Color"] = glm::vec3(1.f, 0.f, 0.f);
|
||||
} else if ((ComponentInfo::EnumType)cTeam["Team"] == cTeam["Team"].Enum("Blue")) {
|
||||
cModel["Color"] = glm::vec3(0.f, 0.25f, 1.f);
|
||||
}
|
||||
Events::LockMouse lock;
|
||||
m_EventBroker->Publish(lock);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -175,7 +222,7 @@ bool PlayerSpawnSystem::OnPlayerSpawned(Events::PlayerSpawned& e)
|
||||
|
||||
bool PlayerSpawnSystem::OnPlayerDeath(Events::PlayerDeath& e)
|
||||
{
|
||||
//Only spawn request if network is disabled or we are server.
|
||||
// Only spawn request if network is disabled or we are server.
|
||||
if (!IsServer && m_NetworkEnabled) {
|
||||
return false;
|
||||
}
|
||||
@@ -183,10 +230,6 @@ bool PlayerSpawnSystem::OnPlayerDeath(Events::PlayerDeath& e)
|
||||
return false;
|
||||
}
|
||||
ComponentWrapper cTeam = e.Player["Team"];
|
||||
//A spectator can't die anyway
|
||||
if ((ComponentInfo::EnumType)cTeam["Team"] == cTeam["Team"].Enum("Spectator")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_PlayerIDs.count(e.Player.ID) == 0) {
|
||||
return false;
|
||||
@@ -195,6 +238,16 @@ bool PlayerSpawnSystem::OnPlayerDeath(Events::PlayerDeath& e)
|
||||
SpawnRequest req;
|
||||
req.PlayerID = m_PlayerIDs.at(e.Player.ID);
|
||||
req.Team = cTeam["Team"];
|
||||
// TODO: Something better than temp class state code, if we ever add class enums in .xml
|
||||
if (e.Player.HasComponent("DashAbility")) {
|
||||
req.Class = PlayerClass::Assault;
|
||||
} else if (e.Player.HasComponent("SprintAbility")) {
|
||||
req.Class = PlayerClass::Sniper;
|
||||
} else if (e.Player.HasComponent("ShieldAbility")) {
|
||||
req.Class = PlayerClass::Defender;
|
||||
} else {
|
||||
req.Class = PlayerClass::None;
|
||||
}
|
||||
m_SpawnRequests.push_back(req);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
#include "Game/Systems/ScoreScreenSystem.h"
|
||||
|
||||
|
||||
ScoreScreenSystem::ScoreScreenSystem(SystemParams params)
|
||||
: System(params)
|
||||
, PureSystem("ScoreScreen")
|
||||
{
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayerDeath, &ScoreScreenSystem::OnPlayerDeath);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &ScoreScreenSystem::OnPlayerSpawn);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayerConnected, &ScoreScreenSystem::OnPlayerConnected);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayerDisconnected, &ScoreScreenSystem::OnPlayerDisconnected);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EReset, &ScoreScreenSystem::OnReset);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &ScoreScreenSystem::OnInputCommand);
|
||||
}
|
||||
|
||||
void ScoreScreenSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& scoreScreen, double dt)
|
||||
{
|
||||
if (!IsServer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!entity.HasComponent("ScoreScreen")){
|
||||
return;
|
||||
}
|
||||
|
||||
int currentTeam = 0;
|
||||
|
||||
if(entity.HasComponent("Team")) {
|
||||
currentTeam = (int)entity["Team"]["Team"];
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
auto children = entity.ChildrenWithComponent("ScoreIdentity");
|
||||
|
||||
float position = 0.f;
|
||||
|
||||
for (auto it = m_PlayerIdentities.begin(); it != m_PlayerIdentities.end(); ++it) {
|
||||
bool found = false;
|
||||
for (auto& child : children) {
|
||||
int ID = (int)child["ScoreIdentity"]["ID"];
|
||||
if (it->first == ID) {
|
||||
if (it->second.Team != currentTeam) {
|
||||
m_World->DeleteEntity(child.ID);
|
||||
(Field<int>)entity["ScoreScreen"]["TotalIdentities"] -= 1;
|
||||
break;
|
||||
}
|
||||
for (auto it2 = m_DisconnectedIdentities.begin(); it2 != m_DisconnectedIdentities.end(); ++it2) {
|
||||
if (ID == *it2) {
|
||||
m_World->DeleteEntity(child.ID);
|
||||
(Field<int>)entity["ScoreScreen"]["TotalIdentities"] -= 1;
|
||||
it2 = m_DisconnectedIdentities.erase(it2);
|
||||
it = m_PlayerIdentities.erase(it);
|
||||
|
||||
//Remove from m_playerIdentities
|
||||
break;
|
||||
}
|
||||
}
|
||||
found = true;
|
||||
if(!child.Valid()) {
|
||||
break;
|
||||
}
|
||||
//Update Deaths for child
|
||||
(Field<int>)child["ScoreIdentity"]["Kills"] = it->second.Kills;
|
||||
//Update Kills for child
|
||||
(Field<int>)child["ScoreIdentity"]["Deaths"] = it->second.Deaths;
|
||||
//KD is not updated at the moment.
|
||||
if (it->second.Deaths != 0) {
|
||||
(Field<double>)child["ScoreIdentity"]["KD"] = it->second.Kills/it->second.Deaths;
|
||||
}
|
||||
|
||||
//Update position for child
|
||||
glm::vec3 offset = (glm::vec3)entity["ScoreScreen"]["Offset"];
|
||||
(Field<glm::vec3>) child["Transform"]["Position"] = offset * position;
|
||||
position += 1.f;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (it == m_PlayerIdentities.end()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(found == false) {
|
||||
if(it->second.Team != currentTeam) {
|
||||
//This player is not the same team as this scoreboard should show.
|
||||
continue;
|
||||
}
|
||||
//There is no entry for this player, create one.
|
||||
auto entityFile = ResourceManager::Load<EntityFile>("Schema/Entities/ScoreIdentity.xml");
|
||||
EntityWrapper scoreIdentity = entityFile->MergeInto(m_World);
|
||||
glm::vec3 offset = (glm::vec3)entity["ScoreScreen"]["Offset"];
|
||||
int newPosition = (int)entity["ScoreScreen"]["TotalIdentities"];
|
||||
(Field<glm::vec3>) scoreIdentity["Transform"]["Position"] = offset * (float)newPosition;
|
||||
auto cScoreIdentity = scoreIdentity["ScoreIdentity"];
|
||||
auto data = it->second;
|
||||
|
||||
(Field<std::string>)cScoreIdentity["Name"] = data.Name;
|
||||
(Field<int>)cScoreIdentity["ID"] = data.ID;
|
||||
|
||||
m_World->SetParent(scoreIdentity.ID, entity.ID);
|
||||
|
||||
(Field<int>)entity["ScoreScreen"]["TotalIdentities"] += 1;
|
||||
|
||||
}
|
||||
}
|
||||
//Local player should have an icon, compare with LocalPlayer somthing
|
||||
}
|
||||
|
||||
bool ScoreScreenSystem::OnPlayerDeath(const Events::KillDeath& e)
|
||||
{
|
||||
//When player die, add it to his score, and when possible the player who killed him.
|
||||
std::unordered_map<int, PlayerData>::iterator got;
|
||||
got = m_PlayerIdentities.find(e.Casualty);
|
||||
if(got != m_PlayerIdentities.end()) {
|
||||
got->second.Deaths++;
|
||||
}
|
||||
got = m_PlayerIdentities.find(e.Killer);
|
||||
if (got != m_PlayerIdentities.end()) {
|
||||
got->second.Kills++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ScoreScreenSystem::OnPlayerSpawn(const Events::PlayerSpawned& e)
|
||||
{
|
||||
//When a player spawn, add data to the list entry with the same ID.
|
||||
|
||||
std::unordered_map<int, PlayerData>::iterator got;
|
||||
|
||||
got = m_PlayerIdentities.find(e.PlayerID);
|
||||
if(got == m_PlayerIdentities.end()) {
|
||||
LOG_ERROR("Player spawned without having an entry on score screen");
|
||||
return 0;
|
||||
}
|
||||
auto& data = got->second;
|
||||
data.Player = e.Player;
|
||||
data.Team = (int)data.Player["Team"]["Team"];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ScoreScreenSystem::OnPlayerConnected(const Events::PlayerConnected& e)
|
||||
{
|
||||
//player has connected, add his data to the list of ScoreIdentities
|
||||
PlayerData data;
|
||||
data.ID = e.PlayerID;
|
||||
data.Name = e.PlayerName;
|
||||
m_PlayerIdentities.insert( {data.ID, data} );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ScoreScreenSystem::OnPlayerDisconnected(const Events::PlayerDisconnected& e)
|
||||
{
|
||||
//player has disconnected, remove him from list of ScoreIdentities
|
||||
m_DisconnectedIdentities.push_back(e.PlayerID);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ScoreScreenSystem::OnReset(const Events::Reset & e)
|
||||
{
|
||||
for (auto& it : m_PlayerIdentities) {
|
||||
it.second.Deaths = 0;
|
||||
it.second.Kills = 0;
|
||||
it.second.Team = 1;
|
||||
it.second.Player = EntityWrapper::Invalid;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScoreScreenSystem::OnInputCommand(const Events::InputCommand & e)
|
||||
{
|
||||
if (e.Command != "PickTeam" || e.PlayerID == -1 || e.Value == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_PlayerIdentities.at(e.PlayerID).Team = e.Value;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#include "../Game/Systems/ServerListSystem.h"
|
||||
|
||||
ServerListSystem::ServerListSystem(SystemParams params, IRenderer* renderer)
|
||||
: System(params)
|
||||
, PureSystem("ServerList")
|
||||
, m_Renderer(renderer)
|
||||
{
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EServerListRecieved, &ServerListSystem::OnServerListRecieved);
|
||||
}
|
||||
|
||||
void ServerListSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cServerList, double dt)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ServerListSystem::RefreshList()
|
||||
{
|
||||
Events::SearchForServers event;
|
||||
m_EventBroker->Publish(event);
|
||||
}
|
||||
|
||||
|
||||
bool ServerListSystem::OnServerListRecieved(const Events::DisplayServerlist& e)
|
||||
{
|
||||
if (e.Serverlist.size() == 0) {
|
||||
return 1;
|
||||
}
|
||||
auto serverLists = m_World->GetComponents("ServerList");
|
||||
if (serverLists == nullptr)
|
||||
return 1;
|
||||
for (auto& cServerList : *serverLists) {
|
||||
EntityWrapper serverListEntity = EntityWrapper(m_World, cServerList.EntityID);
|
||||
EntityWrapper identitySpawner = serverListEntity.FirstChildByName("ServerIdentitySpawner");
|
||||
identitySpawner.DeleteChildren();
|
||||
|
||||
(Field<int>)cServerList["TotalIdentities"] = (int)e.Serverlist.size();
|
||||
for (int i = 0; i < e.Serverlist.size(); i++) {
|
||||
//Create Identities for each server and place them on the right position.
|
||||
EntityWrapper newIdentity = SpawnerSystem::Spawn(identitySpawner, identitySpawner);
|
||||
EntityWrapper serverIdentityEntity = newIdentity.FirstChildByName("ServerIdentity");
|
||||
|
||||
glm::vec3 offset = (glm::vec3)serverListEntity["ServerList"]["Offset"];
|
||||
(Field<glm::vec3>)serverIdentityEntity["Transform"]["Position"] = offset * (float)i;
|
||||
|
||||
auto& cIdentity = serverIdentityEntity["ServerIdentity"];
|
||||
(Field<std::string>)cIdentity["IP"] = e.Serverlist[i].Address;
|
||||
(Field<std::string>)cIdentity["ServerName"] = e.Serverlist[i].Name;
|
||||
(Field<int>)cIdentity["Port"] = e.Serverlist[i].Port;
|
||||
(Field<int>)cIdentity["PlayersConnected"] = e.Serverlist[i].PlayersConnected;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -6,47 +6,30 @@ SoundSystem::SoundSystem(SystemParams params)
|
||||
{
|
||||
ConfigFile* config = ResourceManager::Load<ConfigFile>("Config.ini");
|
||||
m_Announcer = ResourceManager::Load<ConfigFile>("Config.ini")->Get<std::string>("Sound.Announcer", "female");
|
||||
if (IsClient) {
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &SoundSystem::OnPlayerSpawned);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_InputCommand, &SoundSystem::OnInputCommand);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EDoubleJump, &SoundSystem::OnDoubleJump);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EDashAbility, &SoundSystem::OnDashAbility);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayerDamage, &SoundSystem::OnPlayerDamage);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_ECaptured, &SoundSystem::OnCaptured);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_ETriggerTouch, &SoundSystem::OnTriggerTouch);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayerDeath, &SoundSystem::OnPlayerDeath);
|
||||
}
|
||||
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
|
||||
m_RandomGenerator = std::default_random_engine(seed);
|
||||
m_RandIntDistribution = std::uniform_int_distribution<int>(1, 12);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &SoundSystem::OnPlayerSpawned);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_InputCommand, &SoundSystem::OnInputCommand);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EDoubleJump, &SoundSystem::OnDoubleJump);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayerDamage, &SoundSystem::OnPlayerDamage);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_ECaptured, &SoundSystem::OnCaptured);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayerDeath, &SoundSystem::OnPlayerDeath);
|
||||
}
|
||||
|
||||
void SoundSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cComponent, double dt)
|
||||
{ }
|
||||
|
||||
void SoundSystem::Update(double dt)
|
||||
{
|
||||
if (!IsClient) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Temp for play test.
|
||||
if (m_DrumsIsPlaying) {
|
||||
m_DrumsIsPlaying = !drumTimer(dt);
|
||||
}
|
||||
}
|
||||
{ }
|
||||
|
||||
bool SoundSystem::OnPlayerSpawned(const Events::PlayerSpawned &e)
|
||||
{
|
||||
if (e.PlayerID == -1) { // Local player
|
||||
m_World->AttachComponent(e.Player.ID, "Listener");
|
||||
Events::PlaySoundOnEntity go;
|
||||
go.EmitterID = LocalPlayer.ID;
|
||||
go.FilePath = "Audio/announcer/" + m_Announcer + "/go.wav";
|
||||
Events::PlayAnonuncerVoice go;
|
||||
go.FilePath = "Audio/Announcer/" + m_Announcer + "/Go.wav";
|
||||
m_EventBroker->Publish(go);
|
||||
// TEMP: starts bgm
|
||||
{
|
||||
Events::PlayBackgroundMusic ev;
|
||||
ev.FilePath = "Audio/bgm/ambient.wav";
|
||||
m_EventBroker->Publish(ev);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -54,82 +37,67 @@ bool SoundSystem::OnPlayerSpawned(const Events::PlayerSpawned &e)
|
||||
bool SoundSystem::OnInputCommand(const Events::InputCommand & e)
|
||||
{
|
||||
if (e.Command == "Jump" && e.Value > 0) {
|
||||
if (e.PlayerID == -1) { // local player
|
||||
playerJumps();
|
||||
return true;
|
||||
}
|
||||
if (e.PlayerID == -1) {
|
||||
playerJumps(LocalPlayer);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SoundSystem::playerJumps()
|
||||
void SoundSystem::playerJumps(EntityWrapper player)
|
||||
{
|
||||
if (!LocalPlayer.Valid()) {
|
||||
if (!IsClient) { // Only play for clients
|
||||
return;
|
||||
}
|
||||
|
||||
bool grounded = (bool)m_World->GetComponent(LocalPlayer.ID, "Physics")["IsOnGround"];
|
||||
if(!player.HasComponent("Physics")) {
|
||||
return;
|
||||
}
|
||||
bool grounded = (bool)player["Physics"]["IsOnGround"];
|
||||
if (grounded) {
|
||||
Events::PlaySoundOnEntity e;
|
||||
e.EmitterID = LocalPlayer.ID;
|
||||
e.FilePath = "Audio/jump/jump1.wav";
|
||||
e.Emitter = player;
|
||||
e.FilePath = "Audio/Jump/Jump1.wav";
|
||||
m_EventBroker->Publish(e);
|
||||
}
|
||||
}
|
||||
|
||||
bool SoundSystem::drumTimer(double dt)
|
||||
{
|
||||
m_DrumTimer += dt;
|
||||
if (m_DrumTimer > 15) {
|
||||
m_DrumTimer = 0.0;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool SoundSystem::OnCaptured(const Events::Captured & e)
|
||||
{
|
||||
if (!LocalPlayer.Valid()) {
|
||||
if (!IsClient) { // Only play for clients
|
||||
return false;
|
||||
}
|
||||
int homeTeam = (int)m_World->GetComponent(e.CapturePointTakenID, "Team")["Team"];
|
||||
int team = (int)m_World->GetComponent(LocalPlayer.ID, "Team")["Team"];
|
||||
Events::PlaySoundOnEntity ev;
|
||||
Events::PlayAnonuncerVoice ev;
|
||||
if (team == homeTeam) {
|
||||
ev.FilePath = "Audio/announcer/" + m_Announcer + "/objective_achieved.wav";
|
||||
ev.FilePath = "Audio/Announcer/" + m_Announcer + "/ObjectiveAchieved.wav";
|
||||
} else {
|
||||
ev.FilePath = "Audio/announcer/" + m_Announcer + "/objective_failed.wav"; // have not been tested
|
||||
ev.FilePath = "Audio/Announcer/" + m_Announcer + "/ObjectiveFailed.wav";
|
||||
}
|
||||
ev.EmitterID = LocalPlayer.ID;
|
||||
m_EventBroker->Publish(ev);
|
||||
// Temp for play test.
|
||||
m_DrumsIsPlaying = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Testing purposes atm...
|
||||
bool SoundSystem::OnPlayerDamage(const Events::PlayerDamage & e)
|
||||
{
|
||||
if (!IsClient) { // Only play for clients
|
||||
return false;
|
||||
}
|
||||
if (LocalPlayer.ID = e.Victim.ID) { // You're local player was the one who took dmg
|
||||
if (!e.Victim.Valid() || !e.Inflictor.Valid()) {
|
||||
return false;
|
||||
}
|
||||
auto victimTeam = m_World->GetComponent(e.Victim.ID, "Team");
|
||||
auto inflictorTeam = m_World->GetComponent(e.Inflictor.ID, "Team");
|
||||
if ((int)victimTeam["Team"] == (int)inflictorTeam["Team"]) {
|
||||
// Victim and inflictor are the same team, should not play "hurt" sound.
|
||||
return false;
|
||||
}
|
||||
std::uniform_int_distribution<int> dist(1, 12);
|
||||
int rand = dist(generator);
|
||||
std::vector<std::string> paths;
|
||||
paths.push_back("Audio/hurt/hurt" + std::to_string(rand) + ".wav");
|
||||
paths.push_back("Audio/Hurt/Hurt" + std::to_string(m_RandIntDistribution(m_RandomGenerator)) + ".wav");
|
||||
|
||||
// // Breathe
|
||||
// int ammountOfbreaths = (static_cast<int>(e.Damage) / 10) + 2; // TEMP: Idk something stupid like this shit
|
||||
// for (int i = 0; i < ammountOfbreaths; i++) {
|
||||
// paths.push_back("Audio/exhausted/breath.wav");
|
||||
// }
|
||||
Events::PlayQueueOnEntity ev;
|
||||
ev.Emitter = LocalPlayer;
|
||||
ev.Emitter = e.Victim;
|
||||
ev.FilePaths = paths;
|
||||
m_EventBroker->Publish(ev);
|
||||
return false;
|
||||
@@ -137,7 +105,7 @@ bool SoundSystem::OnPlayerDamage(const Events::PlayerDamage & e)
|
||||
|
||||
bool SoundSystem::OnPlayerDeath(const Events::PlayerDeath & e)
|
||||
{
|
||||
if (e.Player.ID != LocalPlayer.ID) {
|
||||
if (!e.Player.Valid()) {
|
||||
return false;
|
||||
}
|
||||
if (!IsClient) {
|
||||
@@ -146,8 +114,9 @@ bool SoundSystem::OnPlayerDeath(const Events::PlayerDeath & e)
|
||||
// The local player is dead. The local player might be invalid?
|
||||
// Play the sound from the listener.
|
||||
// TODO: We might want to hear other players die.
|
||||
Events::PlayBackgroundMusic ev;
|
||||
ev.FilePath = "Audio/die/die2.wav";
|
||||
Events::PlaySoundOnEntity ev;
|
||||
ev.Emitter = e.Player;
|
||||
ev.FilePath = "Audio/Die/Die2.wav";
|
||||
m_EventBroker->Publish(ev);
|
||||
return false;
|
||||
}
|
||||
@@ -155,43 +124,25 @@ bool SoundSystem::OnPlayerDeath(const Events::PlayerDeath & e)
|
||||
bool SoundSystem::OnPlayerHealthPickup(const Events::PlayerHealthPickup & e)
|
||||
{
|
||||
Events::PlaySoundOnEntity ev;
|
||||
ev.EmitterID = LocalPlayer.ID;
|
||||
ev.FilePath = "Audio/pickup/pickup2.wav";
|
||||
ev.Emitter = LocalPlayer;
|
||||
ev.FilePath = "Audio/Pickup/Pickup2.wav";
|
||||
m_EventBroker->Publish(ev);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SoundSystem::OnTriggerTouch(const Events::TriggerTouch & e)
|
||||
{
|
||||
// Temp for play test.
|
||||
if (m_DrumsIsPlaying) {
|
||||
return false;
|
||||
}
|
||||
if (m_World->HasComponent(e.Trigger.ID, "CapturePoint")) {
|
||||
Events::PlaySoundOnEntity ev; // should be BGM
|
||||
ev.EmitterID = LocalPlayer.ID;
|
||||
ev.FilePath = "Audio/bgm/drumstest.wav";
|
||||
m_EventBroker->Publish(ev);
|
||||
// Temp for play test.
|
||||
m_DrumsIsPlaying = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SoundSystem::OnDoubleJump(const Events::DoubleJump & e)
|
||||
{
|
||||
Events::PlaySoundOnEntity ev;
|
||||
ev.EmitterID = LocalPlayer.ID;
|
||||
ev.FilePath = "Audio/jump/jump2.wav";
|
||||
m_EventBroker->Publish(ev);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SoundSystem::OnDashAbility(const Events::DashAbility &e)
|
||||
{
|
||||
Events::PlaySoundOnEntity ev;
|
||||
ev.EmitterID = LocalPlayer.ID;
|
||||
ev.FilePath = "Audio/jump/dash1.wav";
|
||||
m_EventBroker->Publish(ev);
|
||||
if (!m_World->ValidEntity(e.entityID)) {
|
||||
return false;
|
||||
}
|
||||
if (!IsClient) {
|
||||
return false;
|
||||
}
|
||||
if (e.entityID == LocalPlayer.ID) {
|
||||
Events::PlaySoundOnEntity ev;
|
||||
ev.Emitter = EntityWrapper(m_World, e.entityID);
|
||||
ev.FilePath = "Audio/Jump/Jump2.wav";
|
||||
m_EventBroker->Publish(ev);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -8,6 +8,17 @@ SpawnerSystem::SpawnerSystem(SystemParams params)
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -15,17 +26,17 @@ EntityWrapper SpawnerSystem::Spawn(EntityWrapper spawner, EntityWrapper parent /
|
||||
world = spawner.World;
|
||||
}
|
||||
|
||||
// Load the entity file and parse it
|
||||
const std::string& entityFilePath = spawner["Spawner"]["EntityFile"];
|
||||
auto entityFile = ResourceManager::Load<EntityFile>(entityFilePath);
|
||||
if (entityFile == nullptr) {
|
||||
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;
|
||||
}
|
||||
EntityFileParser parser(entityFile);
|
||||
EntityWrapper spawnedEntity(world, parser.MergeEntities(world, parent.ID));
|
||||
|
||||
//If the spawned entity is collideable, then we must not spawn it where it collides with something that
|
||||
//has a dontCollideComponent attached.
|
||||
// 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);
|
||||
@@ -37,7 +48,7 @@ EntityWrapper SpawnerSystem::Spawn(EntityWrapper spawner, EntityWrapper parent /
|
||||
|
||||
// Find any SpawnPoints existing as children of spawner
|
||||
auto children = spawner.World->GetDirectChildren(spawner.ID);
|
||||
std::vector<EntityWrapper> spawnPoints;
|
||||
std::list<EntityWrapper> spawnPoints;
|
||||
for (auto kv = children.first; kv != children.second; ++kv) {
|
||||
const EntityID& child = kv->second;
|
||||
if (spawner.World->HasComponent(child, "SpawnPoint")) {
|
||||
@@ -74,9 +85,9 @@ EntityWrapper SpawnerSystem::Spawn(EntityWrapper spawner, EntityWrapper parent /
|
||||
void SpawnerSystem::transformEntityToSpawnPoint(EntityWrapper spawnedEntity, EntityWrapper spawnPoint)
|
||||
{
|
||||
// Set its position and orientation to that of the SpawnPoint
|
||||
spawnedEntity["Transform"]["Position"] = Transform::AbsolutePosition(spawnPoint.World, spawnPoint.ID);
|
||||
spawnedEntity["Transform"]["Position"] = TransformSystem::AbsolutePosition(spawnPoint.World, spawnPoint.ID);
|
||||
// TODO: Quaternions, bitch
|
||||
spawnedEntity["Transform"]["Orientation"] = glm::eulerAngles(Transform::AbsoluteOrientation(spawnPoint));
|
||||
spawnedEntity["Transform"]["Orientation"] = glm::eulerAngles(TransformSystem::AbsoluteOrientation(spawnPoint));
|
||||
}
|
||||
|
||||
bool SpawnerSystem::spawnedEntityIsColliding(EntityWrapper spawnedEntity, EntityWrapper spawnPoint, const std::string& dontCollideComponent)
|
||||
@@ -84,7 +95,7 @@ bool SpawnerSystem::spawnedEntityIsColliding(EntityWrapper spawnedEntity, Entity
|
||||
transformEntityToSpawnPoint(spawnedEntity, spawnPoint);
|
||||
//Check if the spawned entity collides with anything, and if so, continue to the next spawnpoint.
|
||||
EntityAABB spawnedBox = *Collision::EntityAbsoluteAABB(spawnedEntity);
|
||||
const ComponentPool* otherSpawnedEntities = spawnPoint.World->GetComponents(dontCollideComponent);
|
||||
auto otherSpawnedEntities = spawnPoint.World->GetComponents(dontCollideComponent);
|
||||
for (const auto& obj : *otherSpawnedEntities) {
|
||||
if (spawnedEntity.ID == obj.EntityID) {
|
||||
continue;
|
||||
@@ -111,7 +122,7 @@ bool SpawnerSystem::spawnedEntityIsColliding(EntityWrapper spawnedEntity, Entity
|
||||
spawnedBox,
|
||||
model->CollisionVertices(),
|
||||
model->CollisionIndices(),
|
||||
Transform::ModelMatrix(otherEntity))) {
|
||||
TransformSystem::ModelMatrix(otherEntity))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
#include "Systems/SpectatorCameraSystem.h"
|
||||
#include "Rendering/ESetCamera.h"
|
||||
#include "Core/ELockMouse.h"
|
||||
|
||||
SpectatorCameraSystem::SpectatorCameraSystem(SystemParams params)
|
||||
: System(params)
|
||||
, m_CamSetToTeamPick(false)
|
||||
, m_PickedTeam(-1)
|
||||
{
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &SpectatorCameraSystem::OnInputCommand);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EDisconnect, &SpectatorCameraSystem::OnDisconnect);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EReset, &SpectatorCameraSystem::OnReset);
|
||||
}
|
||||
|
||||
void SpectatorCameraSystem::Update(double dt)
|
||||
{
|
||||
if (!m_CamSetToTeamPick && IsClient) {
|
||||
// Find the class pick camera and set them to it, since they need to pick a team before they can leave the screen.
|
||||
EntityWrapper spectatorCam = m_World->GetFirstEntityByName("PickTeamCamera");
|
||||
if (spectatorCam.HasComponent("Camera")) {
|
||||
m_CamSetToTeamPick = true;
|
||||
Events::SetCamera eSetCamera;
|
||||
eSetCamera.CameraEntity = spectatorCam;
|
||||
m_EventBroker->Publish(eSetCamera);
|
||||
Events::UnlockMouse unlock;
|
||||
m_EventBroker->Publish(unlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SpectatorCameraSystem::reset()
|
||||
{
|
||||
m_CamSetToTeamPick = false;
|
||||
// They will also be set to menu, so unlock mouse just in case they were in game with locked mouse.
|
||||
Events::UnlockMouse unlock;
|
||||
m_EventBroker->Publish(unlock);
|
||||
}
|
||||
|
||||
bool SpectatorCameraSystem::OnInputCommand(const Events::InputCommand& e)
|
||||
{
|
||||
// Only the client should do this, and only if player is not spawned.
|
||||
if (!IsClient || LocalPlayer.Valid()) {
|
||||
return false;
|
||||
}
|
||||
bool swapToClass = e.Command == "PickTeam" || e.Command == "SwapToClassPick";
|
||||
if (e.Value == 0 || !swapToClass && e.Command != "SwapToTeamPick" && e.Command != "PickClass") {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (e.Command == "PickTeam") {
|
||||
m_PickedTeam = e.Value;
|
||||
}
|
||||
|
||||
// If a team has not been picked, they may not exit the pick team screen.
|
||||
if (m_PickedTeam == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// A dead client should be able to swap to and between the overwatch cameras.
|
||||
std::string camName;
|
||||
// TODO: 1 Signifies spectator, should probably have real enum here later.
|
||||
// Spectators should never end up at the class select, instead put them at the SpectatorCamera.
|
||||
if (swapToClass && m_PickedTeam != 1) {
|
||||
if (m_PickedTeam == 2) {
|
||||
camName = "PickClassCameraRed";
|
||||
} else if (m_PickedTeam == 3) {
|
||||
camName = "PickClassCameraBlue";
|
||||
}
|
||||
} else if (e.Command == "SwapToTeamPick") {
|
||||
camName = "PickTeamCamera";
|
||||
} else {
|
||||
camName = "SpectatorCamera";
|
||||
}
|
||||
EntityWrapper spectatorCam = m_World->GetFirstEntityByName(camName);
|
||||
// Set the camera as active, if it exists.
|
||||
if (spectatorCam.HasComponent("Camera")) {
|
||||
// Set the class pick button visible if a blue or red team is picked, else invisible.
|
||||
EntityWrapper HUD;
|
||||
if (camName == "SpectatorCamera") {
|
||||
HUD = spectatorCam.FirstChildByName("SpectatorHUD");
|
||||
} else if (camName == "PickTeamCamera") {
|
||||
HUD = spectatorCam.FirstChildByName("PickTeamHUD");
|
||||
}
|
||||
// If we are at the class pick already, or if HUD is invalid for any other reason, do nothing.
|
||||
if (HUD.Valid()) {
|
||||
EntityWrapper toClassButton = spectatorCam.FirstChildByName("ToClassPick");
|
||||
if (toClassButton.Valid()) {
|
||||
// Set ClassButton as invisible if spectator, else visible.
|
||||
bool visible = m_PickedTeam != 1; // TODO: 1 Signifies spectator.
|
||||
toClassButton["Sprite"]["Visible"] = visible;
|
||||
for (auto& child : toClassButton.ChildrenWithComponent("Text")) {
|
||||
child["Text"]["Visible"] = visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
Events::SetCamera eSetCamera;
|
||||
eSetCamera.CameraEntity = spectatorCam;
|
||||
m_EventBroker->Publish(eSetCamera);
|
||||
Events::UnlockMouse unlock;
|
||||
m_EventBroker->Publish(unlock);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SpectatorCameraSystem::OnDisconnect(const Events::PlayerDisconnected& e)
|
||||
{
|
||||
// If local player gets disconnected, they should be set to
|
||||
// the spectator camera next time a map loads that has one.
|
||||
if (e.Entity == LocalPlayer.ID) {
|
||||
reset();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SpectatorCameraSystem::OnReset(const Events::Reset & e)
|
||||
{
|
||||
reset();
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#include "../Game/Systems/StartSystem.h"
|
||||
|
||||
StartSystem::StartSystem(SystemParams params)
|
||||
: System(params)
|
||||
, ImpureSystem()
|
||||
{
|
||||
EVENT_SUBSCRIBE_MEMBER(m_ECameraActivated, &StartSystem::OnCameraActivated);
|
||||
}
|
||||
|
||||
void StartSystem::Update(double dt)
|
||||
{
|
||||
auto cameras = m_World->GetComponents("Camera");
|
||||
if(cameras == nullptr) {
|
||||
return;
|
||||
}
|
||||
for(auto& cCamera: *cameras) {
|
||||
EntityWrapper cameraEntity = EntityWrapper(m_World, cCamera.EntityID);
|
||||
if(cameraEntity == m_ActiveCamera){
|
||||
return;
|
||||
}
|
||||
if(cameraEntity.Name() == "Overview_Camera_Start_Menu") {
|
||||
Events::SetCamera event;
|
||||
event.CameraEntity = cameraEntity;
|
||||
m_EventBroker->Publish(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool StartSystem::OnCameraActivated(const Events::SetCamera& e)
|
||||
{
|
||||
m_ActiveCamera = e.CameraEntity;
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
#include "Game/Systems/TextFieldReader.h"
|
||||
|
||||
void TextFieldReader::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cAmmunitionHUD, double dt)
|
||||
{
|
||||
if (!entity.HasComponent("Text")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the entity to read from
|
||||
const std::string& entityName = cAmmunitionHUD["ParentEntityName"];
|
||||
const std::string& componentType = cAmmunitionHUD["ComponentType"];
|
||||
EntityWrapper readEntity = entity;
|
||||
if (entityName.empty()) {
|
||||
if (!readEntity.HasComponent(componentType)) {
|
||||
readEntity = readEntity.FirstParentWithComponent(componentType);
|
||||
}
|
||||
} else {
|
||||
readEntity = entity.FirstParentByName(entityName);
|
||||
}
|
||||
if (!readEntity.Valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the component to read from
|
||||
if (componentType.empty() || !readEntity.HasComponent(componentType)) {
|
||||
return;
|
||||
}
|
||||
ComponentWrapper component = readEntity[componentType];
|
||||
|
||||
// Find the field to read from
|
||||
const std::string& fieldName = cAmmunitionHUD["Field"];
|
||||
if (fieldName.empty() || component.Info.Fields.count(fieldName) == 0) {
|
||||
return;
|
||||
}
|
||||
const ComponentInfo::Field_t& field = component.Info.Fields.at(fieldName);
|
||||
|
||||
Field<std::string> text = entity["Text"]["Content"];
|
||||
|
||||
if (field.Type == "int") {
|
||||
text = boost::lexical_cast<std::string>((const int&)component[fieldName]);
|
||||
} else if (field.Type == "float") {
|
||||
std::ostringstream ss;
|
||||
float f = (float)component[fieldName];
|
||||
ss << std::fixed << std::setprecision(2);
|
||||
ss << f;
|
||||
text = ss.str();
|
||||
} else if (field.Type == "double") {
|
||||
std::ostringstream ss;
|
||||
double d = (double)component[fieldName];
|
||||
ss << std::fixed << std::setprecision(1);
|
||||
ss << d;
|
||||
text = ss.str();
|
||||
} else if (field.Type == "bool") {
|
||||
text = boost::lexical_cast<std::string>((const bool&)component[fieldName]);
|
||||
} else if (field.Type == "string") {
|
||||
text = (const std::string&)component[fieldName];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,509 @@
|
||||
#include "Systems/Weapon/AssaultWeaponBehaviour.h"
|
||||
|
||||
void AssaultWeaponBehaviour::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cWeapon, double dt)
|
||||
{
|
||||
Field<double> fireCooldown = cWeapon["FireCooldown"];
|
||||
fireCooldown = glm::max(0.0, fireCooldown - dt);
|
||||
|
||||
WeaponBehaviour::UpdateComponent(entity, cWeapon, dt);
|
||||
}
|
||||
|
||||
void AssaultWeaponBehaviour::UpdateWeapon(ComponentWrapper cWeapon, WeaponInfo& wi, double dt)
|
||||
{
|
||||
CheckBoost(cWeapon, wi);
|
||||
// Start reloading automatically if at 0 mag ammo
|
||||
Field<int> magAmmo = cWeapon["MagazineAmmo"];
|
||||
if (m_ConfigAutoReload && magAmmo <= 0) {
|
||||
OnReload(cWeapon, wi);
|
||||
}
|
||||
|
||||
// Only start reloading once we're done firing
|
||||
Field<bool> reloadQueued = cWeapon["ReloadQueued"];
|
||||
Field<double> fireCooldown = cWeapon["FireCooldown"];
|
||||
Field<bool> isReloading = cWeapon["IsReloading"];
|
||||
if (reloadQueued && fireCooldown <= 0) {
|
||||
reloadQueued = fireCooldown;
|
||||
isReloading = true;
|
||||
}
|
||||
|
||||
// Decrement reload timer
|
||||
Field<double> reloadTimer = cWeapon["ReloadTimer"];
|
||||
if (isReloading) {
|
||||
reloadTimer = glm::max(0.0, reloadTimer - dt);
|
||||
}
|
||||
|
||||
// Handle reloading
|
||||
if (isReloading && reloadTimer <= 0.0) {
|
||||
Field<int> magSize = cWeapon["MagazineSize"];
|
||||
Field<int> ammo = cWeapon["Ammo"];
|
||||
|
||||
int usedAmmo = glm::max(0, *magSize - *magAmmo);
|
||||
magAmmo = glm::clamp(*ammo + *magAmmo, 0, *magSize);
|
||||
ammo = glm::max(0, *ammo - usedAmmo);
|
||||
|
||||
|
||||
isReloading = false;
|
||||
if (wi.FirstPersonEntity.Valid()) {
|
||||
wi.FirstPersonEntity.FirstChildByName("ViewModel")["Model"]["Visible"] = true;
|
||||
}
|
||||
if (wi.ThirdPersonEntity.Valid()) {
|
||||
wi.ThirdPersonEntity["Model"]["Visible"] = true;
|
||||
}
|
||||
}
|
||||
double reloadTime = cWeapon["ReloadTime"];
|
||||
if (isReloading && reloadTimer <= reloadTime / 2) {
|
||||
|
||||
}
|
||||
|
||||
// Restore view angle
|
||||
if (IsClient) {
|
||||
Field<float> currentTravel = cWeapon["CurrentTravel"];
|
||||
Field<float> returnSpeed = cWeapon["ViewReturnSpeed"];
|
||||
if (currentTravel > 0) {
|
||||
float change = returnSpeed * dt;
|
||||
currentTravel = glm::max(0.f, currentTravel - change);
|
||||
EntityWrapper camera = wi.Player.FirstChildByName("Camera");
|
||||
if (camera.Valid()) {
|
||||
Field<glm::vec3> cameraOrientation = camera["Transform"]["Orientation"];
|
||||
cameraOrientation.x(cameraOrientation.x() - change);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update first person run animation
|
||||
ComponentWrapper cPlayer = wi.Player["Player"];
|
||||
ComponentWrapper cPhysics = wi.Player["Physics"];
|
||||
const float& movementSpeed = cPlayer["MovementSpeed"];
|
||||
float speed = glm::length((const glm::vec3&)cPhysics["Velocity"]);
|
||||
float animationWeight = glm::min(speed, movementSpeed) / movementSpeed;
|
||||
EntityWrapper rootNode = wi.FirstPersonEntity;
|
||||
if (rootNode.Valid()) {
|
||||
EntityWrapper blend = rootNode.FirstChildByName("MovementBlendAssault");
|
||||
if (blend.Valid()) {
|
||||
(Field<double>)blend["Blend"]["Weight"] = animationWeight;
|
||||
}
|
||||
}
|
||||
|
||||
// Fire if we're able to fire
|
||||
if (canFire(cWeapon, wi)) {
|
||||
fireBullet(cWeapon, wi);
|
||||
}
|
||||
}
|
||||
|
||||
void AssaultWeaponBehaviour::OnPrimaryFire(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
cWeapon["TriggerHeld"] = true;
|
||||
if (canFire(cWeapon, wi)) {
|
||||
fireBullet(cWeapon, wi);
|
||||
}
|
||||
}
|
||||
|
||||
void AssaultWeaponBehaviour::OnCeasePrimaryFire(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
cWeapon["TriggerHeld"] = false;
|
||||
}
|
||||
|
||||
void AssaultWeaponBehaviour::OnReload(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
Field<bool> reloadQueued = cWeapon["ReloadQueued"];
|
||||
Field<bool> isReloading = cWeapon["IsReloading"];
|
||||
if (reloadQueued || isReloading) {
|
||||
return;
|
||||
}
|
||||
|
||||
Field<int> magAmmo = cWeapon["MagazineAmmo"];
|
||||
Field<int> magSize = cWeapon["MagazineSize"];
|
||||
if (magAmmo >= magSize) {
|
||||
return;
|
||||
}
|
||||
Field<int> ammo = cWeapon["Ammo"];
|
||||
if (ammo <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Field<double> reloadTime = cWeapon["ReloadTime"];
|
||||
Field<double> reloadTimer = cWeapon["ReloadTimer"];
|
||||
|
||||
// Start reload
|
||||
reloadQueued = true;
|
||||
reloadTimer = reloadTime;
|
||||
|
||||
// Play animation
|
||||
playAnimationAndReturn(wi.FirstPersonEntity, "ActionBlend", "Reload");
|
||||
// Third person anim
|
||||
Events::AutoAnimationBlend b1;
|
||||
b1.RootNode = wi.ThirdPersonPlayerModel;
|
||||
b1.NodeName = "Reload";
|
||||
b1.Restart = true;
|
||||
b1.Start = true;
|
||||
m_EventBroker->Publish(b1);
|
||||
|
||||
// Spawn explosion effect
|
||||
if (wi.FirstPersonEntity.Valid()) {
|
||||
if (IsClient) {
|
||||
EntityWrapper reloadEffectSpawner = wi.FirstPersonEntity.FirstChildByName("ReloadSpawner");
|
||||
if (reloadEffectSpawner.Valid()) {
|
||||
reloadEffectSpawner.DeleteChildren();
|
||||
SpawnerSystem::Spawn(reloadEffectSpawner, reloadEffectSpawner);
|
||||
}
|
||||
}
|
||||
wi.FirstPersonEntity.FirstChildByName("ViewModel")["Model"]["Visible"] = false;
|
||||
}
|
||||
if (wi.ThirdPersonEntity.Valid()) {
|
||||
if (IsServer) {
|
||||
EntityWrapper reloadEffectSpawner = wi.ThirdPersonEntity.FirstChildByName("ReloadSpawner");
|
||||
if (reloadEffectSpawner.Valid()) {
|
||||
reloadEffectSpawner.DeleteChildren();
|
||||
SpawnerSystem::Spawn(reloadEffectSpawner, reloadEffectSpawner);
|
||||
}
|
||||
}
|
||||
wi.ThirdPersonEntity["Model"]["Visible"] = false;
|
||||
}
|
||||
|
||||
// Sound
|
||||
Events::PlaySoundOnEntity e;
|
||||
e.Emitter = wi.Player;
|
||||
e.FilePath = "Audio/weapon/Assault/AssaultWeaponReload.wav";
|
||||
m_EventBroker->Publish(e);
|
||||
}
|
||||
|
||||
void AssaultWeaponBehaviour::OnEquip(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
cWeapon["FireCooldown"] = (double)cWeapon["EquipTime"];
|
||||
|
||||
if (wi.ThirdPersonPlayerModel.Valid()) {
|
||||
Events::AutoAnimationBlend b1;
|
||||
b1.RootNode = wi.ThirdPersonPlayerModel;
|
||||
b1.NodeName = "AssaultWeapon";
|
||||
b1.SingleLevelBlend = true;
|
||||
m_EventBroker->Publish(b1);
|
||||
}
|
||||
}
|
||||
|
||||
void AssaultWeaponBehaviour::OnHolster(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
// Make sure the trigger is released if weapon is holstered while firing
|
||||
cWeapon["TriggerHeld"] = false;
|
||||
|
||||
// Cancel any reload
|
||||
cWeapon["IsReloading"] = false;
|
||||
cWeapon["ReloadTimer"] = 0.0;
|
||||
}
|
||||
|
||||
void AssaultWeaponBehaviour::fireBullet(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
cWeapon["FireCooldown"] = 60.0 / (double)cWeapon["RPM"];
|
||||
|
||||
// Ammo
|
||||
Field<int> magAmmo = cWeapon["MagazineAmmo"];
|
||||
if (magAmmo <= 0) {
|
||||
return;
|
||||
} else {
|
||||
magAmmo -= 1;
|
||||
}
|
||||
|
||||
// View punch
|
||||
if (IsClient) {
|
||||
EntityWrapper camera = wi.Player.FirstChildByName("Camera");
|
||||
if (camera.Valid()) {
|
||||
Field<glm::vec3> cameraOrientation = camera["Transform"]["Orientation"];
|
||||
float viewPunch = cWeapon["ViewPunch"];
|
||||
float maxTravelAngle = cWeapon["MaxTravelAngle"];
|
||||
Field<float> currentTravel = cWeapon["CurrentTravel"];
|
||||
if (currentTravel < maxTravelAngle) {
|
||||
float change = viewPunch;
|
||||
if (currentTravel + change > maxTravelAngle) {
|
||||
change = maxTravelAngle - currentTravel;
|
||||
}
|
||||
cameraOrientation.x(cameraOrientation.x() + change);
|
||||
currentTravel += change;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get weapon model based on current person
|
||||
EntityWrapper weaponModelEntity = getRelevantWeaponEntity(wi);
|
||||
if (!weaponModelEntity.Valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Tracer
|
||||
EntityWrapper tracerSpawner = weaponModelEntity.FirstChildByName("WeaponMuzzleRay");
|
||||
if (tracerSpawner.Valid()) {
|
||||
glm::vec3 origin = TransformSystem::AbsolutePosition(tracerSpawner);
|
||||
glm::vec3 direction = glm::quat(TransformSystem::AbsoluteOrientationEuler(tracerSpawner)) * glm::vec3(0, 0, -1);
|
||||
float distance = traceRayDistance(origin, direction);
|
||||
EntityWrapper ray = SpawnerSystem::Spawn(tracerSpawner, tracerSpawner);
|
||||
if (ray.Valid()) {
|
||||
((Field<glm::vec3>)ray["Transform"]["Scale"]).z(distance);
|
||||
}
|
||||
}
|
||||
//MuzzleFlash
|
||||
EntityWrapper muzzleFlashSpawner = weaponModelEntity.FirstChildByName("WeaponMuzzleFlash");
|
||||
if (muzzleFlashSpawner.Valid()) {
|
||||
std::uniform_real_distribution<float> randomSpreadAngle(0.f, 3.1415f*2);
|
||||
EntityWrapper flash = SpawnerSystem::Spawn(muzzleFlashSpawner, muzzleFlashSpawner);
|
||||
if (flash.Valid()) {
|
||||
((Field<glm::vec3>)flash["Transform"]["Orientation"]).z(randomSpreadAngle(m_RandomEngine));
|
||||
}
|
||||
}
|
||||
|
||||
// Deal damage
|
||||
if (dealDamage(cWeapon, wi)) {
|
||||
// Show hit marker
|
||||
EntityWrapper hitMarkerSpawner = wi.Player.FirstChildByName("HitMarkerSpawner");
|
||||
if (hitMarkerSpawner.Valid()) {
|
||||
SpawnerSystem::Spawn(hitMarkerSpawner, hitMarkerSpawner);
|
||||
Events::PlaySoundOnEntity e;
|
||||
e.Emitter = wi.Player;
|
||||
e.FilePath = "Audio/weapon/hitclick.wav";
|
||||
m_EventBroker->Publish(e);
|
||||
}
|
||||
}
|
||||
|
||||
// View punch
|
||||
if (IsClient) {
|
||||
EntityWrapper camera = wi.Player.FirstChildByName("Camera");
|
||||
if (camera.Valid()) {
|
||||
Field<glm::vec3> cameraOrientation = camera["Transform"]["Orientation"];
|
||||
float viewPunch = cWeapon["ViewPunch"];
|
||||
float maxTravelAngle = cWeapon["MaxTravelAngle"];
|
||||
Field<float> currentTravel = cWeapon["CurrentTravel"];
|
||||
if (currentTravel < maxTravelAngle) {
|
||||
float change = viewPunch;
|
||||
if (currentTravel + change > maxTravelAngle) {
|
||||
change = maxTravelAngle - currentTravel;
|
||||
}
|
||||
cameraOrientation.x(cameraOrientation.x() + change);
|
||||
currentTravel += change;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Play animation
|
||||
playAnimationAndReturn(wi.FirstPersonEntity, "ActionBlend", "Fire");
|
||||
|
||||
// Third person anim
|
||||
Events::AutoAnimationBlend b1;
|
||||
b1.RootNode = wi.ThirdPersonPlayerModel;
|
||||
b1.NodeName = "Fire";
|
||||
b1.Restart = true;
|
||||
b1.Start = true;
|
||||
m_EventBroker->Publish(b1);
|
||||
|
||||
// Sound
|
||||
Events::PlaySoundOnEntity e;
|
||||
e.Emitter = wi.Player;
|
||||
e.FilePath = "Audio/weapon/Assault/AssaultWeaponFire.wav";
|
||||
m_EventBroker->Publish(e);
|
||||
}
|
||||
|
||||
bool AssaultWeaponBehaviour::canFire(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
bool triggerHeld = cWeapon["TriggerHeld"];
|
||||
bool cooldownPassed = (double)cWeapon["FireCooldown"] <= 0.0;
|
||||
bool isNotReloading = !(bool)cWeapon["IsReloading"];
|
||||
return triggerHeld && cooldownPassed && isNotReloading;
|
||||
}
|
||||
|
||||
bool AssaultWeaponBehaviour::dealDamage(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
// Only deal damage client side
|
||||
if (!IsClient) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only handle damage for the local player
|
||||
if (wi.Player != LocalPlayer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure the player isn't shooting from the grave
|
||||
if (!wi.Player.Valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3D-pick middle of screen
|
||||
Rectangle viewport = m_Renderer->GetViewportSize();
|
||||
glm::vec2 centerScreen(viewport.Width / 2, viewport.Height / 2);
|
||||
// TODO: Some horizontal spread
|
||||
PickData pickData = m_Renderer->Pick(centerScreen);
|
||||
EntityWrapper victim(m_World, pickData.Entity);
|
||||
if (!victim.Valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't let us somehow shoot ourselves in the foot
|
||||
if (victim == LocalPlayer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only care about players being hit
|
||||
if (!victim.HasComponent("Player")) {
|
||||
victim = victim.FirstParentWithComponent("Player");
|
||||
}
|
||||
if (!victim.Valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
double damage = cWeapon["BaseDamage"];
|
||||
// If friendly fire, reduce damage to 0 (needed to make Boosts, Ammosharing work)
|
||||
if ((ComponentInfo::EnumType)victim["Team"]["Team"] == (ComponentInfo::EnumType)wi.Player["Team"]["Team"]) {
|
||||
damage = 0;
|
||||
}
|
||||
|
||||
// Deal damage!
|
||||
Events::PlayerDamage ePlayerDamage;
|
||||
ePlayerDamage.Inflictor = wi.Player;
|
||||
ePlayerDamage.Victim = victim;
|
||||
ePlayerDamage.Damage = damage;
|
||||
m_EventBroker->Publish(ePlayerDamage);
|
||||
|
||||
return damage > 0;
|
||||
}
|
||||
|
||||
void AssaultWeaponBehaviour::CheckBoost(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
// Only check ammo client side
|
||||
if (!IsClient) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only handle ammo check for the local player
|
||||
if (wi.Player != LocalPlayer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the player isn't checking from the grave
|
||||
if (!wi.Player.Valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 3D-pick middle of screen
|
||||
Rectangle viewport = m_Renderer->GetViewportSize();
|
||||
glm::vec2 centerScreen(viewport.Width / 2, viewport.Height / 2);
|
||||
// TODO: Some horizontal spread
|
||||
PickData pickData = m_Renderer->Pick(centerScreen);
|
||||
EntityWrapper victim(m_World, pickData.Entity);
|
||||
if (!victim.Valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't let us somehow shoot ourselves in the foot
|
||||
if (victim == LocalPlayer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only care about players being hit
|
||||
if (!victim.HasComponent("Player")) {
|
||||
victim = victim.FirstParentWithComponent("Player");
|
||||
}
|
||||
if (!victim.Valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// If friendly fire, reduce damage to 0 (needed to make Boosts, Ammosharing work)
|
||||
if ((ComponentInfo::EnumType)victim["Team"]["Team"] == (ComponentInfo::EnumType)wi.Player["Team"]["Team"]) {
|
||||
|
||||
EntityWrapper friendlyBoostHudSpawner = wi.FirstPersonPlayerModel.FirstChildByName("FriendlyBoostAttachment");
|
||||
if (friendlyBoostHudSpawner.Valid()) {
|
||||
|
||||
EntityWrapper assaultBoost = victim.FirstChildByName("BoostAssault");
|
||||
EntityWrapper defenderBoost = victim.FirstChildByName("BoostDefender");
|
||||
EntityWrapper sniperBoost = victim.FirstChildByName("BoostSniper");
|
||||
|
||||
auto children = m_World->GetDirectChildren(friendlyBoostHudSpawner.ID);
|
||||
|
||||
if (children.first == children.second) {
|
||||
if (friendlyBoostHudSpawner.HasComponent("Spawner")) {
|
||||
|
||||
EntityWrapper friendlyBoostHud = SpawnerSystem::Spawn(friendlyBoostHudSpawner, friendlyBoostHudSpawner);
|
||||
if (friendlyBoostHud.Valid()) {
|
||||
EntityWrapper assaultBoostEntity = friendlyBoostHud.FirstChildByName("AssaultBoost");
|
||||
if (assaultBoostEntity.Valid()) {
|
||||
EntityWrapper active = assaultBoostEntity.FirstChildByName("Active");
|
||||
if (active.HasComponent("Text")) {
|
||||
if (assaultBoost.Valid()) {
|
||||
(Field<bool>)active["Text"]["Visible"] = true;
|
||||
} else {
|
||||
(Field<bool>)active["Text"]["Visible"] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EntityWrapper defenderBoostEntity = friendlyBoostHud.FirstChildByName("DefenderBoost");
|
||||
if (defenderBoostEntity.Valid()) {
|
||||
EntityWrapper active = defenderBoostEntity.FirstChildByName("Active");
|
||||
if (active.HasComponent("Text")) {
|
||||
if (defenderBoost.Valid()) {
|
||||
(Field<bool>)active["Text"]["Visible"] = true;
|
||||
} else {
|
||||
(Field<bool>)active["Text"]["Visible"] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EntityWrapper sniperBoostEntity = friendlyBoostHud.FirstChildByName("SniperBoost");
|
||||
if (sniperBoostEntity.Valid()) {
|
||||
EntityWrapper active = sniperBoostEntity.FirstChildByName("Active");
|
||||
if (active.HasComponent("Text")) {
|
||||
if (sniperBoost.Valid()) {
|
||||
(Field<bool>)active["Text"]["Visible"] = true;
|
||||
} else {
|
||||
(Field<bool>)active["Text"]["Visible"] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
EntityWrapper friendlyBoostHud = friendlyBoostHudSpawner.FirstChildByName("FriendlyBoostHUD");
|
||||
if (friendlyBoostHud.Valid()) {
|
||||
if(friendlyBoostHud.HasComponent("Lifetime")) {
|
||||
(Field<double>)friendlyBoostHud["Lifetime"]["Lifetime"] = 0.5;
|
||||
}
|
||||
|
||||
|
||||
|
||||
EntityWrapper assaultBoostEntity = friendlyBoostHud.FirstChildByName("AssaultBoost");
|
||||
if (assaultBoostEntity.Valid()) {
|
||||
EntityWrapper active = assaultBoostEntity.FirstChildByName("Active");
|
||||
if (active.HasComponent("Text")) {
|
||||
if (assaultBoost.Valid()) {
|
||||
(Field<bool>)active["Text"]["Visible"] = true;
|
||||
} else {
|
||||
(Field<bool>)active["Text"]["Visible"] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EntityWrapper defenderBoostEntity = friendlyBoostHud.FirstChildByName("DefenderBoost");
|
||||
if (defenderBoostEntity.Valid()) {
|
||||
EntityWrapper active = defenderBoostEntity.FirstChildByName("Active");
|
||||
if (active.HasComponent("Text")) {
|
||||
if (defenderBoost.Valid()) {
|
||||
(Field<bool>)active["Text"]["Visible"] = true;
|
||||
} else {
|
||||
(Field<bool>)active["Text"]["Visible"] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EntityWrapper sniperBoostEntity = friendlyBoostHud.FirstChildByName("SniperBoost");
|
||||
if (sniperBoostEntity.Valid()) {
|
||||
EntityWrapper active = sniperBoostEntity.FirstChildByName("Active");
|
||||
if (active.HasComponent("Text")) {
|
||||
if (sniperBoost.Valid()) {
|
||||
(Field<bool>)active["Text"]["Visible"] = true;
|
||||
} else {
|
||||
(Field<bool>)active["Text"]["Visible"] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,9 +366,10 @@ bool AssaultWeaponBehaviour::shoot(double damage)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for friendly fire
|
||||
|
||||
// If friendly fire - reduce damage to 0 (needed to make Boosts, Ammosharing work)
|
||||
if ((ComponentInfo::EnumType)victim["Team"]["Team"] == (ComponentInfo::EnumType)m_Player["Team"]["Team"]) {
|
||||
return false;
|
||||
damage = 0;
|
||||
}
|
||||
|
||||
// Deal damage!
|
||||
|
||||
@@ -2,48 +2,266 @@
|
||||
|
||||
void DefenderWeaponBehaviour::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cWeapon, double dt)
|
||||
{
|
||||
(double&)cWeapon["TimeSinceLastFire"] += dt;
|
||||
Field<double> fireCooldown = cWeapon["FireCooldown"];
|
||||
fireCooldown = glm::max(0.0, fireCooldown - dt);
|
||||
|
||||
WeaponBehaviour::UpdateComponent(entity, cWeapon, dt);
|
||||
}
|
||||
|
||||
void DefenderWeaponBehaviour::UpdateWeapon(WeaponInfo& wi, double dt)
|
||||
void DefenderWeaponBehaviour::UpdateWeapon(ComponentWrapper cWeapon, WeaponInfo& wi, double dt)
|
||||
{
|
||||
ComponentWrapper cWeapon = wi.GetComponent();
|
||||
CheckBoost(cWeapon, wi);
|
||||
// Decrement reload timer
|
||||
Field<double> reloadTimer = cWeapon["ReloadTimer"];
|
||||
reloadTimer = glm::max(0.0, reloadTimer - dt);
|
||||
|
||||
bool isFiring = cWeapon["IsFiring"];
|
||||
bool cooldownPassed = (double)cWeapon["TimeSinceLastFire"] >= (60.0 / (double)cWeapon["RPM"]);
|
||||
bool isNotShielding = wi.Player.ChildrenWithComponent("Shield").size() == 0;
|
||||
if (isFiring && cooldownPassed && isNotShielding) {
|
||||
fireShell(wi);
|
||||
// Start reloading automatically if at 0 mag ammo
|
||||
Field<int> magAmmo = cWeapon["MagazineAmmo"];
|
||||
if (m_ConfigAutoReload && magAmmo <= 0) {
|
||||
OnReload(cWeapon, wi);
|
||||
}
|
||||
|
||||
// Handle reloading
|
||||
Field<bool> isReloading = cWeapon["IsReloading"];
|
||||
if (isReloading && reloadTimer <= 0.0) {
|
||||
double reloadTime = cWeapon["ReloadTime"];
|
||||
Field<int> magSize = cWeapon["MagazineSize"];
|
||||
Field<int> ammo = cWeapon["Ammo"];
|
||||
if (magAmmo < magSize && ammo > 0) {
|
||||
ammo -= 1;
|
||||
magAmmo += 1;
|
||||
reloadTimer = reloadTime;
|
||||
Events::PlaySoundOnEntity e;
|
||||
e.Emitter = wi.Player;
|
||||
e.FilePath = "Audio/weapon/Zoom.wav";
|
||||
m_EventBroker->Publish(e);
|
||||
} else {
|
||||
isReloading = false;
|
||||
playAnimationAndReturn(wi.FirstPersonEntity, "FinalBlend", "ReloadEnd");
|
||||
|
||||
if (wi.ThirdPersonPlayerModel.Valid()) {
|
||||
Events::AutoAnimationBlend eFireIdle;
|
||||
eFireIdle.Duration = 0.2;
|
||||
eFireIdle.RootNode = wi.ThirdPersonPlayerModel;
|
||||
eFireIdle.NodeName = "Fire";
|
||||
eFireIdle.Start = false;
|
||||
m_EventBroker->Publish(eFireIdle);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Restore view angle
|
||||
if (IsClient) {
|
||||
Field<float> currentTravel = cWeapon["CurrentTravel"];
|
||||
Field<float> returnSpeed = cWeapon["ViewReturnSpeed"];
|
||||
if (currentTravel > 0) {
|
||||
float change = returnSpeed * dt;
|
||||
currentTravel = glm::max(0.f, currentTravel - change);
|
||||
EntityWrapper camera = wi.Player.FirstChildByName("Camera");
|
||||
if (camera.Valid()) {
|
||||
Field<glm::vec3> cameraOrientation = camera["Transform"]["Orientation"];
|
||||
cameraOrientation.x(cameraOrientation.x() - change);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fire if we're able to fire
|
||||
if (canFire(cWeapon, wi)) {
|
||||
fireShell(cWeapon, wi);
|
||||
}
|
||||
}
|
||||
|
||||
void DefenderWeaponBehaviour::OnPrimaryFire(WeaponInfo& wi)
|
||||
void DefenderWeaponBehaviour::OnPrimaryFire(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
ComponentWrapper cWeapon = wi.GetComponent();
|
||||
cWeapon["IsFiring"] = true;
|
||||
bool cooldownPassed = (double)cWeapon["TimeSinceLastFire"] >= (60.0 / (double)cWeapon["RPM"]);
|
||||
bool isNotShielding = wi.Player.ChildrenWithComponent("Shield").size() == 0;
|
||||
if (cooldownPassed && isNotShielding) {
|
||||
fireShell(wi);
|
||||
cWeapon["TriggerHeld"] = true;
|
||||
if (canFire(cWeapon, wi)) {
|
||||
fireShell(cWeapon, wi);
|
||||
}
|
||||
}
|
||||
|
||||
void DefenderWeaponBehaviour::OnCeasePrimaryFire(WeaponInfo& wi)
|
||||
void DefenderWeaponBehaviour::OnCeasePrimaryFire(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
ComponentWrapper cWeapon = wi.GetComponent();
|
||||
cWeapon["IsFiring"] = false;
|
||||
cWeapon["TriggerHeld"] = false;
|
||||
}
|
||||
|
||||
bool DefenderWeaponBehaviour::OnInputCommand(WeaponInfo& wi, const Events::InputCommand& e)
|
||||
void DefenderWeaponBehaviour::OnReload(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
if (e.Command == "SpecialAbility" && IsServer) {
|
||||
Field<bool> isReloading = cWeapon["IsReloading"];
|
||||
if (isReloading) {
|
||||
return;
|
||||
}
|
||||
|
||||
Field<int> magAmmo = cWeapon["MagazineAmmo"];
|
||||
Field<int> magSize = cWeapon["MagazineSize"];
|
||||
if (magAmmo >= magSize) {
|
||||
return;
|
||||
}
|
||||
Field<int> ammo = cWeapon["Ammo"];
|
||||
if (ammo <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
double reloadTime = cWeapon["ReloadTime"];
|
||||
Field<double> reloadTimer = cWeapon["ReloadTimer"];
|
||||
|
||||
// Start reload
|
||||
isReloading = true;
|
||||
reloadTimer = reloadTime;
|
||||
|
||||
// Play animation
|
||||
if (wi.FirstPersonEntity.Valid()) {
|
||||
Events::AutoAnimationBlend eBlendStart;
|
||||
eBlendStart.RootNode = wi.FirstPersonEntity;
|
||||
eBlendStart.NodeName = "ReloadStart";
|
||||
eBlendStart.Restart = true;
|
||||
eBlendStart.Start = true;
|
||||
m_EventBroker->Publish(eBlendStart);
|
||||
Events::AutoAnimationBlend eBlendLoop;
|
||||
eBlendLoop.RootNode = wi.FirstPersonEntity;
|
||||
eBlendLoop.NodeName = "ReloadLoop";
|
||||
eBlendLoop.Restart = true;
|
||||
eBlendLoop.Start = true;
|
||||
eBlendLoop.AnimationEntity = wi.FirstPersonEntity.FirstChildByName("ReloadStart");
|
||||
m_EventBroker->Publish(eBlendLoop);
|
||||
}
|
||||
|
||||
if(wi.ThirdPersonPlayerModel.Valid()) {
|
||||
Events::AutoAnimationBlend eReload;
|
||||
eReload.Duration = 0.2;
|
||||
eReload.RootNode = wi.ThirdPersonPlayerModel;
|
||||
eReload.NodeName = "Reload";
|
||||
eReload.Restart = true;
|
||||
eReload.Start = true;
|
||||
m_EventBroker->Publish(eReload);
|
||||
}
|
||||
}
|
||||
|
||||
void DefenderWeaponBehaviour::OnHolster(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
// Make sure the trigger is released if weapon is holstered while firing
|
||||
cWeapon["TriggerHeld"] = false;
|
||||
|
||||
// Cancel any reload
|
||||
cWeapon["IsReloading"] = false;
|
||||
cWeapon["ReloadTimer"] = 0.0;
|
||||
}
|
||||
|
||||
bool DefenderWeaponBehaviour::OnInputCommand(ComponentWrapper cWeapon, WeaponInfo& wi, const Events::InputCommand& e)
|
||||
{
|
||||
if (e.Command == "SpecialAbility") {
|
||||
EntityWrapper attachment = wi.Player.FirstChildByName("ShieldAttachment");
|
||||
if (attachment.Valid()) {
|
||||
if (e.Value > 0) {
|
||||
SpawnerSystem::Spawn(attachment, attachment);
|
||||
|
||||
if(wi.Player.Valid()) {
|
||||
if(wi.Player.HasComponent("ShieldAbility")) {
|
||||
(Field<bool>)wi.Player["ShieldAbility"]["Active"] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsServer) {
|
||||
SpawnerSystem::Spawn(attachment, attachment);
|
||||
}
|
||||
|
||||
EntityWrapper backAttachment = attachment.FirstChildByName("Back");
|
||||
if (backAttachment.Valid()) {
|
||||
Events::AutoAnimationBlend eDeployShieldAttachement;
|
||||
eDeployShieldAttachement.RootNode = backAttachment;
|
||||
eDeployShieldAttachement.NodeName = "Deploy";
|
||||
eDeployShieldAttachement.Restart = true;
|
||||
eDeployShieldAttachement.Start = true;
|
||||
m_EventBroker->Publish(eDeployShieldAttachement);
|
||||
}
|
||||
EntityWrapper frontAttachment = attachment.FirstChildByName("Front");
|
||||
if (frontAttachment.Valid()) {
|
||||
Events::AutoAnimationBlend eDeployShieldAttachement;
|
||||
eDeployShieldAttachement.RootNode = frontAttachment;
|
||||
eDeployShieldAttachement.NodeName = "Deploy";
|
||||
eDeployShieldAttachement.Restart = true;
|
||||
eDeployShieldAttachement.Start = true;
|
||||
m_EventBroker->Publish(eDeployShieldAttachement);
|
||||
|
||||
}
|
||||
|
||||
if (IsClient) {
|
||||
EntityWrapper root = wi.FirstPersonEntity;
|
||||
if (root.Valid()) {
|
||||
EntityWrapper animationNode = root.FirstChildByName("Shield");
|
||||
if (animationNode.Valid()) {
|
||||
Events::AutoAnimationBlend eShieldBlend;
|
||||
eShieldBlend.RootNode = root;
|
||||
eShieldBlend.NodeName = "Shield";
|
||||
eShieldBlend.Restart = true;
|
||||
eShieldBlend.Start = true;
|
||||
m_EventBroker->Publish(eShieldBlend);
|
||||
}
|
||||
}
|
||||
} else { // server only
|
||||
EntityWrapper root = wi.ThirdPersonPlayerModel;
|
||||
if (root.Valid()) {
|
||||
Events::AutoAnimationBlend eShieldActivateBlend;
|
||||
eShieldActivateBlend.RootNode = root;
|
||||
eShieldActivateBlend.NodeName = "ActivateShield";
|
||||
eShieldActivateBlend.Restart = true;
|
||||
eShieldActivateBlend.Start = true;
|
||||
m_EventBroker->Publish(eShieldActivateBlend);
|
||||
|
||||
EntityWrapper animationNode = root.FirstChildByName("ActivateShield");
|
||||
if (animationNode.Valid()) {
|
||||
Events::AutoAnimationBlend eShieldIdleBlend;
|
||||
eShieldIdleBlend.RootNode = root;
|
||||
eShieldIdleBlend.NodeName = "ShieldFront";
|
||||
eShieldIdleBlend.Restart = true;
|
||||
eShieldIdleBlend.Start = true;
|
||||
eShieldIdleBlend.AnimationEntity = animationNode;
|
||||
m_EventBroker->Publish(eShieldIdleBlend);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
attachment.DeleteChildren();
|
||||
|
||||
if (wi.Player.Valid()) {
|
||||
if (wi.Player.HasComponent("ShieldAbility")) {
|
||||
(Field<bool>)wi.Player["ShieldAbility"]["Active"] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsClient) {
|
||||
EntityWrapper root = wi.FirstPersonEntity;
|
||||
if (root.Valid()) {
|
||||
EntityWrapper animationNode = root.FirstChildByName("ActionBlend");
|
||||
if (animationNode.Valid()) {
|
||||
Events::AutoAnimationBlend eFireBlend;
|
||||
eFireBlend.RootNode = root;
|
||||
eFireBlend.NodeName = "ActionBlend";
|
||||
eFireBlend.Restart = true;
|
||||
eFireBlend.Start = true;
|
||||
m_EventBroker->Publish(eFireBlend);
|
||||
}
|
||||
}
|
||||
} else { // server only
|
||||
EntityWrapper root = wi.ThirdPersonPlayerModel;
|
||||
if (root.Valid()) {
|
||||
Events::AutoAnimationBlend eShieldDeactivateBlend;
|
||||
eShieldDeactivateBlend.RootNode = root;
|
||||
eShieldDeactivateBlend.NodeName = "ActivateShield";
|
||||
eShieldDeactivateBlend.Restart = true;
|
||||
eShieldDeactivateBlend.Start = true;
|
||||
eShieldDeactivateBlend.Reverse = true;
|
||||
m_EventBroker->Publish(eShieldDeactivateBlend);
|
||||
|
||||
EntityWrapper animationNode = root.FirstChildByName("ActivateShield");
|
||||
if (animationNode.Valid()) {
|
||||
Events::AutoAnimationBlend eActionBlend;
|
||||
eActionBlend.RootNode = root;
|
||||
eActionBlend.NodeName = "ActionBlend";
|
||||
eActionBlend.AnimationEntity = animationNode;
|
||||
m_EventBroker->Publish(eActionBlend);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,57 +269,147 @@ bool DefenderWeaponBehaviour::OnInputCommand(WeaponInfo& wi, const Events::Input
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DefenderWeaponBehaviour::OnSetCamera(const Events::SetCamera& e)
|
||||
|
||||
void DefenderWeaponBehaviour::OnEquip(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
m_CurrentCamera = e.CameraEntity;
|
||||
return true;
|
||||
if (wi.ThirdPersonPlayerModel.Valid()) {
|
||||
Events::AutoAnimationBlend b1;
|
||||
b1.RootNode = wi.ThirdPersonPlayerModel;
|
||||
b1.NodeName = "DefenderWeapon";
|
||||
b1.SingleLevelBlend = true;
|
||||
m_EventBroker->Publish(b1);
|
||||
}
|
||||
}
|
||||
|
||||
void DefenderWeaponBehaviour::fireShell(WeaponInfo& wi)
|
||||
void DefenderWeaponBehaviour::fireShell(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
ComponentWrapper cWeapon = wi.GetComponent();
|
||||
cWeapon["FireCooldown"] = 60.0 / (double)cWeapon["RPM"];
|
||||
|
||||
cWeapon["TimeSinceLastFire"] = 0.0;
|
||||
int numPellets = cWeapon["NumPellets"];
|
||||
float spreadAngle = cWeapon["SpreadAngle"];
|
||||
std::uniform_real_distribution<float> randomSpreadAngle(-spreadAngle, spreadAngle);
|
||||
// Stop reloading
|
||||
Field<bool> isReloading = cWeapon["IsReloading"];
|
||||
isReloading = false;
|
||||
|
||||
// Calculate pellet angles
|
||||
// HACK: Random for now?
|
||||
// TODO: Make distribution even for each quadrant
|
||||
std::vector<glm::vec2> pelletAngles;
|
||||
for (int i = 0; i < numPellets; i++) {
|
||||
pelletAngles.push_back(glm::vec2(randomSpreadAngle(m_RandomEngine), randomSpreadAngle(m_RandomEngine)));
|
||||
LOG_DEBUG("%f %f", pelletAngles[i].x, pelletAngles[i].y);
|
||||
}
|
||||
|
||||
double pelletDamage = (double)cWeapon["BaseDamage"] / numPellets;
|
||||
|
||||
// Tracers
|
||||
EntityWrapper weaponModelEntity;
|
||||
if (wi.Player == LocalPlayer) {
|
||||
weaponModelEntity = wi.FirstPersonEntity;
|
||||
// Ammo
|
||||
Field<int> magAmmo = cWeapon["MagazineAmmo"];
|
||||
if (magAmmo <= 0) {
|
||||
return;
|
||||
} else {
|
||||
weaponModelEntity = wi.ThirdPersonEntity;
|
||||
magAmmo -= 1;
|
||||
}
|
||||
if (weaponModelEntity.Valid()) {
|
||||
EntityWrapper spawner = weaponModelEntity.FirstChildByName("WeaponMuzzle");
|
||||
for (auto& angles : pelletAngles) {
|
||||
glm::vec3 direction = Transform::AbsoluteOrientation(spawner) * glm::quat(glm::vec3(angles, 0.f)) * glm::vec3(0, 0, -1);
|
||||
float distance = traceRayDistance(Transform::AbsolutePosition(spawner), direction);
|
||||
EntityWrapper ray = SpawnerSystem::Spawn(spawner);
|
||||
((glm::vec3&)ray["Transform"]["Scale"]).z = (distance / 100.f);
|
||||
glm::vec3& orientation = ray["Transform"]["Orientation"];
|
||||
orientation.x += angles.x;
|
||||
orientation.y += angles.y;
|
||||
glm::vec3 trajectory = direction * distance;
|
||||
dealDamage(wi, direction, pelletDamage);
|
||||
|
||||
// We can't really do any valuable calculations without a valid camera
|
||||
if (!m_CurrentCamera.Valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int numPellets = cWeapon["NumPellets"];
|
||||
|
||||
// Create a spread pattern
|
||||
std::vector<glm::vec2> pattern;
|
||||
// The first pellet is always centered
|
||||
pattern.push_back(glm::vec2(0, 0));
|
||||
// Any additional pellets form circles around the middle
|
||||
int numOuterPellets = numPellets - 1;
|
||||
float angleIncrement = glm::two_pi<float>() / numOuterPellets;
|
||||
for (int i = 0; i < numOuterPellets; ++i) {
|
||||
float angle = angleIncrement * i;
|
||||
glm::vec2 pellet = glm::vec2(glm::cos(angle), glm::sin(angle));
|
||||
pattern.push_back(pellet);
|
||||
}
|
||||
|
||||
// Deal damage (clientside)
|
||||
dealDamage(cWeapon, wi, pattern);
|
||||
|
||||
// Spawn tracers
|
||||
spawnTracers(cWeapon, wi, pattern);
|
||||
|
||||
// View punch
|
||||
//if (IsClient) {
|
||||
// EntityWrapper camera = wi.Player.FirstChildByName("Camera");
|
||||
// if (camera.Valid()) {
|
||||
// glm::vec3& cameraOrientation = camera["Transform"]["Orientation"];
|
||||
// float viewPunch = cWeapon["ViewPunch"];
|
||||
// float maxTravelAngle = cWeapon["MaxTravelAngle"];
|
||||
// float& currentTravel = cWeapon["CurrentTravel"];
|
||||
// if (currentTravel < maxTravelAngle) {
|
||||
// float change = viewPunch;
|
||||
// if (currentTravel + change > maxTravelAngle) {
|
||||
// change = maxTravelAngle - currentTravel;
|
||||
// }
|
||||
// cameraOrientation.x += change;
|
||||
// currentTravel += change;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
// Play animation
|
||||
playAnimationAndReturn(wi.FirstPersonEntity, "FinalBlend", "Fire");
|
||||
playAnimationAndReturn(wi.ThirdPersonPlayerModel, "FinalBlend", "Fire");
|
||||
|
||||
// Sound
|
||||
Events::PlaySoundOnEntity e;
|
||||
e.Emitter = wi.Player;
|
||||
e.FilePath = "Audio/weapon/Blast.wav";
|
||||
m_EventBroker->Publish(e);
|
||||
}
|
||||
|
||||
void DefenderWeaponBehaviour::spawnTracers(ComponentWrapper cWeapon, WeaponInfo& wi, std::vector<glm::vec2> pattern)
|
||||
{
|
||||
EntityWrapper flashSpawner = getRelevantWeaponEntity(wi).FirstChildByName("WeaponMuzzleFlash");
|
||||
if (flashSpawner.Valid()) {
|
||||
std::uniform_real_distribution<float> randomSpreadAngle(0.f, 3.1415f*2);
|
||||
EntityWrapper flash = SpawnerSystem::Spawn(flashSpawner, flashSpawner);
|
||||
if (flash.Valid()) {
|
||||
((Field<glm::vec3>)flash["Transform"]["Orientation"]).z(randomSpreadAngle(m_RandomEngine));
|
||||
}
|
||||
}
|
||||
|
||||
EntityWrapper muzzle = getRelevantWeaponEntity(wi).FirstChildByName("WeaponMuzzleRay");
|
||||
if (!muzzle.Valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
EntityWrapper camera = wi.Player.FirstChildByName("Camera");
|
||||
if (!camera.Valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
float spreadAngle = glm::radians((float)cWeapon["SpreadAngle"]);
|
||||
|
||||
for (auto& pellet : pattern) {
|
||||
glm::quat pelletRotation = glm::quat(TransformSystem::AbsoluteOrientationEuler(camera)) * glm::quat(glm::vec3(pellet.y, pellet.x, 0) * spreadAngle);
|
||||
glm::vec3 cameraPosition = TransformSystem::AbsolutePosition(camera);
|
||||
glm::vec3 direction = pelletRotation * glm::vec3(0, 0, -1);
|
||||
|
||||
float distance;
|
||||
glm::vec3 hitPosition;
|
||||
if (Collision::EntityFirstHitByRay(Ray(cameraPosition, direction), m_CollisionOctree, distance, hitPosition) != boost::none) { // don't spawn ray if you "miss the world"
|
||||
|
||||
EntityWrapper ray = SpawnerSystem::Spawn(muzzle);
|
||||
if (ray.Valid()) {
|
||||
ComponentWrapper cTransform = ray["Transform"];
|
||||
Field<glm::vec3> rayOrigin = cTransform["Position"];
|
||||
Field<glm::vec3> rayOrientation = cTransform["Orientation"];
|
||||
Field<glm::vec3> rayScale = cTransform["Scale"];
|
||||
|
||||
glm::vec3 muzzlePosition = TransformSystem::AbsolutePosition(muzzle);
|
||||
glm::quat muzzleOrientation = TransformSystem::AbsoluteOrientation(muzzle);
|
||||
|
||||
rayOrigin = muzzlePosition;
|
||||
|
||||
glm::vec3 muzzleToHit = hitPosition - muzzlePosition;
|
||||
glm::vec3 lookVector = glm::normalize(-muzzleToHit);
|
||||
float pitch = std::asin(-lookVector.y);
|
||||
float yaw = std::atan2(lookVector.x, lookVector.z);
|
||||
glm::quat orientation = glm::quat(glm::vec3(pitch, yaw, 0));
|
||||
rayOrientation = glm::eulerAngles(orientation);
|
||||
rayScale.x(glm::length(muzzleToHit));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DefenderWeaponBehaviour::dealDamage(WeaponInfo& wi, glm::vec3 direction, double damage)
|
||||
void DefenderWeaponBehaviour::dealDamage(ComponentWrapper cWeapon, WeaponInfo& wi, const std::vector<glm::vec2>& pattern)
|
||||
{
|
||||
// Only deal damage client side
|
||||
if (!IsClient) {
|
||||
@@ -117,59 +425,84 @@ void DefenderWeaponBehaviour::dealDamage(WeaponInfo& wi, glm::vec3 direction, do
|
||||
if (!wi.Player.Valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
glm::vec3 maxRange = direction * 2.f;
|
||||
EntityWrapper camera = wi.Player.FirstChildByName("Camera");
|
||||
glm::vec3 cameraPosition = Transform::AbsolutePosition(camera);
|
||||
if (!camera.Valid()) {
|
||||
return;
|
||||
}
|
||||
Rectangle screenResolution = m_Renderer->GetViewportSize();
|
||||
glm::vec2 centerScreen = glm::vec2(screenResolution.Width / 2, screenResolution.Height / 2);
|
||||
glm::vec2 screenCoords = cameraFromEntity(m_CurrentCamera).WorldToScreen(cameraPosition + maxRange, m_Renderer->GetViewportSize());
|
||||
PickData pickData = m_Renderer->Pick(centerScreen + screenCoords);
|
||||
EntityWrapper victim(m_World, pickData.Entity);
|
||||
if (!victim.Valid()) {
|
||||
return;
|
||||
|
||||
// Convert the spread angle to screen coordinates, taking FOV into account
|
||||
Rectangle res = m_Renderer->GetViewportSize();
|
||||
float spreadAngle = glm::radians((float)cWeapon["SpreadAngle"]);
|
||||
float nearClip = (double)m_CurrentCamera["Camera"]["NearClip"];
|
||||
float farClip = (double)m_CurrentCamera["Camera"]["FarClip"];
|
||||
float yFOV = glm::radians((double)m_CurrentCamera["Camera"]["FOV"]);
|
||||
//float yRefFOV = glm::radians(59.f);
|
||||
//float yRef = glm::tan(yRefFOV) * nearClip;
|
||||
//float yRatio = yRef / (glm::tan(yFOV) * nearClip);
|
||||
//float yMax = (yRatio / 2.f) * spreadAngle * (res.Height / 2.f);
|
||||
float yRefFOV = glm::radians(59.f);
|
||||
float yRef = glm::tan(yRefFOV) * nearClip;
|
||||
float yRatio = yRef / (glm::tan(yFOV) * nearClip);
|
||||
float yMax = yRatio * (glm::tan(spreadAngle) * (farClip - nearClip)) * glm::pi<float>(); // ????? Good enough????
|
||||
|
||||
LOG_DEBUG("Ratio: %f", yRatio);
|
||||
LOG_DEBUG("fatClip: %f", farClip);
|
||||
LOG_DEBUG("yMax: %f", yMax);
|
||||
double pelletDamage = (double)cWeapon["BaseDamage"] / pattern.size();
|
||||
|
||||
// Pick!
|
||||
std::unordered_map<EntityWrapper, double> damageSum;
|
||||
glm::vec2 screenCenter(res.Width / 2.f, res.Height / 2.f);
|
||||
for (auto& pellet : pattern) {
|
||||
glm::vec2 pickCoord = screenCenter + (pellet * glm::vec2(yMax, yMax));
|
||||
PickData pick = m_Renderer->Pick(pickCoord);
|
||||
|
||||
EntityWrapper victim(m_World, pick.Entity);
|
||||
if (!victim.Valid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Temp hit decal
|
||||
EntityWrapper hit = ResourceManager::Load<EntityFile>("Schema/Entities/HitTest.xml")->MergeInto(m_World);
|
||||
(Field<glm::vec3>)hit["Transform"]["Position"] = pick.Position;
|
||||
|
||||
// Don't let us shoot ourselves in the foot somehow
|
||||
if (victim == LocalPlayer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only care about players being hit
|
||||
if (!victim.HasComponent("Player")) {
|
||||
victim = victim.FirstParentWithComponent("Player");
|
||||
if (!victim.Valid()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for friendly fire
|
||||
if ((ComponentInfo::EnumType)victim["Team"]["Team"] == (ComponentInfo::EnumType)wi.Player["Team"]["Team"]) {
|
||||
// TODO: Ammo sharing
|
||||
continue;
|
||||
}
|
||||
|
||||
damageSum[victim] += pelletDamage;
|
||||
((Field<glm::vec4>)hit["Model"]["Color"]).z(1.f);
|
||||
}
|
||||
|
||||
// Don't let us shoot ourselves in the foot somehow
|
||||
if (victim == LocalPlayer) {
|
||||
return;
|
||||
// Deal damage!
|
||||
for (auto& kv : damageSum) {
|
||||
// Deal damage!
|
||||
Events::PlayerDamage ePlayerDamage;
|
||||
ePlayerDamage.Inflictor = wi.Player;
|
||||
ePlayerDamage.Victim = kv.first;
|
||||
ePlayerDamage.Damage = kv.second;
|
||||
m_EventBroker->Publish(ePlayerDamage);
|
||||
LOG_DEBUG("Dealt %f damage to #%i", kv.second, kv.first.ID);
|
||||
}
|
||||
|
||||
// Only care about players being hit
|
||||
if (!victim.HasComponent("Player")) {
|
||||
victim = victim.FirstParentWithComponent("Player");
|
||||
}
|
||||
if (!victim.Valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for friendly fire
|
||||
if ((ComponentInfo::EnumType)victim["Team"]["Team"] == (ComponentInfo::EnumType)wi.Player["Team"]["Team"]) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Deal damage!
|
||||
Events::PlayerDamage ePlayerDamage;
|
||||
ePlayerDamage.Inflictor = wi.Player;
|
||||
ePlayerDamage.Victim = victim;
|
||||
ePlayerDamage.Damage = damage;
|
||||
m_EventBroker->Publish(ePlayerDamage);
|
||||
LOG_DEBUG("Damage: %f", damage);
|
||||
}
|
||||
|
||||
float DefenderWeaponBehaviour::traceRayDistance(glm::vec3 origin, glm::vec3 direction)
|
||||
bool DefenderWeaponBehaviour::canFire(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
float distance;
|
||||
glm::vec3 pos;
|
||||
auto entity = Collision::EntityFirstHitByRay(Ray(origin, direction), m_CollisionOctree, distance, pos);
|
||||
if (entity) {
|
||||
return distance;
|
||||
} else {
|
||||
return 100.f;
|
||||
}
|
||||
bool triggerHeld = cWeapon["TriggerHeld"];
|
||||
bool cooldownPassed = (double)cWeapon["FireCooldown"] <= 0.0;
|
||||
bool isNotShielding = wi.Player.ChildrenWithComponent("Shield").size() == 0;
|
||||
return triggerHeld && cooldownPassed && isNotShielding;
|
||||
}
|
||||
|
||||
Camera DefenderWeaponBehaviour::cameraFromEntity(EntityWrapper camera)
|
||||
@@ -186,3 +519,148 @@ Camera DefenderWeaponBehaviour::cameraFromEntity(EntityWrapper camera)
|
||||
cam.SetOrientation(glm::quat((const glm::vec3&)cTransform["Orientation"]));
|
||||
return cam;
|
||||
}
|
||||
|
||||
void DefenderWeaponBehaviour::CheckBoost(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
// Only check ammo client side
|
||||
if (!IsClient) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only handle ammo check for the local player
|
||||
if (wi.Player != LocalPlayer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the player isn't checking from the grave
|
||||
if (!wi.Player.Valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 3D-pick middle of screen
|
||||
Rectangle viewport = m_Renderer->GetViewportSize();
|
||||
glm::vec2 centerScreen(viewport.Width / 2, viewport.Height / 2);
|
||||
// TODO: Some horizontal spread
|
||||
PickData pickData = m_Renderer->Pick(centerScreen);
|
||||
EntityWrapper victim(m_World, pickData.Entity);
|
||||
if (!victim.Valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't let us somehow shoot ourselves in the foot
|
||||
if (victim == LocalPlayer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only care about players being hit
|
||||
if (!victim.HasComponent("Player")) {
|
||||
victim = victim.FirstParentWithComponent("Player");
|
||||
}
|
||||
if (!victim.Valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// If friendly fire, reduce damage to 0 (needed to make Boosts, Ammosharing work)
|
||||
if ((ComponentInfo::EnumType)victim["Team"]["Team"] == (ComponentInfo::EnumType)wi.Player["Team"]["Team"]) {
|
||||
|
||||
EntityWrapper friendlyBoostHudSpawner = wi.FirstPersonPlayerModel.FirstChildByName("FriendlyBoostAttachment");
|
||||
if (friendlyBoostHudSpawner.Valid()) {
|
||||
|
||||
EntityWrapper assaultBoost = victim.FirstChildByName("BoostAssault");
|
||||
EntityWrapper defenderBoost = victim.FirstChildByName("BoostDefender");
|
||||
EntityWrapper sniperBoost = victim.FirstChildByName("BoostSniper");
|
||||
|
||||
auto children = m_World->GetDirectChildren(friendlyBoostHudSpawner.ID);
|
||||
|
||||
if (children.first == children.second) {
|
||||
if (friendlyBoostHudSpawner.HasComponent("Spawner")) {
|
||||
|
||||
EntityWrapper friendlyBoostHud = SpawnerSystem::Spawn(friendlyBoostHudSpawner, friendlyBoostHudSpawner);
|
||||
if (friendlyBoostHud.Valid()) {
|
||||
EntityWrapper assaultBoostEntity = friendlyBoostHud.FirstChildByName("AssaultBoost");
|
||||
if (assaultBoostEntity.Valid()) {
|
||||
EntityWrapper active = assaultBoostEntity.FirstChildByName("Active");
|
||||
if (active.HasComponent("Text")) {
|
||||
if (assaultBoost.Valid()) {
|
||||
(Field<bool>)active["Text"]["Visible"] = true;
|
||||
} else {
|
||||
(Field<bool>)active["Text"]["Visible"] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EntityWrapper defenderBoostEntity = friendlyBoostHud.FirstChildByName("DefenderBoost");
|
||||
if (defenderBoostEntity.Valid()) {
|
||||
EntityWrapper active = defenderBoostEntity.FirstChildByName("Active");
|
||||
if (active.HasComponent("Text")) {
|
||||
if (defenderBoost.Valid()) {
|
||||
(Field<bool>)active["Text"]["Visible"] = true;
|
||||
} else {
|
||||
(Field<bool>)active["Text"]["Visible"] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EntityWrapper sniperBoostEntity = friendlyBoostHud.FirstChildByName("SniperBoost");
|
||||
if (sniperBoostEntity.Valid()) {
|
||||
EntityWrapper active = sniperBoostEntity.FirstChildByName("Active");
|
||||
if (active.HasComponent("Text")) {
|
||||
if (sniperBoost.Valid()) {
|
||||
(Field<bool>)active["Text"]["Visible"] = true;
|
||||
} else {
|
||||
(Field<bool>)active["Text"]["Visible"] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
EntityWrapper friendlyBoostHud = friendlyBoostHudSpawner.FirstChildByName("FriendlyBoostHUD");
|
||||
if (friendlyBoostHud.Valid()) {
|
||||
if (friendlyBoostHud.HasComponent("Lifetime")) {
|
||||
(Field<double>)friendlyBoostHud["Lifetime"]["Lifetime"] = 0.5;
|
||||
}
|
||||
|
||||
|
||||
|
||||
EntityWrapper assaultBoostEntity = friendlyBoostHud.FirstChildByName("AssaultBoost");
|
||||
if (assaultBoostEntity.Valid()) {
|
||||
EntityWrapper active = assaultBoostEntity.FirstChildByName("Active");
|
||||
if (active.HasComponent("Text")) {
|
||||
if (assaultBoost.Valid()) {
|
||||
(Field<bool>)active["Text"]["Visible"] = true;
|
||||
} else {
|
||||
(Field<bool>)active["Text"]["Visible"] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EntityWrapper defenderBoostEntity = friendlyBoostHud.FirstChildByName("DefenderBoost");
|
||||
if (defenderBoostEntity.Valid()) {
|
||||
EntityWrapper active = defenderBoostEntity.FirstChildByName("Active");
|
||||
if (active.HasComponent("Text")) {
|
||||
if (defenderBoost.Valid()) {
|
||||
(Field<bool>)active["Text"]["Visible"] = true;
|
||||
} else {
|
||||
(Field<bool>)active["Text"]["Visible"] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EntityWrapper sniperBoostEntity = friendlyBoostHud.FirstChildByName("SniperBoost");
|
||||
if (sniperBoostEntity.Valid()) {
|
||||
EntityWrapper active = sniperBoostEntity.FirstChildByName("Active");
|
||||
if (active.HasComponent("Text")) {
|
||||
if (sniperBoost.Valid()) {
|
||||
(Field<bool>)active["Text"]["Visible"] = true;
|
||||
} else {
|
||||
(Field<bool>)active["Text"]["Visible"] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,543 @@
|
||||
#include "Systems/Weapon/SidearmWeaponBehaviour.h"
|
||||
|
||||
void SidearmWeaponBehaviour::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cWeapon, double dt)
|
||||
{
|
||||
Field<double> cooldown = cWeapon["FireCooldown"];
|
||||
if (cooldown > 0) {
|
||||
cooldown -= dt;
|
||||
if (cooldown < 0) {
|
||||
cooldown = 0;
|
||||
}
|
||||
}
|
||||
WeaponBehaviour::UpdateComponent(entity, cWeapon, dt);
|
||||
}
|
||||
|
||||
void SidearmWeaponBehaviour::UpdateWeapon(ComponentWrapper cWeapon, WeaponInfo& wi, double dt)
|
||||
{
|
||||
CheckAmmo(cWeapon, wi);
|
||||
|
||||
// Start reloading automatically if at 0 mag ammo
|
||||
Field<int> magAmmo = cWeapon["MagazineAmmo"];
|
||||
if (m_ConfigAutoReload && magAmmo <= 0) {
|
||||
OnReload(cWeapon, wi);
|
||||
}
|
||||
|
||||
// Only start reloading once we're done firing
|
||||
Field<bool> reloadQueued = cWeapon["ReloadQueued"];
|
||||
Field<double> fireCooldown = cWeapon["FireCooldown"];
|
||||
Field<bool> isReloading = cWeapon["IsReloading"];
|
||||
if (reloadQueued && fireCooldown <= 0) {
|
||||
reloadQueued = fireCooldown;
|
||||
isReloading = true;
|
||||
}
|
||||
|
||||
// Decrement reload timer
|
||||
Field<double> reloadTimer = cWeapon["ReloadTimer"];
|
||||
if (isReloading) {
|
||||
reloadTimer = glm::max(0.0, reloadTimer - dt);
|
||||
}
|
||||
|
||||
// Handle reloading
|
||||
if (isReloading && reloadTimer <= 0.0) {
|
||||
Field<int> magSize = cWeapon["MagazineSize"];
|
||||
|
||||
magAmmo = magSize;
|
||||
isReloading = false;
|
||||
if (wi.FirstPersonEntity.Valid()) {
|
||||
wi.FirstPersonEntity.FirstChildByName("ViewModel")["Model"]["Visible"] = true;
|
||||
}
|
||||
if (wi.ThirdPersonEntity.Valid()) {
|
||||
wi.ThirdPersonEntity["Model"]["Visible"] = true;
|
||||
}
|
||||
}
|
||||
double reloadTime = cWeapon["ReloadTime"];
|
||||
if (isReloading && reloadTimer <= reloadTime / 2) {
|
||||
|
||||
}
|
||||
|
||||
// Update first person run animation
|
||||
ComponentWrapper cPlayer = wi.Player["Player"];
|
||||
ComponentWrapper cPhysics = wi.Player["Physics"];
|
||||
const float& movementSpeed = cPlayer["MovementSpeed"];
|
||||
float speed = glm::length((const glm::vec3&)cPhysics["Velocity"]);
|
||||
float animationWeight = glm::min(speed, movementSpeed) / movementSpeed;
|
||||
EntityWrapper rootNode = wi.FirstPersonEntity;
|
||||
if (rootNode.Valid()) {
|
||||
EntityWrapper blend = rootNode.FirstChildByName("MovementBlendSidearm");
|
||||
if (blend.Valid()) {
|
||||
(Field<double>)blend["Blend"]["Weight"] = animationWeight;
|
||||
}
|
||||
}
|
||||
|
||||
if ((bool)cWeapon["Automatic"] && canFire(cWeapon)) {
|
||||
fireBullet(cWeapon, wi);
|
||||
}
|
||||
}
|
||||
|
||||
void SidearmWeaponBehaviour::OnPrimaryFire(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
cWeapon["TriggerHeld"] = true;
|
||||
if (canFire(cWeapon)) {
|
||||
fireBullet(cWeapon, wi);
|
||||
}
|
||||
}
|
||||
|
||||
void SidearmWeaponBehaviour::OnCeasePrimaryFire(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
cWeapon["TriggerHeld"] = false;
|
||||
}
|
||||
|
||||
|
||||
void SidearmWeaponBehaviour::OnReload(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
Field<bool> reloadQueued = cWeapon["ReloadQueued"];
|
||||
Field<bool> isReloading = cWeapon["IsReloading"];
|
||||
if (reloadQueued || isReloading) {
|
||||
return;
|
||||
}
|
||||
|
||||
Field<int> magAmmo = cWeapon["MagazineAmmo"];
|
||||
Field<int> magSize = cWeapon["MagazineSize"];
|
||||
if (magAmmo >= magSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
double reloadTime = cWeapon["ReloadTime"];
|
||||
Field<double> reloadTimer = cWeapon["ReloadTimer"];
|
||||
|
||||
// Start reload
|
||||
reloadQueued = true;
|
||||
reloadTimer = reloadTime;
|
||||
|
||||
// Play animation
|
||||
playAnimationAndReturn(wi.FirstPersonEntity, "ActionBlend", "Reload");
|
||||
// Third person anim
|
||||
Events::AutoAnimationBlend eReloadBlend;
|
||||
eReloadBlend.RootNode = wi.ThirdPersonPlayerModel;
|
||||
eReloadBlend.NodeName = "Reload";
|
||||
eReloadBlend.Restart = true;
|
||||
eReloadBlend.Start = true;
|
||||
m_EventBroker->Publish(eReloadBlend);
|
||||
|
||||
// Spawn explosion effect
|
||||
if (wi.FirstPersonEntity.Valid()) {
|
||||
if (IsClient) {
|
||||
EntityWrapper reloadEffectSpawner = wi.FirstPersonEntity.FirstChildByName("ReloadSpawner");
|
||||
if (reloadEffectSpawner.Valid()) {
|
||||
reloadEffectSpawner.DeleteChildren();
|
||||
SpawnerSystem::Spawn(reloadEffectSpawner, reloadEffectSpawner);
|
||||
}
|
||||
}
|
||||
wi.FirstPersonEntity.FirstChildByName("ViewModel")["Model"]["Visible"] = false;
|
||||
}
|
||||
if (wi.ThirdPersonEntity.Valid()) {
|
||||
if (IsServer) {
|
||||
EntityWrapper reloadEffectSpawner = wi.ThirdPersonEntity.FirstChildByName("ReloadSpawner");
|
||||
if (reloadEffectSpawner.Valid()) {
|
||||
reloadEffectSpawner.DeleteChildren();
|
||||
SpawnerSystem::Spawn(reloadEffectSpawner, reloadEffectSpawner);
|
||||
}
|
||||
}
|
||||
wi.ThirdPersonEntity["Model"]["Visible"] = false;
|
||||
}
|
||||
|
||||
// Sound
|
||||
Events::PlaySoundOnEntity e;
|
||||
e.Emitter = wi.Player;
|
||||
e.FilePath = "Audio/weapon/Assault/AssaultWeaponReload.wav";
|
||||
m_EventBroker->Publish(e);
|
||||
}
|
||||
|
||||
void SidearmWeaponBehaviour::OnEquip(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
cWeapon["FireCooldown"] = (double)cWeapon["EquipTime"];
|
||||
|
||||
if(wi.ThirdPersonPlayerModel.Valid()) {
|
||||
Events::AutoAnimationBlend b1;
|
||||
b1.RootNode = wi.ThirdPersonPlayerModel;
|
||||
b1.NodeName = "SidearmWeapon";
|
||||
b1.SingleLevelBlend = true;
|
||||
m_EventBroker->Publish(b1);
|
||||
}
|
||||
}
|
||||
|
||||
void SidearmWeaponBehaviour::OnHolster(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
// Make sure the trigger is released if weapon is holstered while firing
|
||||
cWeapon["TriggerHeld"] = false;
|
||||
|
||||
// Cancel any reload
|
||||
cWeapon["IsReloading"] = false;
|
||||
cWeapon["ReloadTimer"] = 0.0;
|
||||
}
|
||||
|
||||
void SidearmWeaponBehaviour::fireBullet(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
cWeapon["FireCooldown"] = 60.0 / (double)cWeapon["RPM"];
|
||||
|
||||
// Ammo
|
||||
Field<int> magAmmo = cWeapon["MagazineAmmo"];
|
||||
if (magAmmo <= 0) {
|
||||
return;
|
||||
} else {
|
||||
magAmmo -= 1;
|
||||
}
|
||||
|
||||
// Get weapon model based on current person
|
||||
EntityWrapper weaponModelEntity = getRelevantWeaponEntity(wi);
|
||||
if (!weaponModelEntity.Valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Tracer
|
||||
EntityWrapper tracerSpawner = weaponModelEntity.FirstChildByName("WeaponMuzzleRay");
|
||||
if (tracerSpawner.Valid()) {
|
||||
glm::vec3 origin = TransformSystem::AbsolutePosition(tracerSpawner);
|
||||
glm::vec3 direction = TransformSystem::AbsoluteOrientation(tracerSpawner) * glm::vec3(0, 0, -1);
|
||||
float distance = traceRayDistance(origin, direction);
|
||||
EntityWrapper ray = SpawnerSystem::Spawn(tracerSpawner, tracerSpawner);
|
||||
if (ray.Valid()) {
|
||||
((Field<glm::vec3>)ray["Transform"]["Scale"]).z(distance);
|
||||
}
|
||||
}
|
||||
|
||||
//Flash
|
||||
EntityWrapper flashSpawner = weaponModelEntity.FirstChildByName("WeaponMuzzleFlash");
|
||||
if (flashSpawner.Valid()) {
|
||||
std::uniform_real_distribution<float> randomSpreadAngle(0.f, 3.1415f*2);
|
||||
EntityWrapper flash = SpawnerSystem::Spawn(flashSpawner, flashSpawner);
|
||||
if(flash.Valid()){
|
||||
((Field<glm::vec3>)flash["Transform"]["Orientation"]).z(randomSpreadAngle(m_RandomEngine));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Deal damage
|
||||
if (dealDamage(cWeapon, wi)) {
|
||||
// Show hit marker
|
||||
EntityWrapper hitMarkerSpawner = wi.Player.FirstChildByName("HitMarkerSpawner");
|
||||
if (hitMarkerSpawner.Valid()) {
|
||||
SpawnerSystem::Spawn(hitMarkerSpawner, hitMarkerSpawner);
|
||||
Events::PlaySoundOnEntity e;
|
||||
e.Emitter = wi.Player;
|
||||
e.FilePath = "Audio/weapon/hitclick.wav";
|
||||
m_EventBroker->Publish(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Play animation
|
||||
playAnimationAndReturn(wi.FirstPersonEntity, "ActionBlend", "Fire");
|
||||
|
||||
// Third person anim
|
||||
if (wi.ThirdPersonPlayerModel.Valid()) {
|
||||
Events::AutoAnimationBlend b1;
|
||||
b1.RootNode = wi.ThirdPersonPlayerModel;
|
||||
b1.NodeName = "Fire";
|
||||
b1.Restart = true;
|
||||
b1.Start = true;
|
||||
m_EventBroker->Publish(b1);
|
||||
}
|
||||
}
|
||||
|
||||
bool SidearmWeaponBehaviour::canFire(ComponentWrapper cWeapon)
|
||||
{
|
||||
bool triggerHeld = cWeapon["TriggerHeld"];
|
||||
bool cooldownPassed = (double)cWeapon["FireCooldown"] <= 0.0;
|
||||
bool isNotReloading = !(bool)cWeapon["IsReloading"];
|
||||
return triggerHeld && cooldownPassed && isNotReloading;
|
||||
}
|
||||
|
||||
bool SidearmWeaponBehaviour::dealDamage(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
// Only deal damage client side
|
||||
if (!IsClient) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only handle damage for the local player
|
||||
if (wi.Player != LocalPlayer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure the player isn't shooting from the grave
|
||||
if (!wi.Player.Valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3D-pick middle of screen
|
||||
Rectangle viewport = m_Renderer->GetViewportSize();
|
||||
glm::vec2 centerScreen(viewport.Width / 2, viewport.Height / 2);
|
||||
// TODO: Some horizontal spread
|
||||
PickData pickData = m_Renderer->Pick(centerScreen);
|
||||
EntityWrapper victim(m_World, pickData.Entity);
|
||||
if (!victim.Valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't let us somehow shoot ourselves in the foot
|
||||
if (victim == LocalPlayer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only care about players being hit
|
||||
if (!victim.HasComponent("Player")) {
|
||||
victim = victim.FirstParentWithComponent("Player");
|
||||
}
|
||||
if (!victim.Valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
double damage = cWeapon["BaseDamage"];
|
||||
// If friendly fire, reduce damage to 0 (needed to make Boosts, Ammosharing work)
|
||||
if ((ComponentInfo::EnumType)victim["Team"]["Team"] == (ComponentInfo::EnumType)wi.Player["Team"]["Team"]) {
|
||||
damage = 0;
|
||||
|
||||
bool gaveAmmo = false;
|
||||
|
||||
if (victim.HasComponent("AssaultWeapon")) {
|
||||
int magazineSize = victim["AssaultWeapon"]["MagazineSize"];
|
||||
int magazineAmmo = victim["AssaultWeapon"]["MagazineAmmo"];
|
||||
|
||||
int maxAmmo = victim["AssaultWeapon"]["MaxAmmo"];
|
||||
int ammo = victim["AssaultWeapon"]["Ammo"];
|
||||
|
||||
if (magazineAmmo < magazineSize) {
|
||||
gaveAmmo = true;
|
||||
} else if (ammo < maxAmmo) {
|
||||
gaveAmmo = true;
|
||||
}
|
||||
} else if (victim.HasComponent("DefenderWeapon")) {
|
||||
int magazineSize = victim["DefenderWeapon"]["MagazineSize"];
|
||||
int magazineAmmo = victim["DefenderWeapon"]["MagazineAmmo"];
|
||||
|
||||
int maxAmmo = victim["DefenderWeapon"]["MaxAmmo"];
|
||||
int ammo = victim["DefenderWeapon"]["Ammo"];
|
||||
|
||||
if (magazineAmmo < magazineSize) {
|
||||
gaveAmmo = true;
|
||||
} else if (ammo < maxAmmo) {
|
||||
gaveAmmo = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (gaveAmmo) {
|
||||
EntityWrapper effectSpawner = wi.FirstPersonEntity.FirstChildByName("AmmoShareEffectSpawner");
|
||||
if (effectSpawner.Valid()) {
|
||||
if (effectSpawner.HasComponent("Spawner")) {
|
||||
SpawnerSystem::Spawn(effectSpawner, effectSpawner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spawnTracer(cWeapon, wi);
|
||||
|
||||
// Deal damage!
|
||||
Events::PlayerDamage ePlayerDamage;
|
||||
ePlayerDamage.Inflictor = wi.Player;
|
||||
ePlayerDamage.Victim = victim;
|
||||
ePlayerDamage.Damage = damage;
|
||||
m_EventBroker->Publish(ePlayerDamage);
|
||||
|
||||
return damage > 0;
|
||||
}
|
||||
|
||||
void SidearmWeaponBehaviour::spawnTracer(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
EntityWrapper muzzle = getRelevantWeaponEntity(wi).FirstChildByName("WeaponMuzzle");
|
||||
if (!muzzle.Valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
EntityWrapper camera = wi.Player.FirstChildByName("Camera");
|
||||
if (!camera.Valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
glm::vec3 cameraPosition = TransformSystem::AbsolutePosition(camera);
|
||||
glm::vec3 direction = glm::vec3(0, 0, -1);
|
||||
|
||||
float distance;
|
||||
glm::vec3 hitPosition;
|
||||
Collision::EntityFirstHitByRay(Ray(cameraPosition, direction), m_CollisionOctree, distance, hitPosition);
|
||||
|
||||
EntityWrapper ray = SpawnerSystem::Spawn(muzzle);
|
||||
if (ray.Valid()) {
|
||||
ComponentWrapper cTransform = ray["Transform"];
|
||||
Field<glm::vec3> rayOrigin = cTransform["Position"];
|
||||
Field<glm::vec3> rayOrientation = cTransform["Orientation"];
|
||||
Field<glm::vec3> rayScale = cTransform["Scale"];
|
||||
|
||||
glm::vec3 muzzlePosition = TransformSystem::AbsolutePosition(muzzle);
|
||||
glm::quat muzzleOrientation = TransformSystem::AbsoluteOrientation(muzzle);
|
||||
|
||||
rayOrigin = muzzlePosition;
|
||||
|
||||
glm::vec3 muzzleToHit = hitPosition - muzzlePosition;
|
||||
glm::vec3 lookVector = glm::normalize(-muzzleToHit);
|
||||
float pitch = std::asin(-lookVector.y);
|
||||
float yaw = std::atan2(lookVector.x, lookVector.z);
|
||||
glm::quat orientation = glm::quat(glm::vec3(pitch, yaw, 0));
|
||||
rayOrientation = glm::eulerAngles(orientation);
|
||||
rayScale.z(glm::length(muzzleToHit));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
void SidearmWeaponBehaviour::giveAmmo(ComponentWrapper cWeapon, WeaponInfo& wi, EntityWrapper receiver)
|
||||
{
|
||||
bool gaveAmmo = false;
|
||||
|
||||
if (receiver.HasComponent("AssaultWeapon")) {
|
||||
int magazineSize = receiver["AssaultWeapon"]["MagazineSize"];
|
||||
int& magazineAmmo = receiver["AssaultWeapon"]["MagazineAmmo"];
|
||||
|
||||
int maxAmmo = receiver["AssaultWeapon"]["MaxAmmo"];
|
||||
int& ammo = receiver["AssaultWeapon"]["Ammo"];
|
||||
|
||||
if(magazineAmmo < magazineSize) {
|
||||
magazineAmmo += 1;
|
||||
gaveAmmo = true;
|
||||
} else if (ammo < maxAmmo) {
|
||||
ammo += 1;
|
||||
gaveAmmo = true;
|
||||
}
|
||||
} else if (receiver.HasComponent("DefenderWeapon")) {
|
||||
int magazineSize = receiver["DefenderWeapon"]["MagazineSize"];
|
||||
int& magazineAmmo = receiver["DefenderWeapon"]["MagazineAmmo"];
|
||||
|
||||
int maxAmmo = receiver["DefenderWeapon"]["MaxAmmo"];
|
||||
int& ammo = receiver["DefenderWeapon"]["Ammo"];
|
||||
|
||||
if (magazineAmmo < magazineSize) {
|
||||
magazineAmmo += 1;
|
||||
gaveAmmo = true;
|
||||
} else if (ammo < maxAmmo) {
|
||||
ammo += 1;
|
||||
gaveAmmo = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(gaveAmmo) {
|
||||
EntityWrapper effectSpawner = wi.FirstPersonEntity.FirstChildByName("AmmoShareEffectSpawner");
|
||||
if(effectSpawner.Valid()) {
|
||||
if (effectSpawner.HasComponent("Spawner")) {
|
||||
SpawnerSystem::Spawn(effectSpawner, effectSpawner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
void SidearmWeaponBehaviour::CheckAmmo(ComponentWrapper cWeapon, WeaponInfo& wi)
|
||||
{
|
||||
// Only check ammo client side
|
||||
if (!IsClient) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only handle ammo check for the local player
|
||||
if (wi.Player != LocalPlayer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the player isn't checking from the grave
|
||||
if (!wi.Player.Valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 3D-pick middle of screen
|
||||
Rectangle viewport = m_Renderer->GetViewportSize();
|
||||
glm::vec2 centerScreen(viewport.Width / 2, viewport.Height / 2);
|
||||
// TODO: Some horizontal spread
|
||||
PickData pickData = m_Renderer->Pick(centerScreen);
|
||||
EntityWrapper victim(m_World, pickData.Entity);
|
||||
if (!victim.Valid()) {
|
||||
RemoveFrindlyAmmoHUD(wi);
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't let us somehow shoot ourselves in the foot
|
||||
if (victim == LocalPlayer) {
|
||||
RemoveFrindlyAmmoHUD(wi);
|
||||
return;
|
||||
}
|
||||
|
||||
// Only care about players being hit
|
||||
if (!victim.HasComponent("Player")) {
|
||||
victim = victim.FirstParentWithComponent("Player");
|
||||
}
|
||||
if (!victim.Valid()) {
|
||||
RemoveFrindlyAmmoHUD(wi);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// If friendly fire, reduce damage to 0 (needed to make Boosts, Ammosharing work)
|
||||
if ((ComponentInfo::EnumType)victim["Team"]["Team"] == (ComponentInfo::EnumType)wi.Player["Team"]["Team"]) {
|
||||
int magazineAmmo = 0;
|
||||
int ammo = 0;
|
||||
if (victim.HasComponent("AssaultWeapon")) {
|
||||
magazineAmmo = (int)victim["AssaultWeapon"]["MagazineAmmo"];
|
||||
ammo = (int)victim["AssaultWeapon"]["Ammo"];
|
||||
|
||||
} else if (victim.HasComponent("DefenderWeapon")) {
|
||||
magazineAmmo = (int)victim["DefenderWeapon"]["MagazineAmmo"];
|
||||
ammo = (int)victim["DefenderWeapon"]["Ammo"];
|
||||
}
|
||||
|
||||
|
||||
EntityWrapper friendlyAmmoHudSpawner = wi.FirstPersonPlayerModel.FirstChildByName("FriendlyAmmoAttachment");
|
||||
if (friendlyAmmoHudSpawner.Valid()) {
|
||||
|
||||
auto children = m_World->GetDirectChildren(friendlyAmmoHudSpawner.ID);
|
||||
|
||||
if (children.first == children.second) {
|
||||
if (friendlyAmmoHudSpawner.HasComponent("Spawner")) {
|
||||
EntityWrapper friendlyAmmoHud = SpawnerSystem::Spawn(friendlyAmmoHudSpawner, friendlyAmmoHudSpawner);
|
||||
if (friendlyAmmoHud.Valid()) {
|
||||
EntityWrapper textEntity = friendlyAmmoHud.FirstChildByName("MagazineAmmo");
|
||||
if (textEntity.Valid()) {
|
||||
if (textEntity.HasComponent("Text")) {
|
||||
(Field<std::string>)textEntity["Text"]["Content"] = std::to_string(magazineAmmo);
|
||||
}
|
||||
}
|
||||
|
||||
EntityWrapper ammoTextEntity = friendlyAmmoHud.FirstChildByName("Ammo");
|
||||
if (ammoTextEntity.Valid()) {
|
||||
if (ammoTextEntity.HasComponent("Text")) {
|
||||
(Field<std::string>)ammoTextEntity["Text"]["Content"] = std::to_string(ammo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
EntityWrapper textEntity = friendlyAmmoHudSpawner.FirstChildByName("MagazineAmmo");
|
||||
if (textEntity.Valid()) {
|
||||
if (textEntity.HasComponent("Text")) {
|
||||
(Field<std::string>)textEntity["Text"]["Content"] = std::to_string(magazineAmmo);
|
||||
}
|
||||
}
|
||||
|
||||
EntityWrapper ammoTextEntity = friendlyAmmoHudSpawner.FirstChildByName("Ammo");
|
||||
if (ammoTextEntity.Valid()) {
|
||||
if (ammoTextEntity.HasComponent("Text")) {
|
||||
(Field<std::string>)ammoTextEntity["Text"]["Content"] = std::to_string(ammo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SidearmWeaponBehaviour::RemoveFrindlyAmmoHUD(WeaponInfo& wi)
|
||||
{
|
||||
EntityWrapper friendlyAmmoHudSpawner = wi.FirstPersonPlayerModel.FirstChildByName("FriendlyAmmoAttachment");
|
||||
if (friendlyAmmoHudSpawner.Valid()) {
|
||||
friendlyAmmoHudSpawner.DeleteChildren();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user