This commit is contained in:
2016-02-10 01:16:38 +01:00
parent 5cea3bed3a
commit d931660c56
13 changed files with 181 additions and 100 deletions
+1 -1
View File
@@ -29,7 +29,7 @@ struct EntityWrapper
EntityWrapper FirstChildByName(const std::string& name); EntityWrapper FirstChildByName(const std::string& name);
EntityWrapper FirstParentWithComponent(const std::string& componentType); EntityWrapper FirstParentWithComponent(const std::string& componentType);
bool IsChildOf(EntityWrapper potentialParent); bool IsChildOf(EntityWrapper potentialParent);
bool Valid(); bool Valid() const;
ComponentWrapper operator[](const char* componentName); ComponentWrapper operator[](const char* componentName);
bool operator==(const EntityWrapper& e) const; bool operator==(const EntityWrapper& e) const;
+2 -1
View File
@@ -5,6 +5,7 @@
#include <functional> #include <functional>
#include <list> #include <list>
#include <tuple> #include <tuple>
#include <set>
#include "../Common.h" #include "../Common.h"
#include "Event.h" #include "Event.h"
@@ -107,7 +108,7 @@ private:
typedef std::unordered_map<ContextTypeName_t, EventRelays_t> ContextRelays_t; typedef std::unordered_map<ContextTypeName_t, EventRelays_t> ContextRelays_t;
ContextRelays_t m_ContextRelays; ContextRelays_t m_ContextRelays;
std::vector<BaseEventRelay*> m_RelaysToSubscribe; std::vector<BaseEventRelay*> m_RelaysToSubscribe;
std::vector<std::tuple<EventID, ContextTypeName_t, EventTypeName_t>> m_RelaysToUnsubscribe; std::unordered_map<BaseEventRelay*, std::tuple<EventID, ContextTypeName_t, EventTypeName_t>> m_RelaysToUnsubscribe;
typedef std::list<std::pair<EventTypeName_t, std::shared_ptr<Event>>> EventQueue_t; typedef std::list<std::pair<EventTypeName_t, std::shared_ptr<Event>>> EventQueue_t;
std::shared_ptr<EventQueue_t> m_EventQueueRead; std::shared_ptr<EventQueue_t> m_EventQueueRead;
+1 -3
View File
@@ -5,9 +5,7 @@
#include "../Common.h" #include "../Common.h"
#include "AABB.h" #include "AABB.h"
#include "Ray.h"
//Fwd declarations.
class Ray;
namespace OctSpace namespace OctSpace
{ {
+1 -1
View File
@@ -68,7 +68,7 @@ protected:
const std::string m_ComponentType; const std::string m_ComponentType;
virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& cComponent, double dt) = 0; virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& cPlayer, double dt) = 0;
}; };
class ImpureSystem : public virtual System class ImpureSystem : public virtual System
+116 -13
View File
@@ -13,17 +13,23 @@
#include "Input/EInputCommand.h" #include "Input/EInputCommand.h"
#include "Core/EntityFile.h" #include "Core/EntityFile.h"
#include "Core/EntityFileParser.h" #include "Core/EntityFileParser.h"
#include "Core/Octree.h"
#include "Collision/EntityAABB.h"
class WeaponBehaviour;
class WeaponSystem : public PureSystem, ImpureSystem class WeaponSystem : public PureSystem, ImpureSystem
{ {
public: public:
WeaponSystem(SystemParams params, IRenderer* renderer); WeaponSystem(SystemParams params, IRenderer* renderer, Octree<EntityAABB>* collisionOctree);
virtual void Update(double dt) override; virtual void Update(double dt) override;
virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& cComponent, double dt) override; virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& cPlayer, double dt) override;
private: private:
SystemParams m_SystemParams;
IRenderer* m_Renderer; IRenderer* m_Renderer;
Octree<EntityAABB>* m_CollisionOctree;
std::unordered_map<EntityWrapper, std::shared_ptr<WeaponBehaviour>> m_ActiveWeapons; std::unordered_map<EntityWrapper, std::shared_ptr<WeaponBehaviour>> m_ActiveWeapons;
@@ -38,12 +44,18 @@ private:
void selectWeapon(EntityWrapper player, ComponentInfo::EnumType slot); void selectWeapon(EntityWrapper player, ComponentInfo::EnumType slot);
}; };
class WeaponBehaviour class WeaponBehaviour : protected System
{ {
public: public:
WeaponBehaviour(EntityWrapper weaponEntity) WeaponBehaviour(SystemParams systemParams, Octree<EntityAABB>* collisionOctree, EntityWrapper weaponEntity)
: m_Entity(weaponEntity) : System(systemParams)
, m_CollisionOctree(collisionOctree)
, m_Entity(weaponEntity)
{ } { }
virtual ~WeaponBehaviour() = default;
WeaponBehaviour(const WeaponBehaviour&) = delete;
WeaponBehaviour& operator=(const WeaponBehaviour &) = delete;
virtual void Fire() = 0; virtual void Fire() = 0;
virtual void CeaseFire() { } virtual void CeaseFire() { }
@@ -51,15 +63,19 @@ public:
virtual void Update(double dt) { } virtual void Update(double dt) { }
protected: protected:
Octree<EntityAABB>* m_CollisionOctree;
EntityWrapper m_Entity; EntityWrapper m_Entity;
}; };
class AssaultWeaponBehaviour : public WeaponBehaviour class AssaultWeaponBehaviour : public WeaponBehaviour
{ {
public: public:
AssaultWeaponBehaviour(EntityWrapper weaponEntity) AssaultWeaponBehaviour(SystemParams systemParams, Octree<EntityAABB>* collisionOctree, EntityWrapper weaponEntity)
: WeaponBehaviour(weaponEntity) : WeaponBehaviour(systemParams, collisionOctree, weaponEntity)
{ } {
m_RayRed = ResourceManager::Load<EntityFile>("Schema/Entities/RayRed.xml");
m_RayBlue = ResourceManager::Load<EntityFile>("Schema/Entities/RayBlue.xml");
}
virtual void Fire() override virtual void Fire() override
{ {
@@ -73,6 +89,25 @@ public:
m_Firing = false; m_Firing = false;
} }
virtual void Reload() override
{
ComponentWrapper& cAssaultWeapon = m_Entity["AssaultWeapon"];
int& magAmmo = cAssaultWeapon["MagazineAmmo"];
int magSize = cAssaultWeapon["MagazineSize"];
int& ammo = cAssaultWeapon["Ammo"];
// Don't reload if we're already fully loaded
if (magAmmo == magSize) {
return;
}
// Throw away rounds in magazine to incentivise ammo sharing
int toLoad = glm::min(magSize, ammo);
magAmmo = toLoad;
ammo -= toLoad;
}
virtual void Update(double dt) override virtual void Update(double dt) override
{ {
if (!m_Firing) { if (!m_Firing) {
@@ -82,7 +117,7 @@ public:
m_TimeSinceLastFire += dt; m_TimeSinceLastFire += dt;
ComponentWrapper& cAssaultWeapon = m_Entity["AssaultWeapon"]; ComponentWrapper& cAssaultWeapon = m_Entity["AssaultWeapon"];
if (m_TimeSinceLastFire > (double)cAssaultWeapon["RPM"] / 60.0) { if (m_TimeSinceLastFire >= 1.0 / ((double)cAssaultWeapon["RPM"] / 60.0)) {
fireRound(); fireRound();
} }
} }
@@ -90,7 +125,8 @@ public:
private: private:
bool m_Firing = false; bool m_Firing = false;
double m_TimeSinceLastFire = 0.0; double m_TimeSinceLastFire = 0.0;
ComponentWrapper m_Component; EntityFile* m_RayRed = nullptr;
EntityFile* m_RayBlue = nullptr;
void fireRound() void fireRound()
{ {
@@ -99,18 +135,85 @@ private:
int& magAmmo = cAssaultWeapon["MagazineAmmo"]; int& magAmmo = cAssaultWeapon["MagazineAmmo"];
int ammo = cAssaultWeapon["Ammo"]; int ammo = cAssaultWeapon["Ammo"];
// Reload if our magazine is empty and we have ammo to fill it with // Reload if our magazine is empty
if (magAmmo <= 0 && ammo > 0) { if (magAmmo <= 0) {
CeaseFire();
Reload(); Reload();
return; return;
} }
// Fire // Fire
magAmmo -= 1; magAmmo -= 1;
spawnTracer();
m_TimeSinceLastFire = 0.0; m_TimeSinceLastFire = 0.0;
} }
void spawnTracer()
{
ComponentWrapper cTeam = m_Entity["Team"];
ComponentInfo::EnumType team = cTeam["Team"];
// Select the right color of effect
EntityFile* rayFile = nullptr;
if (team == cTeam["Team"].Enum("Red")) {
rayFile = m_RayRed;
}
if (team == cTeam["Team"].Enum("Blue")) {
rayFile = m_RayBlue;
}
if (rayFile == nullptr) {
return;
}
// Create the entity
EntityFileParser parser(rayFile);
EntityID rayID = parser.MergeEntities(m_World);
EntityWrapper ray(m_World, rayID);
// Figure out where to put it
EntityWrapper attachment;
if (m_Entity == LocalPlayer || true) {
// Spawn the effect from the weapon view model for the local player
attachment = m_Entity.FirstChildByName("WeaponMuzzle");
}
// TODO: Spawn the effect from the weapon world model once it exists
glm::mat4 transformation = Transform::AbsoluteTransformation(attachment);
glm::vec3 _scale;
glm::vec3 translation;
glm::quat _orientation;
glm::vec3 _skew;
glm::vec4 _perspective;
glm::decompose(transformation, _scale, _orientation, translation, _skew, _perspective);
// Matrix to euler angles
glm::vec3 euler;
euler.y = glm::asin(-transformation[0][2]);
if (cos(euler.y) != 0) {
euler.x = atan2(transformation[1][2], transformation[2][2]);
euler.z = atan2(transformation[0][1], transformation[0][0]);
} else {
euler.x = atan2(-transformation[2][0], transformation[1][1]);
euler.z = 0;
}
// TODO: Spread?
(glm::vec3&)ray["Transform"]["Position"] = translation;
(glm::vec3&)ray["Transform"]["Orientation"] = euler;
glm::vec3& scale = ray["Transform"]["Scale"];
scale.z = traceRayDistance(translation, glm::quat(euler) * glm::vec3(0.f, 0.f, -1.f));
}
float traceRayDistance(glm::vec3 origin, glm::vec3 direction)
{
OctSpace::Output result;
if (m_CollisionOctree->RayCollides(Ray(origin, direction), result)) {
return result.CollideDistance;
} else {
return 0.f;
}
}
}; };
#endif #endif
@@ -4,5 +4,6 @@
<MagazineSize>32</MagazineSize> <MagazineSize>32</MagazineSize>
<Ammo>360</Ammo> <Ammo>360</Ammo>
<MaxAmmo>360</MaxAmmo> <MaxAmmo>360</MaxAmmo>
<RPM>40</RPM> <BaseDamage>5</BaseDamage>
<RPM>120</RPM>
</AssaultWeapon> </AssaultWeapon>
@@ -18,6 +18,7 @@
<xs:element name="MaxAmmo" type="t:int" minOccurs="0"> <xs:element name="MaxAmmo" type="t:int" minOccurs="0">
<xs:annotation><xs:documentation>Maximum ammo able to be carried</xs:documentation></xs:annotation> <xs:annotation><xs:documentation>Maximum ammo able to be carried</xs:documentation></xs:annotation>
</xs:element> </xs:element>
<xs:element name="BaseDamage" type="t:double" minOccurs="0"/>
<xs:element name="RPM" type="t:double" minOccurs="0"> <xs:element name="RPM" type="t:double" minOccurs="0">
<xs:annotation><xs:documentation>Rate of fire in rounds per minute</xs:documentation></xs:annotation> <xs:annotation><xs:documentation>Rate of fire in rounds per minute</xs:documentation></xs:annotation>
</xs:element> </xs:element>
+5 -2
View File
@@ -6,6 +6,9 @@
<Origin X="0" Y="0.772000015" Z="0"/> <Origin X="0" Y="0.772000015" Z="0"/>
<Size X="1" Y="1.60000002" Z="1"/> <Size X="1" Y="1.60000002" Z="1"/>
</c:AABB> </c:AABB>
<c:AssaultWeapon>
<RPM>600</RPM>
</c:AssaultWeapon>
<c:Collidable/> <c:Collidable/>
<c:DashAbility> <c:DashAbility>
<CoolDownMaxTimer>2</CoolDownMaxTimer> <CoolDownMaxTimer>2</CoolDownMaxTimer>
@@ -118,7 +121,7 @@
<Entity name="WeaponMuzzle"> <Entity name="WeaponMuzzle">
<Components> <Components>
<c:Transform> <c:Transform>
<Position X="0.215000004" Y="-0.153000012" Z="-0.266000003"/> <Position X="0.215000004" Y="-0.153000012" Z="-0.923000038"/>
</c:Transform> </c:Transform>
</Components> </Components>
<Children/> <Children/>
@@ -145,7 +148,7 @@
<Components> <Components>
<c:Animation> <c:Animation>
<Name>Hold Pos</Name> <Name>Hold Pos</Name>
<Time>0.79606322555778508</Time> <Time>0.86415395295813102</Time>
<Speed>1</Speed> <Speed>1</Speed>
</c:Animation> </c:Animation>
<c:HiddenForLocalPlayer/> <c:HiddenForLocalPlayer/>
+1 -2
View File
@@ -54,7 +54,7 @@ bool EntityWrapper::IsChildOf(EntityWrapper potentialParent)
return false; return false;
} }
bool EntityWrapper::Valid() bool EntityWrapper::Valid() const
{ {
if (this->World == nullptr) { if (this->World == nullptr) {
return false; return false;
@@ -65,7 +65,6 @@ bool EntityWrapper::Valid()
} }
if (!this->World->ValidEntity(this->ID)) { if (!this->World->ValidEntity(this->ID)) {
this->ID = EntityID_Invalid;
return false; return false;
} }
+8 -6
View File
@@ -10,10 +10,9 @@ BaseEventRelay::~BaseEventRelay()
void EventBroker::Unsubscribe(BaseEventRelay& relay) // ? void EventBroker::Unsubscribe(BaseEventRelay& relay) // ?
{ {
auto identifier = std::make_tuple(relay.m_EventID, relay.m_ContextTypeName, relay.m_EventTypeName); auto identifier = std::make_tuple(relay.m_EventID, relay.m_ContextTypeName, relay.m_EventTypeName);
relay.m_Broker = nullptr; relay.m_Broker = nullptr;
if (m_IsProcessing) { if (m_IsProcessing) {
m_RelaysToUnsubscribe.push_back(identifier); m_RelaysToUnsubscribe[&relay] = identifier;
} else { } else {
unsubscribeImmediate(identifier); unsubscribeImmediate(identifier);
} }
@@ -48,8 +47,11 @@ int EventBroker::Process(std::string contextTypeName)
for (auto it2 = itpair.first; it2 != itpair.second; it2++) { for (auto it2 = itpair.first; it2 != itpair.second; it2++) {
std::string name = it2->first; std::string name = it2->first;
BaseEventRelay* relay = it2->second; BaseEventRelay* relay = it2->second;
relay->Receive(event); if (m_RelaysToUnsubscribe.count(relay) != 0) {
eventsProcessed++; continue;
}
relay->Receive(event);
eventsProcessed++;
} }
} }
@@ -62,8 +64,8 @@ int EventBroker::Process(std::string contextTypeName)
m_RelaysToSubscribe.clear(); m_RelaysToSubscribe.clear();
// Process pending unsubscriptions // Process pending unsubscriptions
for (auto& identifier : m_RelaysToUnsubscribe) { for (auto& kv : m_RelaysToUnsubscribe) {
unsubscribeImmediate(identifier); unsubscribeImmediate(kv.second);
} }
m_RelaysToUnsubscribe.clear(); m_RelaysToUnsubscribe.clear();
+1 -1
View File
@@ -103,7 +103,7 @@ Game::Game(int argc, char* argv[])
m_SystemPipeline->AddSystem<PlayerMovementSystem>(updateOrderLevel); m_SystemPipeline->AddSystem<PlayerMovementSystem>(updateOrderLevel);
m_SystemPipeline->AddSystem<SpawnerSystem>(updateOrderLevel); m_SystemPipeline->AddSystem<SpawnerSystem>(updateOrderLevel);
m_SystemPipeline->AddSystem<PlayerSpawnSystem>(updateOrderLevel); m_SystemPipeline->AddSystem<PlayerSpawnSystem>(updateOrderLevel);
m_SystemPipeline->AddSystem<WeaponSystem>(updateOrderLevel, m_Renderer); m_SystemPipeline->AddSystem<WeaponSystem>(updateOrderLevel, m_Renderer, m_OctreeCollision);
m_SystemPipeline->AddSystem<LifetimeSystem>(updateOrderLevel); m_SystemPipeline->AddSystem<LifetimeSystem>(updateOrderLevel);
m_SystemPipeline->AddSystem<CapturePointSystem>(updateOrderLevel); m_SystemPipeline->AddSystem<CapturePointSystem>(updateOrderLevel);
// Populate Octree with collidables // Populate Octree with collidables
+3
View File
@@ -71,6 +71,9 @@ bool PlayerSpawnSystem::OnInputCommand(const Events::InputCommand& e)
bool PlayerSpawnSystem::OnPlayerSpawned(Events::PlayerSpawned& e) bool PlayerSpawnSystem::OnPlayerSpawned(Events::PlayerSpawned& e)
{ {
// When a player is actually spawned (since the actual spawning is handled on the server) // When a player is actually spawned (since the actual spawning is handled on the server)
if (!IsClient) {
return false;
}
// Check if a player already exists // Check if a player already exists
if (m_PlayerEntities.count(e.PlayerID) != 0) { if (m_PlayerEntities.count(e.PlayerID) != 0) {
+39 -69
View File
@@ -1,13 +1,15 @@
#include "Systems/WeaponSystem.h" #include "Systems/WeaponSystem.h"
WeaponSystem::WeaponSystem(SystemParams params, IRenderer* renderer) WeaponSystem::WeaponSystem(SystemParams params, IRenderer* renderer, Octree<EntityAABB>* collisionOctree)
: System(params) : System(params)
, PureSystem("Player") , PureSystem("Player")
, ImpureSystem() , m_SystemParams(params)
, m_Renderer(renderer) , m_Renderer(renderer)
, m_CollisionOctree(collisionOctree)
{ {
EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &WeaponSystem::OnInputCommand); EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &WeaponSystem::OnInputCommand);
EVENT_SUBSCRIBE_MEMBER(m_EShoot, &WeaponSystem::OnShoot); EVENT_SUBSCRIBE_MEMBER(m_EShoot, &WeaponSystem::OnShoot);
EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &WeaponSystem::OnPlayerSpawned);
} }
void WeaponSystem::Update(double dt) void WeaponSystem::Update(double dt)
@@ -15,41 +17,48 @@ void WeaponSystem::Update(double dt)
} }
void WeaponSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cComponent, double dt) void WeaponSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cPlayer, double dt)
{ {
// Update potential weapon behaviour for player
//auto it = m_ActiveWeapons.find(entity);
//if (it != m_ActiveWeapons.end()) {
// //if (it->first.Valid()) {
// it->second->Update(dt);
// //} else {
// // m_ActiveWeapons.erase(it);
// //}
//}
} }
bool WeaponSystem::OnInputCommand(Events::InputCommand& e) bool WeaponSystem::OnInputCommand(Events::InputCommand& e)
{ {
EntityWrapper player = e.Player;
if (e.PlayerID == -1) {
player = LocalPlayer;
}
// Make sure player is alive // Make sure player is alive
if (!e.Player.Valid()) { if (!player.Valid()) {
return false; return false;
} }
// Weapon selection // Weapon selection
if (e.Command == "SelectWeapon") { if (e.Command == "SelectWeapon") {
selectWeapon(e.Player, static_cast<ComponentInfo::EnumType>(e.Value)); if (e.Value != 0) {
} selectWeapon(player, static_cast<ComponentInfo::EnumType>(e.Value));
}
// Only shoot client-side! }
if (!IsClient) {
return false; // Fire
} if (e.Command == "PrimaryFire") {
if (m_ActiveWeapons.find(player) != m_ActiveWeapons.end()) {
// Only shoot if the player is alive auto weapon = m_ActiveWeapons.at(player);
if (!e.Player.Valid()) { if (e.Value > 0) {
return false; weapon->Fire();
} } else {
weapon->CeaseFire();
if (e.Command == "PrimaryFire" && e.Value > 0) { }
Events::Shoot eShoot;
if (e.PlayerID == -1) {
eShoot.Player = LocalPlayer;
} else {
eShoot.Player = e.Player;
} }
m_EventBroker->Publish(eShoot);
} }
return true; return true;
@@ -60,7 +69,11 @@ void WeaponSystem::selectWeapon(EntityWrapper player, ComponentInfo::EnumType sl
// Primary // Primary
if (slot == 1) { if (slot == 1) {
// TODO: if class... // TODO: if class...
m_ActiveWeapons[player] = std::make_shared<AssaultWeaponBehaviour>(); if (m_ActiveWeapons.count(player) == 0) {
m_ActiveWeapons.insert(std::make_pair(player, std::make_shared<AssaultWeaponBehaviour>(m_SystemParams, m_CollisionOctree, player)));
} else {
m_ActiveWeapons.erase(player);
}
} }
// Secondary // Secondary
@@ -83,49 +96,6 @@ bool WeaponSystem::OnShoot(Events::Shoot& eShoot)
return false; return false;
} }
// TODO: Weapon firing effects here
auto rayRed = ResourceManager::Load<EntityFile>("Schema/Entities/RayRed.xml");
auto rayBlue = ResourceManager::Load<EntityFile>("Schema/Entities/RayBlue.xml");
EntityWrapper weapon = eShoot.Player.FirstChildByName("WeaponMuzzle");
if (weapon.Valid()) {
EntityWrapper ray;
if ((ComponentInfo::EnumType)eShoot.Player["Team"]["Team"] == eShoot.Player["Team"]["Team"].Enum("Red")) {
EntityFileParser parser(rayRed);
EntityID rayID = parser.MergeEntities(m_World);
ray = EntityWrapper(m_World, rayID);
} else {
EntityFileParser parser(rayBlue);
EntityID rayID = parser.MergeEntities(m_World);
ray = EntityWrapper(m_World, rayID);
}
glm::mat4 transformation = Transform::AbsoluteTransformation(weapon);
glm::vec3 scale;
glm::vec3 translation;
glm::quat orientation;
glm::vec3 skew;
glm::vec4 perspective;
glm::decompose(transformation, scale, orientation, translation, skew, perspective);
// Matrix to euler angles
glm::vec3 euler;
euler.y = glm::asin(-transformation[0][2]);
if (cos(euler.y) != 0) {
euler.x = atan2(transformation[1][2], transformation[2][2]);
euler.z = atan2(transformation[0][1], transformation[0][0]);
} else {
euler.x = atan2(-transformation[2][0], transformation[1][1]);
euler.z = 0;
}
//LOG_DEBUG("rotation: %f %f %f", euler.x, euler.y, euler.z);
(glm::vec3&)ray["Transform"]["Position"] = translation;
(glm::vec3&)ray["Transform"]["Orientation"] = euler;
//(glm::vec3&)ray["Transform"]["Orientation"] = Transform::AbsoluteOrientationEuler(weapon);
}
// Only run further picking code for the local player! // Only run further picking code for the local player!
if (eShoot.Player != LocalPlayer) { if (eShoot.Player != LocalPlayer) {
return false; return false;