diff --git a/include/Game/Systems/Weapon/DefenderWeaponBehaviour.h b/include/Game/Systems/Weapon/DefenderWeaponBehaviour.h index 986d8586..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(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(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 index 787c3c61..d221b8bb 100644 --- a/include/Game/Systems/Weapon/SidearmWeaponBehaviour.h +++ b/include/Game/Systems/Weapon/SidearmWeaponBehaviour.h @@ -15,12 +15,12 @@ public: 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; - EntityWrapper m_CurrentCamera; // Weapon functions void fireBullet(ComponentWrapper cWeapon, WeaponInfo& wi); @@ -28,5 +28,6 @@ private: // 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 2ab53e0e..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,6 +22,7 @@ public: , m_CollisionOctree(collisionOctree) { EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &WeaponBehaviour::_OnInputCommand) + EVENT_SUBSCRIBE_MEMBER(m_ESetCamera, &WeaponBehaviour::_OnSetCamera) } virtual ~WeaponBehaviour() = default; @@ -44,6 +46,7 @@ protected: }; IRenderer* m_Renderer; + EntityWrapper m_CurrentCamera; Octree* m_CollisionOctree; std::unordered_map m_ActiveWeapons; @@ -51,10 +54,49 @@ protected: 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) { @@ -78,7 +120,7 @@ private: if (e.Command == "SelectWeapon") { if (e.Value > 0) { if (static_cast(e.Value) == static_cast((*cWeapon)["Slot"])) { - selectWeapon(player); + selectWeapon(*cWeapon, player); } else { holsterWeapon(*cWeapon, player); } @@ -132,7 +174,7 @@ 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)) { @@ -170,10 +212,13 @@ private: thirdPersonWeapon = SpawnerSystem::Spawn(thirdPersonAttachment, thirdPersonAttachment); } - 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) 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 0ec61964..c1b98e2f 100755 --- a/resources/Schema/Components/DefenderWeapon.xsd +++ b/resources/Schema/Components/DefenderWeapon.xsd @@ -48,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 index b63706b6..1d503ecc 100644 --- a/resources/Schema/Components/SidearmWeapon.xml +++ b/resources/Schema/Components/SidearmWeapon.xml @@ -3,9 +3,11 @@ 16 16 20 - 120 + 500 + false 0.01 0.5 + 0.5 false 0 diff --git a/resources/Schema/Components/SidearmWeapon.xsd b/resources/Schema/Components/SidearmWeapon.xsd index 844b449f..bafb9de2 100644 --- a/resources/Schema/Components/SidearmWeapon.xsd +++ b/resources/Schema/Components/SidearmWeapon.xsd @@ -23,7 +23,7 @@ Ammo currently loaded into the magazine - Max number of rounds in a magazine + Max number of rounds in a magazine Damage dealt if all shotgun pellets hit @@ -31,12 +31,16 @@ 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/Entities/Player.xml b/resources/Schema/Entities/Player.xml index 394f4622..5c5efc07 100644 --- a/resources/Schema/Entities/Player.xml +++ b/resources/Schema/Entities/Player.xml @@ -8,7 +8,7 @@ - 52.867678870419283 + 102.85760837900634 @@ -454,7 +454,7 @@ Idle - 0.022133545026491674 + 1.8348644854054612 1 @@ -478,8 +478,8 @@ Schema/Entities/DefenderWeaponView.xml - - + + @@ -497,8 +497,8 @@ Schema/Entities/SidearmWeaponView.xml - - + + @@ -528,7 +528,7 @@ Idle - 0.13373697879978863 + 0.013134522267137072 1 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 index 68d29676..f01ffdf7 100644 --- a/resources/Schema/Entities/SidearmWeaponView.xml +++ b/resources/Schema/Entities/SidearmWeaponView.xml @@ -14,7 +14,7 @@ - Schema/Entities/RayBlue.xml + Schema/Entities/Ray2Red.xml @@ -22,6 +22,67 @@ + + + + + + + + + + + + + Textures/Core/UnitHexagon.png + + + + + + + + + + + + + + + + + + + 16 + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + 8 + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + diff --git a/src/Engine/Editor/EditorGUI.cpp b/src/Engine/Editor/EditorGUI.cpp index a1ceafd0..4e7c8ab6 100644 --- a/src/Engine/Editor/EditorGUI.cpp +++ b/src/Engine/Editor/EditorGUI.cpp @@ -390,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/Game/Systems/Weapon/DefenderWeaponBehaviour.cpp b/src/Game/Systems/Weapon/DefenderWeaponBehaviour.cpp index 0a179b4c..4c0f9673 100644 --- a/src/Game/Systems/Weapon/DefenderWeaponBehaviour.cpp +++ b/src/Game/Systems/Weapon/DefenderWeaponBehaviour.cpp @@ -2,33 +2,73 @@ 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(ComponentWrapper cWeapon, WeaponInfo& wi, double 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) { + double& reloadTimer = cWeapon["ReloadTimer"]; + reloadTimer = glm::max(0.0, reloadTimer - dt); + + 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(ComponentWrapper cWeapon, WeaponInfo& wi) { - cWeapon["IsFiring"] = true; - bool cooldownPassed = (double)cWeapon["TimeSinceLastFire"] >= (60.0 / (double)cWeapon["RPM"]); - bool isNotShielding = wi.Player.ChildrenWithComponent("Shield").size() == 0; - if (cooldownPassed && isNotShielding) { + cWeapon["TriggerHeld"] = true; + if (canFire(cWeapon, wi)) { fireShell(cWeapon, wi); } } void DefenderWeaponBehaviour::OnCeasePrimaryFire(ComponentWrapper cWeapon, WeaponInfo& wi) { - cWeapon["IsFiring"] = false; + cWeapon["TriggerHeld"] = false; +} + +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) @@ -47,15 +87,23 @@ bool DefenderWeaponBehaviour::OnInputCommand(ComponentWrapper cWeapon, WeaponInf return false; } -bool DefenderWeaponBehaviour::OnSetCamera(const Events::SetCamera& e) -{ - m_CurrentCamera = e.CameraEntity; - return true; -} - void DefenderWeaponBehaviour::fireShell(ComponentWrapper cWeapon, WeaponInfo& wi) { - cWeapon["TimeSinceLastFire"] = 0.0; + cWeapon["FireCooldown"] = 60.0 / (double)cWeapon["RPM"]; + + // Stop reloading + bool& isReloading = cWeapon["IsReloading"]; + isReloading = false; + + // Ammo + int& magAmmo = cWeapon["MagazineAmmo"]; + if (magAmmo <= 0) { + OnReload(cWeapon, wi); + return; + } else { + magAmmo -= 1; + } + int numPellets = cWeapon["NumPellets"]; float spreadAngle = cWeapon["SpreadAngle"]; std::uniform_real_distribution randomSpreadAngle(-spreadAngle, spreadAngle); @@ -92,7 +140,6 @@ void DefenderWeaponBehaviour::fireShell(ComponentWrapper cWeapon, WeaponInfo& wi dealDamage(cWeapon, wi, direction, pelletDamage); } } - } void DefenderWeaponBehaviour::dealDamage(ComponentWrapper cWeapon, WeaponInfo& wi, glm::vec3 direction, double damage) @@ -154,16 +201,13 @@ void DefenderWeaponBehaviour::dealDamage(ComponentWrapper cWeapon, WeaponInfo& w 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 index 8c3906af..9a3ed6ab 100644 --- a/src/Game/Systems/Weapon/SidearmWeaponBehaviour.cpp +++ b/src/Game/Systems/Weapon/SidearmWeaponBehaviour.cpp @@ -14,7 +14,7 @@ void SidearmWeaponBehaviour::UpdateComponent(EntityWrapper& entity, ComponentWra void SidearmWeaponBehaviour::UpdateWeapon(ComponentWrapper cWeapon, WeaponInfo& wi, double dt) { - if (canFire(cWeapon)) { + if ((bool)cWeapon["Automatic"] && canFire(cWeapon)) { fireBullet(cWeapon, wi); } } @@ -32,6 +32,11 @@ void SidearmWeaponBehaviour::OnCeasePrimaryFire(ComponentWrapper cWeapon, Weapon 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 @@ -46,7 +51,23 @@ void SidearmWeaponBehaviour::OnHolster(ComponentWrapper cWeapon, WeaponInfo& wi) 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)