#ifndef WeaponSystem_h__ #define WeaponSystem_h__ //#include //#include #include "Rendering/IRenderer.h" #include "Common.h" #include "Core/System.h" #include "Core/EPlayerDamage.h" #include "Core/EShoot.h" #include "Core/EPlayerSpawned.h" #include "Input/EInputCommand.h" #include "Core/EntityFile.h" #include "Core/EntityFileParser.h" #include "Core/Octree.h" #include "Collision/EntityAABB.h" class WeaponBehaviour; class WeaponSystem : public PureSystem, ImpureSystem { public: WeaponSystem(SystemParams params, IRenderer* renderer, Octree* collisionOctree); virtual void Update(double dt) override; virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& cPlayer, double dt) override; private: SystemParams m_SystemParams; IRenderer* m_Renderer; Octree* m_CollisionOctree; std::unordered_map> m_ActiveWeapons; // Events EventRelay m_EPlayerSpawned; bool OnPlayerSpawned(Events::PlayerSpawned& e); EventRelay m_EShoot; bool OnShoot(Events::Shoot& e); EventRelay m_EInputCommand; bool OnInputCommand(Events::InputCommand& e); void selectWeapon(EntityWrapper player, ComponentInfo::EnumType slot); }; class WeaponBehaviour : protected System { public: WeaponBehaviour(SystemParams systemParams, Octree* collisionOctree, EntityWrapper 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 CeaseFire() { } virtual void Reload() { } virtual void Update(double dt) { } protected: Octree* m_CollisionOctree; EntityWrapper m_Entity; }; class AssaultWeaponBehaviour : public WeaponBehaviour { public: AssaultWeaponBehaviour(SystemParams systemParams, Octree* collisionOctree, EntityWrapper weaponEntity) : WeaponBehaviour(systemParams, collisionOctree, weaponEntity) { m_RayRed = ResourceManager::Load("Schema/Entities/RayRed.xml"); m_RayBlue = ResourceManager::Load("Schema/Entities/RayBlue.xml"); } virtual void Fire() override { m_TimeSinceLastFire = 0.0; m_Firing = true; fireRound(); } virtual void CeaseFire() override { 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 { if (!m_Firing) { return; } m_TimeSinceLastFire += dt; ComponentWrapper& cAssaultWeapon = m_Entity["AssaultWeapon"]; if (m_TimeSinceLastFire >= 1.0 / ((double)cAssaultWeapon["RPM"] / 60.0)) { fireRound(); } } private: bool m_Firing = false; double m_TimeSinceLastFire = 0.0; EntityFile* m_RayRed = nullptr; EntityFile* m_RayBlue = nullptr; void fireRound() { ComponentWrapper& cAssaultWeapon = m_Entity["AssaultWeapon"]; int& magAmmo = cAssaultWeapon["MagazineAmmo"]; int ammo = cAssaultWeapon["Ammo"]; // Reload if our magazine is empty if (magAmmo <= 0) { Reload(); return; } // Fire magAmmo -= 1; spawnTracer(); 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