diff --git a/include/Engine/Core/EntityWrapper.h b/include/Engine/Core/EntityWrapper.h index 8ece8e59..12029720 100644 --- a/include/Engine/Core/EntityWrapper.h +++ b/include/Engine/Core/EntityWrapper.h @@ -27,6 +27,7 @@ struct EntityWrapper bool HasComponent(const std::string& componentType); void AttachComponent(const char* componentName); EntityWrapper Parent(); + EntityWrapper FirstParentByName(const std::string& parentEntityName); EntityWrapper FirstChildByName(const std::string& name); EntityWrapper FirstParentWithComponent(const std::string& componentType); EntityWrapper Clone(EntityWrapper parent = EntityWrapper::Invalid); diff --git a/include/Engine/Editor/EditorGUI.h b/include/Engine/Editor/EditorGUI.h index 57574e66..807bfc8b 100644 --- a/include/Engine/Editor/EditorGUI.h +++ b/include/Engine/Editor/EditorGUI.h @@ -22,6 +22,7 @@ #include "../Core/ELockMouse.h" #include "../Core/EFileDropped.h" #include "../Rendering/Texture.h" +#include "Game/Events/ESpawnerSpawn.h" class EditorGUI { diff --git a/include/Game/Systems/AmmunitionHUDSystem.h b/include/Game/Systems/AmmunitionHUDSystem.h deleted file mode 100644 index b22a85b5..00000000 --- a/include/Game/Systems/AmmunitionHUDSystem.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef AmmunitionHUDSystem_h__ -#define AmmunitionHUDSystem_h__ - -#include "../../Engine/Core/System.h" -#include "../../Engine/GLM.h" - -class AmmunitionHUDSystem : public ImpureSystem -{ -public: - AmmunitionHUDSystem(SystemParams params) - : System(params) - { } - - virtual void Update(double dt) override; -}; - -#endif \ No newline at end of file diff --git a/include/Game/Systems/TextFieldReader.h b/include/Game/Systems/TextFieldReader.h new file mode 100644 index 00000000..1ea8e966 --- /dev/null +++ b/include/Game/Systems/TextFieldReader.h @@ -0,0 +1,19 @@ +#ifndef AmmunitionHUDSystem_h__ +#define AmmunitionHUDSystem_h__ + +#include +#include "../../Engine/Core/System.h" +#include "../../Engine/GLM.h" + +class TextFieldReader : public PureSystem +{ +public: + TextFieldReader(SystemParams params) + : System(params) + , PureSystem("TextFieldReader") + { } + + virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& cTextFieldReader, double dt) override; +}; + +#endif \ No newline at end of file diff --git a/include/Game/Systems/Weapon/DefenderWeaponBehaviour.h b/include/Game/Systems/Weapon/DefenderWeaponBehaviour.h index 5ca13d3e..c1e132b1 100644 --- a/include/Game/Systems/Weapon/DefenderWeaponBehaviour.h +++ b/include/Game/Systems/Weapon/DefenderWeaponBehaviour.h @@ -1,7 +1,6 @@ #include "WeaponBehaviour.h" #include "Collision/Collision.h" #include "Core/EPlayerDamage.h" -#include "Rendering/ESetCamera.h" class DefenderWeaponBehaviour : public WeaponBehaviour { @@ -10,29 +9,24 @@ public: : System(systemParams) , WeaponBehaviour(systemParams, "DefenderWeapon", renderer, collisionOctree) , m_RandomEngine(m_RandomDevice()) - { - EVENT_SUBSCRIBE_MEMBER(m_ESetCamera, &DefenderWeaponBehaviour::OnSetCamera); - } + { } void UpdateComponent(EntityWrapper& entity, ComponentWrapper& cWeapon, double dt) override; - void UpdateWeapon(WeaponInfo& wi, double dt) override; - void OnPrimaryFire(WeaponInfo& wi) override; - void OnCeasePrimaryFire(WeaponInfo& wi) override; - bool OnInputCommand(WeaponInfo& wi, const Events::InputCommand& e) override; + void UpdateWeapon(ComponentWrapper cWeapon, WeaponInfo& wi, double dt) override; + void OnPrimaryFire(ComponentWrapper cWeapon, WeaponInfo& wi) override; + void OnCeasePrimaryFire(ComponentWrapper cWeapon, WeaponInfo& wi) override; + void OnReload(ComponentWrapper cWeapon, WeaponInfo& wi) override; + bool OnInputCommand(ComponentWrapper cWeapon, WeaponInfo& wi, const Events::InputCommand& e) override; private: std::random_device m_RandomDevice; std::mt19937 m_RandomEngine; - EntityWrapper m_CurrentCamera; - - EventRelay m_ESetCamera; - bool OnSetCamera(const Events::SetCamera& e); // Weapon functions - void fireShell(WeaponInfo& wi); - void dealDamage(WeaponInfo& wi, glm::vec3 direction, double damage); + void fireShell(ComponentWrapper cWeapon, WeaponInfo& wi); + void dealDamage(ComponentWrapper cWeapon, WeaponInfo& wi, glm::vec3 direction, double damage); + bool canFire(ComponentWrapper cWeapon, WeaponInfo& wi); // Utility - float traceRayDistance(glm::vec3 origin, glm::vec3 direction); Camera cameraFromEntity(EntityWrapper camera); }; \ No newline at end of file diff --git a/include/Game/Systems/Weapon/SidearmWeaponBehaviour.h b/include/Game/Systems/Weapon/SidearmWeaponBehaviour.h new file mode 100644 index 00000000..d221b8bb --- /dev/null +++ b/include/Game/Systems/Weapon/SidearmWeaponBehaviour.h @@ -0,0 +1,33 @@ +#include "WeaponBehaviour.h" +#include "Collision/Collision.h" +#include "Core/EPlayerDamage.h" + +class SidearmWeaponBehaviour : public WeaponBehaviour +{ +public: + SidearmWeaponBehaviour(SystemParams systemParams, IRenderer* renderer, Octree* collisionOctree) + : System(systemParams) + , WeaponBehaviour(systemParams, "SidearmWeapon", renderer, collisionOctree) + , m_RandomEngine(m_RandomDevice()) + { } + + void UpdateComponent(EntityWrapper& entity, ComponentWrapper& cWeapon, double dt) override; + void UpdateWeapon(ComponentWrapper cWeapon, WeaponInfo& wi, double dt) override; + void OnPrimaryFire(ComponentWrapper cWeapon, WeaponInfo& wi) override; + void OnCeasePrimaryFire(ComponentWrapper cWeapon, WeaponInfo& wi) override; + void OnEquip(ComponentWrapper cWeapon, WeaponInfo& wi) override; + void OnHolster(ComponentWrapper cWeapon, WeaponInfo& wi) override; + +private: + std::random_device m_RandomDevice; + std::mt19937 m_RandomEngine; + + // Weapon functions + void fireBullet(ComponentWrapper cWeapon, WeaponInfo& wi); + //void dealDamage(WeaponInfo& wi, glm::vec3 direction, double damage); + + // Utility + bool canFire(ComponentWrapper cWeapon); + bool playerInFirstPerson(EntityWrapper player); + //float traceRayDistance(glm::vec3 origin, glm::vec3 direction); +}; \ No newline at end of file diff --git a/include/Game/Systems/Weapon/WeaponBehaviour.h b/include/Game/Systems/Weapon/WeaponBehaviour.h index f23269df..e0783bd2 100644 --- a/include/Game/Systems/Weapon/WeaponBehaviour.h +++ b/include/Game/Systems/Weapon/WeaponBehaviour.h @@ -7,6 +7,7 @@ #include "Collision/EntityAABB.h" #include "Input/EInputCommand.h" #include "Systems/SpawnerSystem.h" +#include "Rendering/ESetCamera.h" template class WeaponBehaviour : public PureSystem @@ -21,41 +22,81 @@ public: , m_CollisionOctree(collisionOctree) { EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &WeaponBehaviour::_OnInputCommand) + EVENT_SUBSCRIBE_MEMBER(m_ESetCamera, &WeaponBehaviour::_OnSetCamera) } virtual ~WeaponBehaviour() = default; - virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt) override + virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& cWeapon, double dt) override { auto weapon = getActiveWeapon(entity); if (!weapon) { return; } else { - UpdateWeapon(*weapon, dt); + UpdateWeapon(cWeapon, *weapon, dt); } } protected: struct WeaponInfo { - std::string WeaponComponent; EntityWrapper Player; EntityWrapper WeaponEntity; EntityWrapper FirstPersonEntity; EntityWrapper ThirdPersonEntity; - ComponentWrapper GetComponent() { return WeaponEntity[WeaponComponent]; } }; IRenderer* m_Renderer; + EntityWrapper m_CurrentCamera; Octree* m_CollisionOctree; std::unordered_map m_ActiveWeapons; - virtual void UpdateWeapon(WeaponInfo& wi, double dt) { } - virtual void OnPrimaryFire(WeaponInfo& wi) { } - virtual void OnCeasePrimaryFire(WeaponInfo& wi) { } - virtual void OnReload(WeaponInfo& wi) { } - virtual bool OnInputCommand(WeaponInfo& wi, const Events::InputCommand& e) { return false; } + virtual void UpdateWeapon(ComponentWrapper cWeapon, WeaponInfo& wi, double dt) { } + virtual void OnPrimaryFire(ComponentWrapper cWeapon, WeaponInfo& wi) { } + virtual void OnCeasePrimaryFire(ComponentWrapper cWeapon, WeaponInfo& wi) { } + virtual void OnReload(ComponentWrapper cWeapon, WeaponInfo& wi) { } + virtual void OnEquip(ComponentWrapper cWeapon, WeaponInfo& wi) { } + virtual void OnHolster(ComponentWrapper cWeapon, WeaponInfo& wi) { } + virtual bool OnInputCommand(ComponentWrapper cWeapon, WeaponInfo& wi, const Events::InputCommand& e) { return false; } + + bool isPlayerInFirstPerson(EntityWrapper player) + { + if (!m_CurrentCamera.Valid()) { + return false; + } else { + return m_CurrentCamera == player || m_CurrentCamera.IsChildOf(player); + } + } + + // Returns wi.FirstPersonEntity or wi.ThirdPersonEntity depending on + // if the player is in first person mode or not. + EntityWrapper getRelevantWeaponModelEntity(WeaponInfo& wi) + { + if (isPlayerInFirstPerson(wi.Player)) { + return wi.FirstPersonEntity; + } else { + return wi.ThirdPersonEntity; + } + } + + float traceRayDistance(glm::vec3 origin, glm::vec3 direction) + { + float distance; + glm::vec3 pos; + auto entity = Collision::EntityFirstHitByRay(Ray(origin, direction), m_CollisionOctree, distance, pos); + if (entity) { + return distance; + } else { + return 100.f; + } + } private: + EventRelay m_ESetCamera; + bool _OnSetCamera(const Events::SetCamera& e) + { + m_CurrentCamera = e.CameraEntity; + return true; + } EventRelay m_EInputCommand; bool _OnInputCommand(const Events::InputCommand& e) { @@ -70,15 +111,19 @@ private: } // Make sure the player has this weapon - auto weapon = getWeaponComponent(player); - if (!weapon) { + auto cWeapon = getWeaponComponent(player); + if (!cWeapon) { return false; } // Weapon selection if (e.Command == "SelectWeapon") { - if (static_cast(e.Value) == static_cast((*weapon)["Slot"])) { - selectWeapon(player); + if (e.Value > 0) { + if (static_cast(e.Value) == static_cast((*cWeapon)["Slot"])) { + selectWeapon(*cWeapon, player); + } else { + holsterWeapon(*cWeapon, player); + } } } @@ -91,18 +136,18 @@ private: // Fire if (e.Command == "PrimaryFire") { if (e.Value > 0) { - OnPrimaryFire(*activeWeapon); + OnPrimaryFire(*cWeapon, *activeWeapon); } else { - OnCeasePrimaryFire(*activeWeapon); + OnCeasePrimaryFire(*cWeapon, *activeWeapon); } } // Reload if (e.Command == "Reload" && e.Value != 0) { - OnReload(*activeWeapon); + OnReload(*cWeapon, *activeWeapon); } - return OnInputCommand(*activeWeapon, e); + return OnInputCommand(*cWeapon, *activeWeapon, e); } boost::optional getWeaponComponent(EntityWrapper player) @@ -129,8 +174,13 @@ private: return activeWeapon; } - void selectWeapon(EntityWrapper player) + void selectWeapon(ComponentWrapper cWeapon, EntityWrapper player) { + // Don't reselect weapon if it's already active + if (getActiveWeapon(player)) { + return; + } + // Find the weapon attachments matching the weapon type std::vector weaponAttachments = player.ChildrenWithComponent("WeaponAttachment"); EntityWrapper firstPersonAttachment; @@ -152,14 +202,6 @@ private: return; } - // Purge other weapon entities - for (auto& attachment : weaponAttachments) { - //if (attachment == firstPersonAttachment || attachment == thirdPersonAttachment) { - // continue; - //} - attachment.DeleteChildren(); - } - // Spawn the weapon(s) EntityWrapper firstPersonWeapon; EntityWrapper thirdPersonWeapon; @@ -170,11 +212,36 @@ private: thirdPersonWeapon = SpawnerSystem::Spawn(thirdPersonAttachment, thirdPersonAttachment); } - m_ActiveWeapons[player].WeaponComponent = m_ComponentType; - m_ActiveWeapons[player].Player = player; - m_ActiveWeapons[player].WeaponEntity = player; - m_ActiveWeapons[player].FirstPersonEntity = firstPersonWeapon; - m_ActiveWeapons[player].ThirdPersonEntity = thirdPersonWeapon; + WeaponInfo& wi = m_ActiveWeapons[player]; + wi.Player = player; + wi.WeaponEntity = player; + wi.FirstPersonEntity = firstPersonWeapon; + wi.ThirdPersonEntity = thirdPersonWeapon; + + OnEquip(cWeapon, wi); + } + + void holsterWeapon(ComponentWrapper cWeapon, EntityWrapper player) + { + auto activeWeapon = getActiveWeapon(player); + if (!activeWeapon) { + return; + } + WeaponInfo& wi = *activeWeapon; + + // Send holster event + OnHolster(cWeapon, wi); + + // Delete weapon entities + if (wi.FirstPersonEntity.Valid()) { + m_World->DeleteEntity(wi.FirstPersonEntity.ID); + } + if (wi.ThirdPersonEntity.Valid()) { + m_World->DeleteEntity(wi.ThirdPersonEntity.ID); + } + + // Make weapon inactive + m_ActiveWeapons.erase(player); } }; diff --git a/resources/Schema/Components.xsd b/resources/Schema/Components.xsd index 48f3ca04..06151577 100644 --- a/resources/Schema/Components.xsd +++ b/resources/Schema/Components.xsd @@ -45,7 +45,7 @@ - + @@ -55,6 +55,7 @@ + diff --git a/resources/Schema/Components/AmmunitionHUD.xml b/resources/Schema/Components/AmmunitionHUD.xml deleted file mode 100644 index 63b86150..00000000 --- a/resources/Schema/Components/AmmunitionHUD.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/resources/Schema/Components/AmmunitionHUD.xsd b/resources/Schema/Components/AmmunitionHUD.xsd deleted file mode 100644 index 1a48d8d1..00000000 --- a/resources/Schema/Components/AmmunitionHUD.xsd +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - 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. - - - \ No newline at end of file diff --git a/resources/Schema/Components/DefenderWeapon.xml b/resources/Schema/Components/DefenderWeapon.xml index 998f3bde..1b336fc4 100755 --- a/resources/Schema/Components/DefenderWeapon.xml +++ b/resources/Schema/Components/DefenderWeapon.xml @@ -11,6 +11,8 @@ 0.01 0.5 - false - 0 + false + 0 + false + 0 \ No newline at end of file diff --git a/resources/Schema/Components/DefenderWeapon.xsd b/resources/Schema/Components/DefenderWeapon.xsd index 3fe5a64a..c1b98e2f 100755 --- a/resources/Schema/Components/DefenderWeapon.xsd +++ b/resources/Schema/Components/DefenderWeapon.xsd @@ -4,6 +4,18 @@ + + + + + + + + + + + + @@ -36,8 +48,10 @@ Time it takes to load ONE SHELL into the weapon in seconds - - + + + + diff --git a/resources/Schema/Components/SidearmWeapon.xml b/resources/Schema/Components/SidearmWeapon.xml new file mode 100644 index 00000000..1d503ecc --- /dev/null +++ b/resources/Schema/Components/SidearmWeapon.xml @@ -0,0 +1,16 @@ + + + 16 + 16 + 20 + 500 + false + 0.01 + 0.5 + 0.5 + + false + 0 + false + 0 + \ No newline at end of file diff --git a/resources/Schema/Components/SidearmWeapon.xsd b/resources/Schema/Components/SidearmWeapon.xsd new file mode 100644 index 00000000..bafb9de2 --- /dev/null +++ b/resources/Schema/Components/SidearmWeapon.xsd @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + Ammo currently loaded into the magazine + + + Max number of rounds in a magazine + + + Damage dealt if all shotgun pellets hit + + + Rate of fire in rounds per minute + + + + View punch in radians for each shell fired + + + Time it takes to load ONE SHELL into the weapon in seconds + + + Time it takes from selecting the weapon until it's ready to fire + + + + + + + + + + diff --git a/resources/Schema/Components/TextFieldReader.xml b/resources/Schema/Components/TextFieldReader.xml new file mode 100644 index 00000000..52430804 --- /dev/null +++ b/resources/Schema/Components/TextFieldReader.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/resources/Schema/Components/TextFieldReader.xsd b/resources/Schema/Components/TextFieldReader.xsd new file mode 100644 index 00000000..7c77890d --- /dev/null +++ b/resources/Schema/Components/TextFieldReader.xsd @@ -0,0 +1,21 @@ + + + + + + Reads a value from a specific field of a compoent of parent entity and writes it to the Text component on this entity. + + + + The name of the parent entity to read the component field from. Leave empty to read from this entity. + + + The component type to read the field value from. + + + The field name to read the value from. + + + + + \ No newline at end of file diff --git a/resources/Schema/Entities/DefenderWeaponView.xml b/resources/Schema/Entities/DefenderWeaponView.xml index f6b6e89d..0886ac67 100755 --- a/resources/Schema/Entities/DefenderWeaponView.xml +++ b/resources/Schema/Entities/DefenderWeaponView.xml @@ -1,16 +1,12 @@ - + - - R_Arm_Weapon_Joint - - Models/Weapons/Blue/DefenderGunBlue.mesh - + @@ -37,7 +33,6 @@ - @@ -65,8 +60,13 @@ + + Player + DefenderWeapon + MagazineAmmo + - 32 + 0 Fonts/DroidSans.ttf,64 @@ -78,8 +78,13 @@ + + Player + DefenderWeapon + Ammo + - 360 + 0 Fonts/DroidSans.ttf,64 diff --git a/resources/Schema/Entities/Ray2Red b/resources/Schema/Entities/Ray2Red new file mode 100644 index 00000000..813443b2 --- /dev/null +++ b/resources/Schema/Entities/Ray2Red @@ -0,0 +1,18 @@ + + + + + + Textures/Effects/Ray.png + + + + + + + + + + + + diff --git a/resources/Schema/Entities/Ray2Red.xml b/resources/Schema/Entities/Ray2Red.xml new file mode 100644 index 00000000..6c7c3248 --- /dev/null +++ b/resources/Schema/Entities/Ray2Red.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + Textures/Effects/Ray.png + + + + + + + + + + + + + + + Textures/Effects/Ray.png + + + + + + + + + + + + + + diff --git a/resources/Schema/Entities/SidearmWeaponView.xml b/resources/Schema/Entities/SidearmWeaponView.xml new file mode 100644 index 00000000..d3dcda66 --- /dev/null +++ b/resources/Schema/Entities/SidearmWeaponView.xml @@ -0,0 +1,97 @@ + + + + + + Models/Weapons/SecondaryWeapon.mesh + + + + + + + + + + + Schema/Entities/Ray2Red.xml + + + + + + + + + + + + + + + + + + + + + + + + Textures/Core/UnitHexagon.png + + + + + + + + + + + + + + + + + + + Player + SidearmWeapon + MagazineAmmo + + + 16 + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + 8 + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + + + + diff --git a/resources/Schema/Entities/SidearmWeaponWorld.xml b/resources/Schema/Entities/SidearmWeaponWorld.xml new file mode 100644 index 00000000..21cb26a7 --- /dev/null +++ b/resources/Schema/Entities/SidearmWeaponWorld.xml @@ -0,0 +1,27 @@ + + + + + + Models/Weapons/SecondaryWeapon.mesh + + + + + + + + + + + Schema/Entities/RayBlue.xml + + + + + + + + + + diff --git a/resources/Schema/Types/Entity.xsd b/resources/Schema/Types/Entity.xsd index 9f364e11..8bcb9bfd 100644 --- a/resources/Schema/Types/Entity.xsd +++ b/resources/Schema/Types/Entity.xsd @@ -43,7 +43,7 @@ - + @@ -53,6 +53,7 @@ + diff --git a/src/Engine/Core/EntityWrapper.cpp b/src/Engine/Core/EntityWrapper.cpp index b3bef55a..3c217353 100644 --- a/src/Engine/Core/EntityWrapper.cpp +++ b/src/Engine/Core/EntityWrapper.cpp @@ -34,6 +34,18 @@ EntityWrapper EntityWrapper::Parent() } } +EntityWrapper EntityWrapper::FirstParentByName(const std::string& parentEntityName) +{ + EntityWrapper entity = *this; + while (entity.Parent().Valid()) { + entity = entity.Parent(); + if (entity.Name() == parentEntityName) { + return entity; + } + } + return EntityWrapper::Invalid; +} + EntityWrapper EntityWrapper::FirstChildByName(const std::string& name) { return firstChildByNameRecursive(name, this->ID); diff --git a/src/Engine/Editor/EditorGUI.cpp b/src/Engine/Editor/EditorGUI.cpp index 8ce15d0a..4e7c8ab6 100644 --- a/src/Engine/Editor/EditorGUI.cpp +++ b/src/Engine/Editor/EditorGUI.cpp @@ -338,6 +338,15 @@ bool EditorGUI::drawComponentNode(EntityWrapper entity, const ComponentInfo& ci) } } + if (ci.Name == "Spawner") { + if (ImGui::Button("Activate")) { + Events::SpawnerSpawn e; + e.Spawner = entity; + e.Parent = entity; + m_EventBroker->Publish(e); + } + } + return true; } @@ -381,14 +390,52 @@ bool EditorGUI::drawComponentField_Vector(ComponentWrapper &c, const ComponentIn // Limit scale values to a minimum of 0 return ImGui::DragFloat3("", glm::value_ptr(val), 0.1f, 0.f, std::numeric_limits::max()); } else if (field.Name == "Orientation") { - // Make orentations have a period of 2*Pi - glm::vec3 tempVal = glm::fmod(val, glm::vec3(glm::two_pi())); - if (ImGui::SliderFloat3("", glm::value_ptr(tempVal), 0.f, glm::two_pi())) { - val = tempVal; - return true; - } else { - return false; + //glm::vec3 tempVal = val; + glm::vec3 originalVal = val; + + ImVec2 cursorPos = ImGui::GetCursorScreenPos(); + glm::tvec3 isSnapping(false, false, false); + bool changed = ImGui::DragFloat3("", glm::value_ptr(val), 0.066666f); + if (changed) { + // Make orentations have a period of 2*Pi + val = glm::fmod(val, glm::vec3(glm::two_pi())); + for (int i = 0; i < 3; i++) { + if (val[i] < 0) { + val[i] += glm::two_pi(); + } + } } + + // Snap to angle + //float snapRange = glm::pi() / 15.f; + //float snapAngle = glm::quarter_pi(); + //glm::vec3 snap = glm::fmod(val, glm::vec3(snapAngle)); + //for (int i = 0; i < 3; i++) { + // isSnapping[i] = glm::abs(snap[i] - (snapRange / 2.f)) < snapRange; + //} + //if (changed && ImGui::IsMouseDown(0)) { + // glm::vec3 change = val - originalVal; + // for (int i = 0; i < 3; i++) { + // if (isSnapping[i] && glm::abs(change[i]) < snapRange) { + // val[i] -= snap[i] - snapRange; + // } + // } + //} + + // Draw snapping outline + float width = ImGui::CalcItemWidth() / 3.f;; + float spacing = GImGui->Style.ItemInnerSpacing.x; + for (int i = 0; i < 3; i++) { + if (isSnapping[i]) { + ImVec2 pos = cursorPos + ImVec2(i * (width + spacing), 0.f); + ImRect bb(pos - ImVec2(1, 1), pos + ImVec2(width, 17)); + auto window = ImGui::GetCurrentWindow(); + const ImU32 col = window->Color(ImGuiCol_HeaderActive); + window->DrawList->AddRect(bb.Min, bb.Max, col, 3.f); + } + } + + return changed; } else { return ImGui::DragFloat3("", glm::value_ptr(val), 0.1f, std::numeric_limits::lowest(), std::numeric_limits::max()); } diff --git a/src/Engine/Rendering/Renderer.cpp b/src/Engine/Rendering/Renderer.cpp index 3ce985b6..7e11f3b2 100644 --- a/src/Engine/Rendering/Renderer.cpp +++ b/src/Engine/Rendering/Renderer.cpp @@ -17,7 +17,7 @@ void Renderer::Initialize() m_TextPass->Initialize(); /* m_ScreenQuad = ResourceManager::Load("Models/Core/ScreenQuad.obj"); - m_UnitQuad = ResourceManager::Load("Models/Core/UnitQuad.obj"); + m_UnitQuad = ResourceManager::Load(sModels/Core/UnitQuad.obj"); m_UnitSphere = ResourceManager::Load("Models/Core/UnitSphere.obj");*/ m_ImGuiRenderPass = new ImGuiRenderPass(this, m_EventBroker); diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index e2055574..e9e1ea27 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -19,6 +19,7 @@ #include "Game/Systems/AmmoPickupSystem.h" #include "Game/Systems/DamageIndicatorSystem.h" #include "Game/Systems/Weapon/DefenderWeaponBehaviour.h" +#include "Game/Systems/Weapon/SidearmWeaponBehaviour.h" #include "Rendering/AnimationSystem.h" #include "Game/Systems/HealthHUDSystem.h" #include "Rendering/BoneAttachmentSystem.h" @@ -26,7 +27,7 @@ #include "../Engine/Core/UniformScaleSystem.h" #include "Rendering/AnimationSystem.h" #include "Network/MultiplayerSnapshotFilter.h" -#include "Game/Systems/AmmunitionHUDSystem.h" +#include "Game/Systems/TextFieldReader.h" #include "Game/Systems/AbilityCooldownHUDSystem.h" #include "Game/Systems/CapturePointArrowHUDSystem.h" #include "Game/Systems/KillFeedSystem.h" @@ -130,13 +131,14 @@ Game::Game(int argc, char* argv[]) m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel, m_Renderer, m_OctreeCollision); + m_SystemPipeline->AddSystem(updateOrderLevel, m_Renderer, m_OctreeCollision); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); - m_SystemPipeline->AddSystem(updateOrderLevel); + m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); @@ -146,7 +148,7 @@ Game::Game(int argc, char* argv[]) m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); - m_SystemPipeline->AddSystem(updateOrderLevel); + m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel, m_Renderer); diff --git a/src/Game/Systems/AmmunitionHUDSystem.cpp b/src/Game/Systems/AmmunitionHUDSystem.cpp deleted file mode 100644 index c9d87072..00000000 --- a/src/Game/Systems/AmmunitionHUDSystem.cpp +++ /dev/null @@ -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. - - 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"]); - } - } - } -} diff --git a/src/Game/Systems/TextFieldReader.cpp b/src/Game/Systems/TextFieldReader.cpp new file mode 100644 index 00000000..8712a61f --- /dev/null +++ b/src/Game/Systems/TextFieldReader.cpp @@ -0,0 +1,46 @@ +#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& parentEntityName = cAmmunitionHUD["ParentEntityName"]; + EntityWrapper readEntity = entity; + if (!parentEntityName.empty()) { + readEntity = entity.FirstParentByName(parentEntityName); + if (!readEntity.Valid()) { + return; + } + } + + // Find the component to read from + const std::string& componentType = cAmmunitionHUD["ComponentType"]; + 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); + + std::string& text = entity["Text"]["Content"]; + + if (field.Type == "int") { + text = boost::lexical_cast((const int&)component[fieldName]); + } else if (field.Type == "float") { + text = boost::lexical_cast((const float&)component[fieldName]); + } else if (field.Type == "double") { + text = boost::lexical_cast((const double&)component[fieldName]); + } else if (field.Type == "bool") { + text = boost::lexical_cast((const bool&)component[fieldName]); + } else if (field.Type == "string") { + text = (const std::string&)component[fieldName]; + } +} diff --git a/src/Game/Systems/Weapon/DefenderWeaponBehaviour.cpp b/src/Game/Systems/Weapon/DefenderWeaponBehaviour.cpp index 028bd10c..4c0f9673 100644 --- a/src/Game/Systems/Weapon/DefenderWeaponBehaviour.cpp +++ b/src/Game/Systems/Weapon/DefenderWeaponBehaviour.cpp @@ -2,40 +2,76 @@ void DefenderWeaponBehaviour::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cWeapon, double dt) { - (double&)cWeapon["TimeSinceLastFire"] += dt; + 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(); + 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); + double reloadTime = cWeapon["ReloadTime"]; + bool& isReloading = cWeapon["IsReloading"]; + if (isReloading && reloadTimer <= 0.0) { + int& magAmmo = cWeapon["MagazineAmmo"]; + int& magSize = cWeapon["MagazineSize"]; + int& ammo = cWeapon["Ammo"]; + if (magAmmo < magSize && ammo > 0) { + ammo -= 1; + magAmmo += 1; + reloadTimer = reloadTime; + } else { + isReloading = false; + } + } + + 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) +{ + bool& isReloading = cWeapon["IsReloading"]; + if (isReloading) { + return; + } + + int& magAmmo = cWeapon["MagazineAmmo"]; + int& magSize = cWeapon["MagazineSize"]; + if (magAmmo >= magSize) { + return; + } + int& ammo = cWeapon["Ammo"]; + if (ammo <= 0) { + return; + } + + double reloadTime = cWeapon["ReloadTime"]; + double& reloadTimer = cWeapon["ReloadTimer"]; + + // Start reload + isReloading = true; + reloadTimer = reloadTime; +} + +bool DefenderWeaponBehaviour::OnInputCommand(ComponentWrapper cWeapon, WeaponInfo& wi, const Events::InputCommand& e) { if (e.Command == "SpecialAbility" && IsServer) { EntityWrapper attachment = wi.Player.FirstChildByName("ShieldAttachment"); @@ -51,17 +87,23 @@ bool DefenderWeaponBehaviour::OnInputCommand(WeaponInfo& wi, const Events::Input return false; } -bool DefenderWeaponBehaviour::OnSetCamera(const Events::SetCamera& e) +void DefenderWeaponBehaviour::fireShell(ComponentWrapper cWeapon, WeaponInfo& wi) { - m_CurrentCamera = e.CameraEntity; - return true; -} + cWeapon["FireCooldown"] = 60.0 / (double)cWeapon["RPM"]; -void DefenderWeaponBehaviour::fireShell(WeaponInfo& wi) -{ - ComponentWrapper cWeapon = wi.GetComponent(); + // Stop reloading + bool& isReloading = cWeapon["IsReloading"]; + isReloading = false; + + // Ammo + int& magAmmo = cWeapon["MagazineAmmo"]; + if (magAmmo <= 0) { + OnReload(cWeapon, wi); + return; + } else { + magAmmo -= 1; + } - cWeapon["TimeSinceLastFire"] = 0.0; int numPellets = cWeapon["NumPellets"]; float spreadAngle = cWeapon["SpreadAngle"]; std::uniform_real_distribution randomSpreadAngle(-spreadAngle, spreadAngle); @@ -95,13 +137,12 @@ void DefenderWeaponBehaviour::fireShell(WeaponInfo& wi) orientation.x += angles.x; orientation.y += angles.y; glm::vec3 trajectory = direction * distance; - dealDamage(wi, direction, pelletDamage); + dealDamage(cWeapon, wi, direction, pelletDamage); } } - } -void DefenderWeaponBehaviour::dealDamage(WeaponInfo& wi, glm::vec3 direction, double damage) +void DefenderWeaponBehaviour::dealDamage(ComponentWrapper cWeapon, WeaponInfo& wi, glm::vec3 direction, double damage) { // Only deal damage client side if (!IsClient) { @@ -160,16 +201,13 @@ void DefenderWeaponBehaviour::dealDamage(WeaponInfo& wi, glm::vec3 direction, do 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; + // TODO: Ammo checks + return triggerHeld && cooldownPassed && isNotShielding; } Camera DefenderWeaponBehaviour::cameraFromEntity(EntityWrapper camera) diff --git a/src/Game/Systems/Weapon/SidearmWeaponBehaviour.cpp b/src/Game/Systems/Weapon/SidearmWeaponBehaviour.cpp new file mode 100644 index 00000000..9a3ed6ab --- /dev/null +++ b/src/Game/Systems/Weapon/SidearmWeaponBehaviour.cpp @@ -0,0 +1,79 @@ +#include "Systems/Weapon/SidearmWeaponBehaviour.h" + +void SidearmWeaponBehaviour::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cWeapon, double dt) +{ + 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) +{ + 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::OnEquip(ComponentWrapper cWeapon, WeaponInfo& wi) +{ + cWeapon["FireCooldown"] = (double)cWeapon["EquipTime"]; +} + +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; + + LOG_DEBUG("HOLSTER"); +} + +void SidearmWeaponBehaviour::fireBullet(ComponentWrapper cWeapon, WeaponInfo& wi) +{ + cWeapon["FireCooldown"] = 60.0 / (double)cWeapon["RPM"]; + + // Get weapon model based on current person + EntityWrapper weaponModelEntity = getRelevantWeaponModelEntity(wi); + if (!weaponModelEntity.Valid()) { + return; + } + + // Tracer + EntityWrapper tracerSpawner = weaponModelEntity.FirstChildByName("WeaponMuzzle"); + if (tracerSpawner.Valid()) { + glm::vec3 origin = Transform::AbsolutePosition(tracerSpawner); + glm::vec3 direction = Transform::AbsoluteOrientation(tracerSpawner) * glm::vec3(0, 0, -1); + float distance = traceRayDistance(origin, direction); + EntityWrapper ray = SpawnerSystem::Spawn(tracerSpawner); + ((glm::vec3&)ray["Transform"]["Scale"]).z = (distance / 100.f); + } +} + +bool SidearmWeaponBehaviour::canFire(ComponentWrapper cWeapon) +{ + bool triggerHeld = cWeapon["TriggerHeld"]; + double& cooldown = cWeapon["FireCooldown"]; + // TODO: Ammo checks + return triggerHeld && cooldown <= 0.0; +} \ No newline at end of file