diff --git a/include/Engine/Collision/Collision.h b/include/Engine/Collision/Collision.h index 5b4150d8..546d03f5 100644 --- a/include/Engine/Collision/Collision.h +++ b/include/Engine/Collision/Collision.h @@ -90,10 +90,10 @@ boost::optional AbsoluteAABBExplosionEffect(EntityWrapper& entity); //Returns the first entity hit by the input ray. entitiesPotentiallyHitSorted needs to be sorted //by their distance to the ray, e.g. result from Octree::ObjectsPossiblyHitByRay. //Returns boost::none if none was hit. outDistance will be the distance to the intersection point if the ray intersects. -boost::optional EntityFirstHitByRay(const Ray& ray, std::vector entitiesPotentiallyHitSorted, float outDistance, glm::vec3& outIntersectPos); +boost::optional EntityFirstHitByRay(const Ray& ray, std::vector entitiesPotentiallyHitSorted, float& outDistance, glm::vec3& outIntersectPos); //Returns the first entity hit by the input ray that exists in the octree. //outDistance will be the distance to the intersection point if the ray intersects. -boost::optional EntityFirstHitByRay(const Ray& ray, Octree* octree, float outDistance, glm::vec3& outIntersectPos); +boost::optional EntityFirstHitByRay(const Ray& ray, Octree* octree, float& outDistance, glm::vec3& outIntersectPos); } diff --git a/include/Engine/Core/EPlayerDamage.h b/include/Engine/Core/EPlayerDamage.h index c11b121f..6f3f2c12 100644 --- a/include/Engine/Core/EPlayerDamage.h +++ b/include/Engine/Core/EPlayerDamage.h @@ -9,9 +9,8 @@ namespace Events struct PlayerDamage : Event { - //NOTE: this struct is missing information on what the damageSource is - EntityWrapper Player; - EntityWrapper PlayerShooter; + EntityWrapper Inflictor; + EntityWrapper Victim; double Damage; }; diff --git a/include/Engine/Core/EShoot.h b/include/Engine/Core/EShoot.h index 76821a24..28346abb 100644 --- a/include/Engine/Core/EShoot.h +++ b/include/Engine/Core/EShoot.h @@ -9,7 +9,8 @@ namespace Events struct Shoot : Event { - EntityWrapper Player; + EntityWrapper Inflictor; + double Damage; }; } diff --git a/include/Game/Systems/Weapon/AssaultWeaponBehaviour.h b/include/Game/Systems/Weapon/AssaultWeaponBehaviour.h new file mode 100644 index 00000000..915854ff --- /dev/null +++ b/include/Game/Systems/Weapon/AssaultWeaponBehaviour.h @@ -0,0 +1,44 @@ +#include "Sound/EPlaySoundOnEntity.h" +#include "Collision/Collision.h" +#include "Rendering/AnimationSystem.h" +#include "WeaponBehaviour.h" +#include "../SpawnerSystem.h" +#include "Core/EPlayerDamage.h" +#include "Core/EShoot.h" + +class AssaultWeaponBehaviour : public WeaponBehaviour +{ +public: + AssaultWeaponBehaviour(SystemParams systemParams, IRenderer* renderer, Octree* collisionOctree, EntityWrapper weaponEntity); + + virtual void Fire() override; + virtual void CeaseFire() override; + virtual void Reload() override; + + virtual void Update(double dt) override; + +private: + EntityWrapper m_FirstPersonModel; + // State + bool m_Firing = false; + bool m_Reloading = false; + double m_ReloadTimer = 0.0; + EntityWrapper m_ReloadImpersonator; + double m_TimeSinceLastFire = 0.0; + + EventRelay m_EAnimationComplete; + bool OnAnimationComplete(Events::AnimationComplete& e); + + bool hasAmmo(); + void fireRound(); + void spawnTracer(); + float traceRayDistance(glm::vec3 origin, glm::vec3 direction); + void playSound(); + void viewPunch(); + void finishReload(); + void playShootAnimation(); + void playIdleAnimation(); + void playReloadAnimation(); + bool shoot(double damage); + void showHitMarker(); +}; diff --git a/include/Game/Systems/Weapon/WeaponBehaviour.h b/include/Game/Systems/Weapon/WeaponBehaviour.h new file mode 100644 index 00000000..7a0b4626 --- /dev/null +++ b/include/Game/Systems/Weapon/WeaponBehaviour.h @@ -0,0 +1,34 @@ +#ifndef WeaponBehaviour_h__ +#define WeaponBehaviour_h__ + +#include "Core/System.h" +#include "Rendering/IRenderer.h" +#include "Core/Octree.h" +#include "Collision/EntityAABB.h" + +class WeaponBehaviour : public System +{ +public: + WeaponBehaviour(SystemParams systemParams, IRenderer* renderer, Octree* collisionOctree, EntityWrapper player) + : System(systemParams) + , m_Renderer(renderer) + , m_CollisionOctree(collisionOctree) + , m_Player(player) + { } + 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: + IRenderer* m_Renderer; + Octree* m_CollisionOctree; + EntityWrapper m_Player; +}; + +#endif diff --git a/include/Game/Systems/Weapon/WeaponSystem.h b/include/Game/Systems/Weapon/WeaponSystem.h new file mode 100644 index 00000000..b8278bc4 --- /dev/null +++ b/include/Game/Systems/Weapon/WeaponSystem.h @@ -0,0 +1,44 @@ +#ifndef WeaponSystem_h__ +#define WeaponSystem_h__ + +#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" +#include "Systems/SpawnerSystem.h" +#include "Sound/EPlaySoundOnEntity.h" +#include "AssaultWeaponBehaviour.h" + +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_EInputCommand; + bool OnInputCommand(Events::InputCommand& e); + + void selectWeapon(EntityWrapper player, ComponentInfo::EnumType slot); +}; + +#endif \ No newline at end of file diff --git a/include/Game/Systems/WeaponSystem.h b/include/Game/Systems/WeaponSystem.h deleted file mode 100644 index b118b8cd..00000000 --- a/include/Game/Systems/WeaponSystem.h +++ /dev/null @@ -1,192 +0,0 @@ -#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" -#include "Systems/SpawnerSystem.h" -#include "Sound/EPlaySoundOnEntity.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 : public 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) - { } - - 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(); - playSound(); - - m_TimeSinceLastFire = 0.0; - } - - void spawnTracer() - { - if (!IsClient) { - return; - } - - EntityWrapper spawner; - if (m_Entity == LocalPlayer) { - spawner = m_Entity.FirstChildByName("WeaponMuzzle"); - } else { - spawner = m_Entity.FirstChildByName("ThirdPersonWeaponMuzzle"); - } - - if (!spawner.Valid()) { - return; - } - - Events::SpawnerSpawn e; - e.Spawner = spawner; - m_EventBroker->Publish(e); - } - - float traceRayDistance(glm::vec3 origin, glm::vec3 direction) - { - // TODO: Cast a ray and size tracer appropriately - return 100.f; - } - - void playSound() - { - if (!IsClient) { - return; - } - - Events::PlaySoundOnEntity e; - e.EmitterID = m_Entity.ID; - e.FilePath = "Audio/laser/laser1.wav"; - m_EventBroker->Publish(e); - } -}; - -#endif \ No newline at end of file diff --git a/resources/Schema/Components/AssaultWeapon.xml b/resources/Schema/Components/AssaultWeapon.xml index 902795c1..6c645624 100755 --- a/resources/Schema/Components/AssaultWeapon.xml +++ b/resources/Schema/Components/AssaultWeapon.xml @@ -6,4 +6,6 @@ 360 5 120 + 0.01 + 2 \ No newline at end of file diff --git a/resources/Schema/Components/AssaultWeapon.xsd b/resources/Schema/Components/AssaultWeapon.xsd index 1b2704ea..95df64b7 100755 --- a/resources/Schema/Components/AssaultWeapon.xsd +++ b/resources/Schema/Components/AssaultWeapon.xsd @@ -22,6 +22,12 @@ Rate of fire in rounds per minute + + View punch in radians for each bullet fired + + + Time it takes to reload the weapon in seconds + diff --git a/resources/Schema/Components/Player.xml b/resources/Schema/Components/Player.xml index b51326aa..00cff257 100644 --- a/resources/Schema/Components/Player.xml +++ b/resources/Schema/Components/Player.xml @@ -2,4 +2,5 @@ 3 1.5 + \ No newline at end of file diff --git a/resources/Schema/Components/Player.xsd b/resources/Schema/Components/Player.xsd index 13948dc2..1b33d222 100644 --- a/resources/Schema/Components/Player.xsd +++ b/resources/Schema/Components/Player.xsd @@ -5,12 +5,13 @@ - The player charachter + The player entity + diff --git a/resources/Schema/Entities/HitMarker.xml b/resources/Schema/Entities/HitMarker.xml new file mode 100644 index 00000000..74a539d0 --- /dev/null +++ b/resources/Schema/Entities/HitMarker.xml @@ -0,0 +1,20 @@ + + + + + + 0.1 + + + Textures/Weapons/Crosshair/SmallThickHoleDot.png + false + + + + + + + + + + diff --git a/resources/Schema/Entities/MovementTest.xml b/resources/Schema/Entities/MovementTest.xml index 16a28684..84aaa03a 100644 --- a/resources/Schema/Entities/MovementTest.xml +++ b/resources/Schema/Entities/MovementTest.xml @@ -124,10 +124,14 @@ + + 600 + - + + - false + 5 @@ -138,8 +142,7 @@ - - + @@ -147,7 +150,7 @@ - + @@ -156,10 +159,9 @@ Fonts/DroidSans.ttf,100 - false - + @@ -170,6 +172,7 @@ Models/Widgets/Camera.mesh + false @@ -178,6 +181,100 @@ + + + + + + + + + + + 1 + + + + + Models/Core/UnitHexagon.mesh + + + + + + + + + + + + + + Textures/Weapons/Crosshair/SmallThickHoleDot.png + false + + + + + + + + + + + + + + Idle + 1.9569972344146196 + 1 + + + Models/Characters/Assault/FirstPerson.mesh + true + + + + + + + + R_Arm_Weapon_Joint + + + Models/Weapons/Blue/AssaultWeaponBlue.mesh + true + + + + + + + + + + + Schema/Entities/RayBlue.xml + + + + + + + + + + + + + Schema/Entities/WeaponReloadEffect.xml + + + + + + + @@ -196,15 +293,52 @@ + + Idle + 1.8055945618467364 + 1 + + + AimRifle + + + - Models/Characters/Assault/AssaultHeadless.mesh + Models/Characters/Assault/AssaultAnimations.mesh - - - + - + + + + + R_Arm_Weapon_Joint + + + Models/Weapons/Blue/AssaultWeaponBlue.mesh + false + + + + + + + + + + + Schema/Entities/RayBlue.xml + + + + + + + + + + diff --git a/resources/Schema/Entities/Player.xml b/resources/Schema/Entities/Player.xml index 4ba23a0d..9f8e0955 100644 --- a/resources/Schema/Entities/Player.xml +++ b/resources/Schema/Entities/Player.xml @@ -23,7 +23,9 @@ - + + + @@ -90,24 +92,33 @@ - - Models/Weapons/CrosshairQuad.mesh - + + Textures/Weapons/Crosshair/SmallThickHoleDot.png + false + - - + + + + + Schema/Entities/HitMarker.xml + + + + + Idle - 0.52743271827223559 + 0.1719161089749548 1 @@ -124,10 +135,11 @@ Models/Weapons/Blue/AssaultWeaponBlue.mesh + true - - + + @@ -144,6 +156,15 @@ + + + + Schema/Entities/WeaponReloadEffect.xml + + + + + @@ -166,7 +187,7 @@ Idle - 0.69666320633760392 + 1.8038469763698401 1 @@ -188,10 +209,11 @@ Models/Weapons/Blue/AssaultWeaponBlue.mesh + false - - + + diff --git a/resources/Schema/Entities/WeaponReloadEffect.xml b/resources/Schema/Entities/WeaponReloadEffect.xml new file mode 100644 index 00000000..3099b405 --- /dev/null +++ b/resources/Schema/Entities/WeaponReloadEffect.xml @@ -0,0 +1,31 @@ + + + + + + R_Arm_Weapon_Joint + + + 2 + + + true + + + true + + true + + + Models/Weapons/Blue/AssaultWeaponBlue.mesh + true + + + + + + + + + + diff --git a/resources/Shaders/Sprite.frag.glsl b/resources/Shaders/Sprite.frag.glsl index a1ff3025..9ce2bbdf 100644 --- a/resources/Shaders/Sprite.frag.glsl +++ b/resources/Shaders/Sprite.frag.glsl @@ -34,7 +34,8 @@ void main() } sceneColor = vec4(color_result.xyz, clamp(color_result.a, 0, 1)); - bloomColor = vec4(clamp((glowTexel.xyz*3) - 1.0, 0, 100), 1.0); + //bloomColor = vec4(clamp((glowTexel.xyz*3) - 1.0, 0, 100), 1.0); + bloomColor = vec4(1.0, 1.0, 1.0, 0.0); } diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index 7b182de1..ab2098b7 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -642,7 +642,7 @@ boost::optional AbsoluteAABBExplosionEffect(EntityWrapper& entity) return aabb; } -boost::optional EntityFirstHitByRay(const Ray& ray, std::vector entitiesPotentiallyHitSorted, float outDistance, glm::vec3& outIntersectPos) +boost::optional EntityFirstHitByRay(const Ray& ray, std::vector entitiesPotentiallyHitSorted, float& outDistance, glm::vec3& outIntersectPos) { for (EntityAABB& entityBox : entitiesPotentiallyHitSorted) { if (!entityBox.Entity.HasComponent("Model")) { @@ -667,7 +667,7 @@ boost::optional EntityFirstHitByRay(const Ray& ray, std::vector EntityFirstHitByRay(const Ray& ray, Octree* octree, float outDistance, glm::vec3& outIntersectPos) +boost::optional EntityFirstHitByRay(const Ray& ray, Octree* octree, float& outDistance, glm::vec3& outIntersectPos) { std::vector outObjects; octree->ObjectsPossiblyHitByRay(ray, outObjects); diff --git a/src/Engine/Core/EntityFilePreprocessor.cpp b/src/Engine/Core/EntityFilePreprocessor.cpp index 86217979..a1370dd2 100644 --- a/src/Engine/Core/EntityFilePreprocessor.cpp +++ b/src/Engine/Core/EntityFilePreprocessor.cpp @@ -65,7 +65,6 @@ void EntityFilePreprocessor::parseComponentInfo() // Name compInfo.Name = XS::ToString(element->getName()); - bool brk = compInfo.Name == "HiddenForLocalPlayer"; // Known allocation compInfo.Meta->Allocation = m_ComponentCounts[compInfo.Name]; // Annotation diff --git a/src/Engine/Network/Client.cpp b/src/Engine/Network/Client.cpp index 7d2c8f92..b03aad07 100644 --- a/src/Engine/Network/Client.cpp +++ b/src/Engine/Network/Client.cpp @@ -390,8 +390,9 @@ bool Client::OnInputCommand(const Events::InputCommand & e) bool Client::OnPlayerDamage(const Events::PlayerDamage & e) { Packet packet(MessageType::OnPlayerDamage, m_SendPacketID); + packet.WritePrimitive(m_ClientIDToServerID.at(e.Inflictor.ID)); + packet.WritePrimitive(m_ClientIDToServerID.at(e.Victim.ID)); packet.WritePrimitive(e.Damage); - packet.WritePrimitive(m_ClientIDToServerID.at(e.Player.ID)); send(packet); return false; } diff --git a/src/Engine/Network/Server.cpp b/src/Engine/Network/Server.cpp index 7ad1cc76..1f0b3bc7 100644 --- a/src/Engine/Network/Server.cpp +++ b/src/Engine/Network/Server.cpp @@ -299,8 +299,9 @@ void Server::parseOnInputCommand(Packet& packet) void Server::parseOnPlayerDamage(Packet & packet) { Events::PlayerDamage e; + e.Inflictor = EntityWrapper(m_World, packet.ReadPrimitive()); + e.Victim = EntityWrapper(m_World, packet.ReadPrimitive()); e.Damage = packet.ReadPrimitive(); - e.Player = EntityWrapper(m_World, packet.ReadPrimitive()); m_EventBroker->Publish(e); //LOG_DEBUG("Server::parseOnPlayerDamage: Command is %s. Value is %f. PlayerID is %i.", e.DamageAmount, e.PlayerDamagedID, e.TypeOfDamage.c_str()); } diff --git a/src/Engine/Rendering/AnimationSystem.cpp b/src/Engine/Rendering/AnimationSystem.cpp index 48681e6e..d5b5984d 100644 --- a/src/Engine/Rendering/AnimationSystem.cpp +++ b/src/Engine/Rendering/AnimationSystem.cpp @@ -34,19 +34,32 @@ void AnimationSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& a if (!(bool)animationComponent["Loop" + std::to_string(i)]) { if (nextTime > animation->Duration) { nextTime = animation->Duration; + Events::AnimationComplete e; + e.Entity = entity; + e.Name = (std::string)animationComponent["AnimationName" + std::to_string(i)]; + m_EventBroker->Publish(e); } else if (nextTime < 0) { + Events::AnimationComplete e; + e.Entity = entity; + e.Name = (std::string)animationComponent["AnimationName" + std::to_string(i)]; + m_EventBroker->Publish(e); nextTime = 0; } (double&)animationComponent["Speed" + std::to_string(i)] = 0.0; - Events::AnimationComplete e; - e.Entity = entity; - e.Name = (std::string)animationComponent["AnimationName" + std::to_string(i)]; - m_EventBroker->Publish(e); + } else { if (nextTime > animation->Duration) { + Events::AnimationComplete e; + e.Entity = entity; + e.Name = (std::string)animationComponent["AnimationName" + std::to_string(i)]; + m_EventBroker->Publish(e); nextTime -= animation->Duration; } else if (nextTime < 0) { + Events::AnimationComplete e; + e.Entity = entity; + e.Name = (std::string)animationComponent["AnimationName" + std::to_string(i)]; + m_EventBroker->Publish(e); nextTime += animation->Duration; } } diff --git a/src/Game/CMakeLists.txt b/src/Game/CMakeLists.txt index db923364..f188d7fa 100644 --- a/src/Game/CMakeLists.txt +++ b/src/Game/CMakeLists.txt @@ -16,6 +16,12 @@ file(GLOB SOURCE_FILES_Systems ) source_group(Systems FILES ${SOURCE_FILES_Systems}) +file(GLOB SOURCE_FILES_Systems_Weapon + "${INCLUDE_PATH}/Systems/Weapon/*.h" + "Systems/Weapon/*.cpp" +) +source_group(Systems\\Weapon FILES ${SOURCE_FILES_Systems_Weapon}) + file(GLOB SOURCE_FILES_Events "${INCLUDE_PATH}/Events/*.h" "Events/*.cpp" @@ -31,6 +37,7 @@ set(SOURCE_FILES ${SOURCE_FILES} "Game.cpp" ${SOURCE_FILES_Systems} + ${SOURCE_FILES_Systems_Weapon} ${SOURCE_FILES_Events} ${SOURCE_FILES_Network} ) diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index 8ee6dbed..0dc52822 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -15,7 +15,7 @@ #include "Game/Systems/CapturePointHUDSystem.h" #include "Game/Systems/PickupSpawnSystem.h" #include "Game/Systems/DamageIndicatorSystem.h" -#include "Game/Systems/WeaponSystem.h" +#include "Game/Systems/Weapon/WeaponSystem.h" #include "Rendering/AnimationSystem.h" #include "Game/Systems/PlayerHUDSystem.h" #include "Rendering/BoneAttachmentSystem.h" diff --git a/src/Game/Systems/HealthSystem.cpp b/src/Game/Systems/HealthSystem.cpp index bb02ea13..29d8790d 100644 --- a/src/Game/Systems/HealthSystem.cpp +++ b/src/Game/Systems/HealthSystem.cpp @@ -15,13 +15,13 @@ void HealthSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& comp bool HealthSystem::OnPlayerDamaged(Events::PlayerDamage& e) { - ComponentWrapper cHealth = e.Player["Health"]; + ComponentWrapper cHealth = e.Victim["Health"]; double& health = cHealth["Health"]; health -= e.Damage; if (health <= 0.0) { Events::PlayerDeath ePlayerDeath; - ePlayerDeath.Player = e.Player; + ePlayerDeath.Player = e.Victim; m_EventBroker->Publish(ePlayerDeath); //Note: we will delete the entity in PlayerDeathSystem } diff --git a/src/Game/Systems/PlayerDeathSystem.cpp b/src/Game/Systems/PlayerDeathSystem.cpp index 29ed9832..78e20490 100644 --- a/src/Game/Systems/PlayerDeathSystem.cpp +++ b/src/Game/Systems/PlayerDeathSystem.cpp @@ -41,7 +41,9 @@ void PlayerDeathSystem::createDeathEffect(EntityWrapper player) playerEntityModel.Copy(deathEffectEW["Model"]); playerEntityAnimation.Copy(deathEffectEW["Animation"]); //freeze the animation - deathEffectEW["Animation"]["Speed"] = 0.0; + deathEffectEW["Animation"]["Speed1"] = 0.0; + deathEffectEW["Animation"]["Speed2"] = 0.0; + deathEffectEW["Animation"]["Speed3"] = 0.0; //copy the models position,orientation deathEffectEW["Transform"]["Position"] = (glm::vec3)player["Transform"]["Position"]; @@ -50,8 +52,10 @@ void PlayerDeathSystem::createDeathEffect(EntityWrapper player) //deathEffectEW["ExplosionEffect"]["ExplosionOrigin"] = glm::vec3(0, 0, 0); //camera (with lifetime) behind the player - auto cam = deathEffectEW.FirstChildByName("Camera"); - Events::SetCamera eSetCamera; - eSetCamera.CameraEntity = cam; - m_EventBroker->Publish(eSetCamera); + if (player == LocalPlayer) { + auto cam = deathEffectEW.FirstChildByName("Camera"); + Events::SetCamera eSetCamera; + eSetCamera.CameraEntity = cam; + m_EventBroker->Publish(eSetCamera); + } } diff --git a/src/Game/Systems/PlayerMovementSystem.cpp b/src/Game/Systems/PlayerMovementSystem.cpp index bad512df..461c7098 100644 --- a/src/Game/Systems/PlayerMovementSystem.cpp +++ b/src/Game/Systems/PlayerMovementSystem.cpp @@ -51,6 +51,7 @@ void PlayerMovementSystem::updateMovementControllers(double dt) float playerMovementSpeed = player["Player"]["MovementSpeed"]; float playerCrouchSpeed = player["Player"]["CrouchSpeed"]; + glm::vec3& wishDirection = player["Player"]["CurrentWishDirection"]; if (player.HasComponent("Physics")) { ComponentWrapper cPhysics = player["Physics"]; @@ -58,7 +59,7 @@ void PlayerMovementSystem::updateMovementControllers(double dt) if (player.HasComponent("DashAbility")) { controller->AssaultDashCheck(dt, ((glm::vec3)cPhysics["Velocity"]).y != 0.0f, player["DashAbility"]["CoolDownMaxTimer"]); } - glm::vec3 wishDirection = controller->Movement() * glm::inverse(glm::quat(ori)); + wishDirection = controller->Movement() * glm::inverse(glm::quat(ori)); //this makes sure you can only dash in the 4 directions: forw,backw,left,right if (controller->AssaultDashDoubleTapped() && controller->Movement().z != 0 && controller->Movement().x != 0) { wishDirection = glm::vec3(controller->Movement().x, 0, 0)* glm::inverse(glm::quat(ori)); diff --git a/src/Game/Systems/SoundSystem.cpp b/src/Game/Systems/SoundSystem.cpp index 10dc61f7..d6ac7dab 100644 --- a/src/Game/Systems/SoundSystem.cpp +++ b/src/Game/Systems/SoundSystem.cpp @@ -54,7 +54,7 @@ bool SoundSystem::OnInputCommand(const Events::InputCommand & e) } if (e.Command == "TakeDamage" && e.Value > 0) { Events::PlayerDamage ev; - ev.Player = LocalPlayer; + ev.Victim = LocalPlayer; ev.Damage = 1.0; m_EventBroker->Publish(ev); } diff --git a/src/Game/Systems/SpawnerSystem.cpp b/src/Game/Systems/SpawnerSystem.cpp index 99f5df93..b3c556f1 100644 --- a/src/Game/Systems/SpawnerSystem.cpp +++ b/src/Game/Systems/SpawnerSystem.cpp @@ -48,10 +48,12 @@ EntityWrapper SpawnerSystem::Spawn(EntityWrapper spawner, EntityWrapper parent / EntityFileParser parser(entityFile); EntityWrapper spawnedEntity(world, parser.MergeEntities(world, parent.ID)); - // Set its position and orientation to that of the SpawnPoint - spawnedEntity["Transform"]["Position"] = Transform::AbsolutePosition(spawnPoint.World, spawnPoint.ID); - // TODO: Quaternions, bitch - spawnedEntity["Transform"]["Orientation"] = glm::eulerAngles(Transform::AbsoluteOrientation(spawnPoint)); + if (spawnPoint != parent) { + // Set its position and orientation to that of the SpawnPoint + spawnedEntity["Transform"]["Position"] = Transform::AbsolutePosition(spawnPoint.World, spawnPoint.ID); + // TODO: Quaternions, bitch + spawnedEntity["Transform"]["Orientation"] = glm::eulerAngles(Transform::AbsoluteOrientation(spawnPoint)); + } return spawnedEntity; } diff --git a/src/Game/Systems/Weapon/AssaultWeaponBehaviour.cpp b/src/Game/Systems/Weapon/AssaultWeaponBehaviour.cpp new file mode 100644 index 00000000..724e1f2c --- /dev/null +++ b/src/Game/Systems/Weapon/AssaultWeaponBehaviour.cpp @@ -0,0 +1,340 @@ +#include "Systems/Weapon/AssaultWeaponBehaviour.h" + +AssaultWeaponBehaviour::AssaultWeaponBehaviour(SystemParams systemParams, IRenderer* renderer, Octree* collisionOctree, EntityWrapper player) + : WeaponBehaviour(systemParams, renderer, collisionOctree, player) +{ + m_FirstPersonModel = m_Player.FirstChildByName("Hands"); + EVENT_SUBSCRIBE_MEMBER(m_EAnimationComplete, &AssaultWeaponBehaviour::OnAnimationComplete); +} + +void AssaultWeaponBehaviour::Fire() +{ + m_TimeSinceLastFire = 0.0; + m_Firing = true; + fireRound(); +} + +void AssaultWeaponBehaviour::CeaseFire() +{ + m_Firing = false; +} + +void AssaultWeaponBehaviour::Reload() +{ + if (m_Reloading) { + return; + } + + ComponentWrapper& cAssaultWeapon = m_Player["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; + } + + // Don't reload if we're completly out of ammo + if (ammo == 0) { + return; + } + + m_Reloading = true; + m_ReloadTimer = cAssaultWeapon["ReloadTime"]; + playReloadAnimation(); +} + +void AssaultWeaponBehaviour::Update(double dt) +{ + if (m_Reloading) { + m_ReloadTimer -= dt; + // Re-enable glow on reload impersonator half-way through the animation + if (m_ReloadTimer <= (double)m_Player["AssaultWeapon"]["ReloadTime"] / 2.0) { + if (m_ReloadImpersonator.Valid()) { + m_ReloadImpersonator["Model"]["GlowMap"] = true; + } + } + if (m_ReloadTimer <= 0) { + finishReload(); + } + } + + if (m_Firing && !m_Reloading) { + m_TimeSinceLastFire += dt; + ComponentWrapper& cAssaultWeapon = m_Player["AssaultWeapon"]; + if (m_TimeSinceLastFire >= 1.0 / ((double)cAssaultWeapon["RPM"] / 60.0)) { + fireRound(); + } + } + + if (!m_Firing && !m_Reloading) { + playIdleAnimation(); + } + + // Disable glow map on weapon if it's out of ammo + // Make real first person weapon model visible again + EntityWrapper firstPersonWeaponModel = m_Player.FirstChildByName("WeaponModel"); + if (firstPersonWeaponModel.Valid()) { + firstPersonWeaponModel["Model"]["GlowMap"] = hasAmmo(); + } +} + +bool AssaultWeaponBehaviour::OnAnimationComplete(Events::AnimationComplete& e) +{ + if (e.Entity != m_FirstPersonModel) { + return false; + } + + //if (e.Name == "ShootRifle") { + // if (!m_Firing) { + // playIdleAnimation(); + // } + //} + + return true; +} + +bool AssaultWeaponBehaviour::hasAmmo() +{ + ComponentWrapper& cAssaultWeapon = m_Player["AssaultWeapon"]; + int& magAmmo = cAssaultWeapon["MagazineAmmo"]; + return magAmmo > 0; +} + +void AssaultWeaponBehaviour::fireRound() +{ + if (m_Reloading) { + return; + } + + ComponentWrapper& cAssaultWeapon = m_Player["AssaultWeapon"]; + + int& magAmmo = cAssaultWeapon["MagazineAmmo"]; + int ammo = cAssaultWeapon["Ammo"]; + + // Reload if our magazine is empty + if (magAmmo <= 0) { + Reload(); + return; + } + + // Fire + magAmmo -= 1; + spawnTracer(); + playSound(); + viewPunch(); + playShootAnimation(); + bool hit = shoot(cAssaultWeapon["BaseDamage"]); + if (hit) { + showHitMarker(); + } + + m_TimeSinceLastFire = 0.0; +} + +void AssaultWeaponBehaviour::spawnTracer() +{ + if (!IsClient) { + return; + } + + EntityWrapper spawner; + if (m_Player == LocalPlayer) { + spawner = m_Player.FirstChildByName("WeaponMuzzle"); + } else { + spawner = m_Player.FirstChildByName("ThirdPersonWeaponMuzzle"); + } + + if (!spawner.Valid()) { + return; + } + + float distance = traceRayDistance(Transform::AbsolutePosition(spawner), Transform::AbsoluteOrientation(spawner) * glm::vec3(0, 0, -1)); + EntityWrapper ray = SpawnerSystem::Spawn(spawner); + ((glm::vec3&)ray["Transform"]["Scale"]).z = (distance / 100.f); +} + +float AssaultWeaponBehaviour::traceRayDistance(glm::vec3 origin, glm::vec3 direction) +{ + // TODO: Cast a ray and size tracer appropriately + float distance; + glm::vec3 pos; + auto entity = Collision::EntityFirstHitByRay(Ray(origin, direction), m_CollisionOctree, distance, pos); + if (entity) { + return distance; + } else { + return 100.f; + } +} + +void AssaultWeaponBehaviour::playSound() +{ + if (!IsClient) { + return; + } + + Events::PlaySoundOnEntity e; + e.EmitterID = m_Player.ID; + e.FilePath = "Audio/laser/laser1.wav"; + m_EventBroker->Publish(e); +} + +void AssaultWeaponBehaviour::viewPunch() +{ + EntityWrapper playerCamera = m_Player.FirstChildByName("Camera"); + if (!playerCamera.Valid()) { + return; + } + float viewPunch = m_Player["AssaultWeapon"]["ViewPunch"]; + ComponentWrapper cTransform = playerCamera["Transform"]; + glm::vec3& orientation = cTransform["Orientation"]; + orientation.x += viewPunch; +} + +void AssaultWeaponBehaviour::finishReload() +{ + ComponentWrapper& cAssaultWeapon = m_Player["AssaultWeapon"]; + int& magAmmo = cAssaultWeapon["MagazineAmmo"]; + int magSize = cAssaultWeapon["MagazineSize"]; + int& ammo = cAssaultWeapon["Ammo"]; + + // Throw away rounds in magazine to incentivise ammo sharing + int toLoad = glm::min(magSize, ammo); + magAmmo = toLoad; + ammo -= toLoad; + + // Make real first person weapon model visible again + EntityWrapper firstPersonWeaponModel = m_Player.FirstChildByName("WeaponModel"); + firstPersonWeaponModel["Model"]["Visible"] = true; + + m_Reloading = false; +} + +void AssaultWeaponBehaviour::playShootAnimation() +{ + ComponentWrapper cAnimation = m_FirstPersonModel["Animation"]; + if (cAnimation["AnimationName1"] != "ShootRifle") { + cAnimation["AnimationName1"] = "ShootRifle"; + cAnimation["Weight1"] = 1.0; + cAnimation["Time1"] = 0.0; + cAnimation["Speed1"] = 1.0; + cAnimation["Loop1"] = true; + } +} + +void AssaultWeaponBehaviour::playIdleAnimation() +{ + if (!m_FirstPersonModel.Valid()) { + return; + } + + ComponentWrapper cAnimation = m_FirstPersonModel["Animation"]; + std::string& animationName1 = cAnimation["AnimationName1"]; + double& animationSpeed1 = cAnimation["Speed1"]; + + std::string animationToPlay = "Idle"; + double speedToSet = 1.0; + + ComponentWrapper cPlayer = m_Player["Player"]; + glm::vec3 movementDirection = cPlayer["CurrentWishDirection"]; + if (glm::length2(movementDirection) > 0) { + animationToPlay = "Run"; + ComponentWrapper cPhysics = m_Player["Physics"]; + speedToSet = glm::length((glm::vec3)cPhysics["Velocity"]) / (float)cPlayer["MovementSpeed"]; + } + + if (animationName1 != animationToPlay) { + cAnimation["AnimationName1"] = animationToPlay; + cAnimation["Weight1"] = 1.0; + cAnimation["Time1"] = 0.0; + cAnimation["Loop1"] = true; + } + + if (animationSpeed1 != speedToSet) { + cAnimation["Speed1"] = speedToSet; + } +} + +void AssaultWeaponBehaviour::playReloadAnimation() +{ + // Play animation + ComponentWrapper cAnimation = m_FirstPersonModel["Animation"]; + cAnimation["AnimationName1"] = "ReloadSwitch"; + cAnimation["Weight1"] = 1.0; + cAnimation["Time1"] = 0.0; + cAnimation["Speed1"] = 0.5; + cAnimation["Loop1"] = true; + + // Hide weapon model and spawn the exploding version + EntityWrapper firstPersonWeaponModel = m_Player.FirstChildByName("WeaponModel"); + EntityWrapper reloadSpawner = m_Player.FirstChildByName("FirstPersonReloadSpawner"); + m_ReloadImpersonator = SpawnerSystem::Spawn(reloadSpawner, reloadSpawner); + firstPersonWeaponModel["Model"].Copy(m_ReloadImpersonator["Model"]); + firstPersonWeaponModel["Model"]["Visible"] = false; +} + +bool AssaultWeaponBehaviour::shoot(double damage) +{ + // Only do shooting clientside + if (!IsClient) { + return false; + } + + // Only handle shooting for the local player + if (m_Player != LocalPlayer) { + return false; + } + + // Make sure the player isn't shooting from the grave + if (!m_Player.Valid()) { + return false; + } + + // Screen center, based on current resolution! + Rectangle screenResolution = m_Renderer->GetViewportSize(); + glm::vec2 centerScreen = glm::vec2(screenResolution.Width / 2, screenResolution.Height / 2); + + // Pick middle of screen + PickData pickData = m_Renderer->Pick(centerScreen); + if (pickData.Entity == EntityID_Invalid) { + return false; + } + + EntityWrapper victim(m_World, pickData.Entity); + + // Don't let us 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; + } + + // Check for friendly fire + if ((ComponentInfo::EnumType)victim["Team"]["Team"] == (ComponentInfo::EnumType)m_Player["Team"]["Team"]) { + return false; + } + + // Deal damage! + Events::PlayerDamage ePlayerDamage; + ePlayerDamage.Victim = victim; + ePlayerDamage.Damage = damage; + m_EventBroker->Publish(ePlayerDamage); + + return true; +} + +void AssaultWeaponBehaviour::showHitMarker() +{ + // Show hit marker + EntityWrapper hitMarkerSpawner = m_Player.FirstChildByName("HitMarkerSpawner"); + if (hitMarkerSpawner.Valid()) { + SpawnerSystem::Spawn(hitMarkerSpawner, hitMarkerSpawner); + } +} diff --git a/src/Game/Systems/WeaponSystem.cpp b/src/Game/Systems/Weapon/WeaponSystem.cpp similarity index 56% rename from src/Game/Systems/WeaponSystem.cpp rename to src/Game/Systems/Weapon/WeaponSystem.cpp index 8b6ba85b..4a25b811 100644 --- a/src/Game/Systems/WeaponSystem.cpp +++ b/src/Game/Systems/Weapon/WeaponSystem.cpp @@ -1,4 +1,4 @@ -#include "Systems/WeaponSystem.h" +#include "Systems/Weapon/WeaponSystem.h" WeaponSystem::WeaponSystem(SystemParams params, IRenderer* renderer, Octree* collisionOctree) : System(params) @@ -8,7 +8,6 @@ WeaponSystem::WeaponSystem(SystemParams params, IRenderer* renderer, OctreeProcess(); m_ActiveWeapons.at(entity)->Update(dt); } @@ -59,6 +59,14 @@ bool WeaponSystem::OnInputCommand(Events::InputCommand& e) } } + // Reload + if (e.Command == "Reload" && e.Value != 0) { + if (m_ActiveWeapons.find(player) != m_ActiveWeapons.end()) { + auto weapon = m_ActiveWeapons.at(player); + weapon->Reload(); + } + } + return true; } @@ -68,7 +76,7 @@ void WeaponSystem::selectWeapon(EntityWrapper player, ComponentInfo::EnumType sl if (slot == 1) { // TODO: if class... if (m_ActiveWeapons.count(player) == 0) { - m_ActiveWeapons.insert(std::make_pair(player, std::make_shared(m_SystemParams, m_CollisionOctree, player))); + m_ActiveWeapons.insert(std::make_pair(player, std::make_shared(m_SystemParams, m_Renderer, m_CollisionOctree, player))); } else { //m_ActiveWeapons.erase(player); } @@ -87,52 +95,4 @@ bool WeaponSystem::OnPlayerSpawned(Events::PlayerSpawned& e) return true; } -bool WeaponSystem::OnShoot(Events::Shoot& eShoot) -{ - if (!eShoot.Player.Valid()) { - return false; - } - - // Only run further picking code for the local player! - if (eShoot.Player != LocalPlayer) { - return false; - } - - // Screen center, based on current resolution! - // TODO: check if player has enough ammo and if weapon has a cooldown or not - Rectangle screenResolution = m_Renderer->GetViewportSize(); - glm::vec2 centerScreen = glm::vec2(screenResolution.Width / 2, screenResolution.Height / 2); - - // TODO: check if player has enough ammo and if weapon has a cooldown or not - - // Pick middle of screen - PickData pickData = m_Renderer->Pick(centerScreen); - if (pickData.Entity == EntityID_Invalid) { - return false; - } - - EntityWrapper player(m_World, pickData.Entity); - - // Only care about players being hit - if (!player.HasComponent("Player")) { - player = player.FirstParentWithComponent("Player"); - } - if (!player.Valid()) { - return false; - } - - // Check for friendly fire - EntityWrapper shooter = eShoot.Player; - if ((ComponentInfo::EnumType)player["Team"]["Team"] == (ComponentInfo::EnumType)shooter["Team"]["Team"]) { - return false; - } - - // TODO: Weapon damage calculations etc - Events::PlayerDamage ePlayerDamage; - ePlayerDamage.Player = player; - ePlayerDamage.PlayerShooter = eShoot.Player; - ePlayerDamage.Damage = 100; - m_EventBroker->Publish(ePlayerDamage); - - return true; -} \ No newline at end of file + ePlayerDamage.PlayerShooter = eShoot.Player; \ No newline at end of file diff --git a/src/Tests/HealthSystemTest.cpp b/src/Tests/HealthSystemTest.cpp index 36608f8f..8bf16024 100644 --- a/src/Tests/HealthSystemTest.cpp +++ b/src/Tests/HealthSystemTest.cpp @@ -71,7 +71,7 @@ GameHealthSystemTest::GameHealthSystemTest() //damage player with 50 Events::PlayerDamage e; e.Damage = 50.0f; - e.Player = EntityWrapper(m_World, player.EntityID); + e.Victim = EntityWrapper(m_World, player.EntityID); m_EventBroker->Publish(e); //heal some other player with 40