From fd11f9c7e33cf68ebdb02a5ddbe20a64e8d04438 Mon Sep 17 00:00:00 2001 From: Daniel Evans Date: Mon, 31 Dec 2018 23:13:18 +0000 Subject: [PATCH 1/6] Melee animation handling --- rwengine/src/ai/CharacterController.cpp | 28 ++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/rwengine/src/ai/CharacterController.cpp b/rwengine/src/ai/CharacterController.cpp index cd1cf7c2..df45cfc0 100644 --- a/rwengine/src/ai/CharacterController.cpp +++ b/rwengine/src/ai/CharacterController.cpp @@ -688,9 +688,31 @@ bool Activities::UseItem::update(CharacterObject *character, character->playCycle(shootcycle); } } else if (weapon->fireType == WeaponData::MELEE) { - RW_CHECK(weapon->fireType != WeaponData::MELEE, - "Melee attacks not implemented"); - return true; + /// @todo second attack animation for on-ground targets + const auto attackAnimation = shootcycle; + + if (character->getCurrentCycle() != attackAnimation) { + character->playCycle(attackAnimation); + } + + auto fireTime = weapon->animFirePoint / 100.f; + auto loopStart = weapon->animLoopStart / 100.f; + auto currentTime = animator->getAnimationTime(AnimIndexAction); + + if (currentTime >= fireTime && !fired) { + /// @todo weapon hit here + fired = true; + } + + if (animator->isCompleted(AnimIndexAction)) { + if (character->getCurrentState().primaryActive) { + animator->setAnimationTime(AnimIndexAction, loopStart); + fired = false; + } + else { + return true; + } + } } else { RW_ERROR("Unrecognized fireType: " << weapon->fireType); return true; From 62637ccc291e73d69f803a012e7b8549b3f37c45 Mon Sep 17 00:00:00 2001 From: Daniel Evans Date: Tue, 1 Jan 2019 12:12:37 +0000 Subject: [PATCH 2/6] Move WeaponScan to Weapon.hpp, add scan source field --- rwengine/src/data/WeaponData.hpp | 45 ---------------------------- rwengine/src/engine/GameWorld.cpp | 3 +- rwengine/src/items/Weapon.cpp | 4 +++ rwengine/src/items/Weapon.hpp | 50 +++++++++++++++++++++++++++++++ tests/test_Weapon.cpp | 26 +++++++++++++++- 5 files changed, 81 insertions(+), 47 deletions(-) diff --git a/rwengine/src/data/WeaponData.hpp b/rwengine/src/data/WeaponData.hpp index edee94eb..f00a3fbe 100644 --- a/rwengine/src/data/WeaponData.hpp +++ b/rwengine/src/data/WeaponData.hpp @@ -34,49 +34,4 @@ struct WeaponData { std::uint32_t inventorySlot; }; -/** - * @brief simple object for performing weapon checks against the world - * - * @todo RADIUS hitscans - */ -struct WeaponScan { - enum ScanType { - /** Instant-hit ray weapons */ - HITSCAN, - /** Area of effect attack */ - RADIUS, - }; - - const ScanType type; - - float damage; - - glm::vec3 center{}; - float radius; - - glm::vec3 end{}; - - WeaponData* weapon; - - // Constructor for a RADIUS hitscan - WeaponScan(float damage, const glm::vec3& center, float radius, - WeaponData* weapon = nullptr) - : type(RADIUS) - , damage(damage) - , center(center) - , radius(radius) - , weapon(weapon) { - } - - // Constructor for a ray hitscan - WeaponScan(float damage, const glm::vec3& start, const glm::vec3& end, - WeaponData* weapon = nullptr) - : type(HITSCAN) - , damage(damage) - , center(start) - , end(end) - , weapon(weapon) { - } -}; - #endif diff --git a/rwengine/src/engine/GameWorld.cpp b/rwengine/src/engine/GameWorld.cpp index 41bfd7b3..2a2a5134 100644 --- a/rwengine/src/engine/GameWorld.cpp +++ b/rwengine/src/engine/GameWorld.cpp @@ -25,7 +25,8 @@ #include "data/CutsceneData.hpp" #include "data/InstanceData.hpp" -#include "data/WeaponData.hpp" + +#include "items/Weapon.hpp" #include "loaders/LoaderCutsceneDAT.hpp" #include "loaders/LoaderIFP.hpp" diff --git a/rwengine/src/items/Weapon.cpp b/rwengine/src/items/Weapon.cpp index 0dded254..de9b383a 100644 --- a/rwengine/src/items/Weapon.cpp +++ b/rwengine/src/items/Weapon.cpp @@ -9,6 +9,10 @@ #include "objects/CharacterObject.hpp" #include "objects/ProjectileObject.hpp" +bool WeaponScan::doesDamage(GameObject* target) const { + return target != source; +} + void Weapon::fireHitscan(WeaponData* weapon, CharacterObject* owner) { auto handFrame = owner->getClump()->findFrame("srhand"); glm::mat4 handMatrix = handFrame->getWorldTransform(); diff --git a/rwengine/src/items/Weapon.hpp b/rwengine/src/items/Weapon.hpp index 72e4ac46..14c1e8aa 100644 --- a/rwengine/src/items/Weapon.hpp +++ b/rwengine/src/items/Weapon.hpp @@ -1,9 +1,59 @@ #ifndef _RWENGINE_WEAPON_HPP_ #define _RWENGINE_WEAPON_HPP_ +#include class CharacterObject; +class GameObject; struct WeaponData; +/** + * @brief simple object for performing weapon checks against the world + */ +struct WeaponScan { + enum ScanType { + /** Instant-hit ray weapons */ + HITSCAN, + /** Area of effect attack */ + RADIUS, + }; + + const ScanType type; + + float damage; + + glm::vec3 center{}; + float radius; + + glm::vec3 end{}; + + WeaponData* weapon; + GameObject* source; + + // Constructor for a RADIUS hitscan + WeaponScan(float damage, const glm::vec3& center, float radius, + WeaponData* weapon = nullptr, GameObject* source = nullptr) + : type(RADIUS) + , damage(damage) + , center(center) + , radius(radius) + , weapon(weapon) + , source(source) { + } + + // Constructor for a ray hitscan + WeaponScan(float damage, const glm::vec3& start, const glm::vec3& end, + WeaponData* weapon = nullptr, GameObject* source = nullptr) + : type(HITSCAN) + , damage(damage) + , center(start) + , end(end) + , weapon(weapon) + , source(source) { + } + + bool doesDamage(GameObject* target) const; +}; + namespace Weapon { void fireProjectile(WeaponData* weapon, CharacterObject* character, float force); void fireHitscan(WeaponData *weapon, CharacterObject* character); diff --git a/tests/test_Weapon.cpp b/tests/test_Weapon.cpp index ccacda4d..8edbeb5a 100644 --- a/tests/test_Weapon.cpp +++ b/tests/test_Weapon.cpp @@ -1,13 +1,37 @@ #include #include +#include #include #include #include "test_Globals.hpp" BOOST_AUTO_TEST_SUITE(WeaponTests) +BOOST_AUTO_TEST_CASE(radius_ctor_creates_radius_scan) { + WeaponScan scan{10.f, {1.f, 1.f, 1.f}, 5.f}; + BOOST_CHECK_EQUAL(scan.type, WeaponScan::RADIUS); +} + +BOOST_AUTO_TEST_CASE(hitscan_ctor_creates_radius_scan) { + WeaponScan scan{10.f, {1.f, 1.f, 1.f}, {0.f, 0.f, 0.f}}; + BOOST_CHECK_EQUAL(scan.type, WeaponScan::HITSCAN); +} + +struct WeaponScanFixture { + GameObject* source = reinterpret_cast(0x0000BEEF); + WeaponScan scan{10.f, {1.f, 1.f, 1.f}, {0.f, 0.f, 0.f}, nullptr, source}; +}; + +BOOST_FIXTURE_TEST_CASE(weapon_scan_doesnt_injur_source, WeaponScanFixture) { + BOOST_CHECK(!scan.doesDamage(reinterpret_cast(0x0000BEEF))); +} + +BOOST_FIXTURE_TEST_CASE(weapon_scan_does_injur_others, WeaponScanFixture) { + BOOST_CHECK(scan.doesDamage(reinterpret_cast(0xDEADBEEF))); +} + #if RW_TEST_WITH_DATA -BOOST_AUTO_TEST_CASE(TestWeaponScan) { +BOOST_AUTO_TEST_CASE(TestDoWeaponScan) { { // Test RADIUS scan auto character = Global::get().e->createPedestrian(1, {0.f, 0.f, 0.f}); From c307a1c5e344f548c74cb4ac06fad9d17deaf3af Mon Sep 17 00:00:00 2001 From: Daniel Evans Date: Tue, 1 Jan 2019 22:14:55 +0000 Subject: [PATCH 3/6] Implement melee weapon damage --- rwengine/src/ai/CharacterController.cpp | 2 +- rwengine/src/engine/GameWorld.cpp | 22 +++++++++++++++++----- rwengine/src/items/Weapon.cpp | 13 ++++++++++++- rwengine/src/items/Weapon.hpp | 1 + rwengine/src/objects/GameObject.hpp | 2 +- 5 files changed, 32 insertions(+), 8 deletions(-) diff --git a/rwengine/src/ai/CharacterController.cpp b/rwengine/src/ai/CharacterController.cpp index df45cfc0..5b775ca6 100644 --- a/rwengine/src/ai/CharacterController.cpp +++ b/rwengine/src/ai/CharacterController.cpp @@ -700,7 +700,7 @@ bool Activities::UseItem::update(CharacterObject *character, auto currentTime = animator->getAnimationTime(AnimIndexAction); if (currentTime >= fireTime && !fired) { - /// @todo weapon hit here + Weapon::meleeHit(weapon, character); fired = true; } diff --git a/rwengine/src/engine/GameWorld.cpp b/rwengine/src/engine/GameWorld.cpp index 2a2a5134..654ac79f 100644 --- a/rwengine/src/engine/GameWorld.cpp +++ b/rwengine/src/engine/GameWorld.cpp @@ -23,6 +23,8 @@ #include "ai/PlayerController.hpp" #include "ai/TrafficDirector.hpp" +#include "dynamics/HitTest.hpp" + #include "data/CutsceneData.hpp" #include "data/InstanceData.hpp" @@ -566,12 +568,22 @@ void GameWorld::destroyEffect(VisualFX& effect) { } void GameWorld::doWeaponScan(const WeaponScan& scan) { - RW_CHECK(scan.type != WeaponScan::RADIUS, - "Radius scans not implemented yet"); - if (scan.type == WeaponScan::RADIUS) { - // TODO - // Requires custom ConvexResultCallback + HitTest test {*dynamicsWorld}; + const auto result = test.sphereTest(scan.center, scan.radius); + + for(const auto& target : result) { + if (!scan.doesDamage(target.object)) { + continue; + } + + GameObject::DamageInfo di; + di.damageSource = scan.center; + di.type = GameObject::DamageInfo::Melee; + di.hitpoints = scan.damage; + target.object->takeDamage(di); + } + } else if (scan.type == WeaponScan::HITSCAN) { btVector3 from(scan.center.x, scan.center.y, scan.center.z), to(scan.end.x, scan.end.y, scan.end.z); diff --git a/rwengine/src/items/Weapon.cpp b/rwengine/src/items/Weapon.cpp index de9b383a..fd3464fd 100644 --- a/rwengine/src/items/Weapon.cpp +++ b/rwengine/src/items/Weapon.cpp @@ -22,7 +22,7 @@ void Weapon::fireHitscan(WeaponData* weapon, CharacterObject* owner) { auto fireOrigin = glm::vec3(handMatrix[3]); float dmg = static_cast(weapon->damage); - owner->engine->doWeaponScan({dmg, fireOrigin, rayend, weapon}); + owner->engine->doWeaponScan({dmg, fireOrigin, rayend, weapon, owner}); } void Weapon::fireProjectile(WeaponData* weapon, CharacterObject* owner, @@ -49,3 +49,14 @@ void Weapon::fireProjectile(WeaponData* weapon, CharacterObject* owner, pool.insert(std::move(projectile)); owner->engine->allObjects.push_back(ptr); } + +void Weapon::meleeHit(WeaponData* weapon, CharacterObject* character) { + const auto center = character->getPosition() + character->getRotation() + * weapon->fireOffset; + auto e = character->engine; + e->doWeaponScan({ + static_cast(weapon->damage), + center, weapon->meleeRadius, weapon, + character + }); +} diff --git a/rwengine/src/items/Weapon.hpp b/rwengine/src/items/Weapon.hpp index 14c1e8aa..dd601355 100644 --- a/rwengine/src/items/Weapon.hpp +++ b/rwengine/src/items/Weapon.hpp @@ -57,6 +57,7 @@ struct WeaponScan { namespace Weapon { void fireProjectile(WeaponData* weapon, CharacterObject* character, float force); void fireHitscan(WeaponData *weapon, CharacterObject* character); +void meleeHit(WeaponData *weapon, CharacterObject* character); } #endif diff --git a/rwengine/src/objects/GameObject.hpp b/rwengine/src/objects/GameObject.hpp index 83022832..f9ada284 100644 --- a/rwengine/src/objects/GameObject.hpp +++ b/rwengine/src/objects/GameObject.hpp @@ -164,7 +164,7 @@ public: } struct DamageInfo { - enum DamageType { Explosion, Burning, Bullet, Physics }; + enum DamageType { Explosion, Burning, Bullet, Physics, Melee }; /** * World position of damage From f89471818356539e77342903dd2a8acdce9b8a07 Mon Sep 17 00:00:00 2001 From: Daniel Evans Date: Wed, 2 Jan 2019 21:02:51 +0000 Subject: [PATCH 4/6] Melee against on-ground target uses alternate anim --- rwengine/src/ai/CharacterController.cpp | 44 ++++++++++++++---------- rwengine/src/items/Weapon.cpp | 19 ++++++++++ rwengine/src/items/Weapon.hpp | 1 + rwengine/src/objects/CharacterObject.cpp | 7 ++++ rwengine/src/objects/CharacterObject.hpp | 2 ++ 5 files changed, 54 insertions(+), 19 deletions(-) diff --git a/rwengine/src/ai/CharacterController.cpp b/rwengine/src/ai/CharacterController.cpp index 5b775ca6..bb412580 100644 --- a/rwengine/src/ai/CharacterController.cpp +++ b/rwengine/src/ai/CharacterController.cpp @@ -688,31 +688,37 @@ bool Activities::UseItem::update(CharacterObject *character, character->playCycle(shootcycle); } } else if (weapon->fireType == WeaponData::MELEE) { - /// @todo second attack animation for on-ground targets - const auto attackAnimation = shootcycle; + auto currentAnim = character->getCurrentCycle(); + if (currentAnim == shootcycle || currentAnim == throwcycle) { + auto fireTime = weapon->animFirePoint / 100.f; + auto loopStart = weapon->animLoopStart / 100.f; + auto currentTime = animator->getAnimationTime(AnimIndexAction); - if (character->getCurrentCycle() != attackAnimation) { - character->playCycle(attackAnimation); + if (currentTime >= fireTime && !fired) { + Weapon::meleeHit(weapon, character); + fired = true; + } + + if (animator->isCompleted(AnimIndexAction)) { + if (character->getCurrentState().primaryActive) { + animator->setAnimationTime(AnimIndexAction, loopStart); + fired = false; + } + else { + return true; + } + } } - - auto fireTime = weapon->animFirePoint / 100.f; - auto loopStart = weapon->animLoopStart / 100.f; - auto currentTime = animator->getAnimationTime(AnimIndexAction); - - if (currentTime >= fireTime && !fired) { - Weapon::meleeHit(weapon, character); - fired = true; - } - - if (animator->isCompleted(AnimIndexAction)) { - if (character->getCurrentState().primaryActive) { - animator->setAnimationTime(AnimIndexAction, loopStart); - fired = false; + else { + const auto onGround = Weapon::targetOnGround(weapon, character); + if (onGround) { + character->playCycle(throwcycle); } else { - return true; + character->playCycle(shootcycle); } } + } else { RW_ERROR("Unrecognized fireType: " << weapon->fireType); return true; diff --git a/rwengine/src/items/Weapon.cpp b/rwengine/src/items/Weapon.cpp index fd3464fd..be74ecfd 100644 --- a/rwengine/src/items/Weapon.cpp +++ b/rwengine/src/items/Weapon.cpp @@ -5,10 +5,12 @@ #include #include "data/WeaponData.hpp" +#include "dynamics/HitTest.hpp" #include "engine/GameWorld.hpp" #include "objects/CharacterObject.hpp" #include "objects/ProjectileObject.hpp" + bool WeaponScan::doesDamage(GameObject* target) const { return target != source; } @@ -60,3 +62,20 @@ void Weapon::meleeHit(WeaponData* weapon, CharacterObject* character) { character }); } + +bool Weapon::targetOnGround(WeaponData *weapon, CharacterObject *character) { + const auto center = character->getPosition() + character->getRotation() + * weapon->fireOffset; + HitTest test {*character->engine->dynamicsWorld}; + const auto result = test.sphereTest(center, weapon->meleeRadius); + bool ground = false; + for (const auto& r : result) { + if (r.object == character) { + continue; + } + if (r.object->type() == GameObject::Character) { + ground |= static_cast(r.object)->isKnockedDown(); + } + } + return ground; +} diff --git a/rwengine/src/items/Weapon.hpp b/rwengine/src/items/Weapon.hpp index dd601355..5720c0ee 100644 --- a/rwengine/src/items/Weapon.hpp +++ b/rwengine/src/items/Weapon.hpp @@ -58,6 +58,7 @@ namespace Weapon { void fireProjectile(WeaponData* weapon, CharacterObject* character, float force); void fireHitscan(WeaponData *weapon, CharacterObject* character); void meleeHit(WeaponData *weapon, CharacterObject* character); +bool targetOnGround(WeaponData *weapon, CharacterObject* character); } #endif diff --git a/rwengine/src/objects/CharacterObject.cpp b/rwengine/src/objects/CharacterObject.cpp index 62f60871..75fe21a4 100644 --- a/rwengine/src/objects/CharacterObject.cpp +++ b/rwengine/src/objects/CharacterObject.cpp @@ -26,6 +26,7 @@ #include "loaders/LoaderIFP.hpp" #include "objects/VehicleObject.hpp" + #ifndef BT_BULLET_VERSION #error Unable to find BT_BULLET_VERSION #endif @@ -468,6 +469,12 @@ void CharacterObject::SetDead() { currentState.isDead = true; } +bool CharacterObject::isKnockedDown() const { + /// @todo husho says: State in [knocked down, getting up, dying, dead] + auto a = animator->getAnimation(AnimIndexMovement); + return a == animations->animation(AnimCycle::KnockOutShotFront0); +} + bool CharacterObject::enterVehicle(VehicleObject* vehicle, size_t seat) { if (vehicle) { // Check that the seat is free diff --git a/rwengine/src/objects/CharacterObject.hpp b/rwengine/src/objects/CharacterObject.hpp index c76ef3eb..84cb99db 100644 --- a/rwengine/src/objects/CharacterObject.hpp +++ b/rwengine/src/objects/CharacterObject.hpp @@ -141,6 +141,8 @@ public: void Die(); void SetDead(); + bool isKnockedDown() const; + bool takeDamage(const DamageInfo& damage) override; bool enterVehicle(VehicleObject* vehicle, size_t seat); From bdacc1137a0e2587fc5d9a15f8505c9e5580520a Mon Sep 17 00:00:00 2001 From: Daniel Evans Date: Sat, 5 Jan 2019 02:02:28 +0000 Subject: [PATCH 5/6] Modernize ScanType enum --- rwengine/src/engine/GameWorld.cpp | 4 ++-- rwengine/src/items/Weapon.hpp | 24 ++++++++++++------------ tests/test_Weapon.cpp | 8 ++++++-- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/rwengine/src/engine/GameWorld.cpp b/rwengine/src/engine/GameWorld.cpp index 654ac79f..e512145a 100644 --- a/rwengine/src/engine/GameWorld.cpp +++ b/rwengine/src/engine/GameWorld.cpp @@ -568,7 +568,7 @@ void GameWorld::destroyEffect(VisualFX& effect) { } void GameWorld::doWeaponScan(const WeaponScan& scan) { - if (scan.type == WeaponScan::RADIUS) { + if (scan.type == ScanType::Radius) { HitTest test {*dynamicsWorld}; const auto result = test.sphereTest(scan.center, scan.radius); @@ -584,7 +584,7 @@ void GameWorld::doWeaponScan(const WeaponScan& scan) { target.object->takeDamage(di); } - } else if (scan.type == WeaponScan::HITSCAN) { + } else if (scan.type == ScanType::HitScan) { btVector3 from(scan.center.x, scan.center.y, scan.center.z), to(scan.end.x, scan.end.y, scan.end.z); glm::vec3 hitEnd = scan.end; diff --git a/rwengine/src/items/Weapon.hpp b/rwengine/src/items/Weapon.hpp index 5720c0ee..39a9fdc8 100644 --- a/rwengine/src/items/Weapon.hpp +++ b/rwengine/src/items/Weapon.hpp @@ -1,22 +1,22 @@ #ifndef _RWENGINE_WEAPON_HPP_ #define _RWENGINE_WEAPON_HPP_ -#include +#include class CharacterObject; class GameObject; struct WeaponData; +enum class ScanType { + /** Instant-hit ray weapons */ + HitScan, + /** Area of effect attack */ + Radius, +}; + /** * @brief simple object for performing weapon checks against the world */ struct WeaponScan { - enum ScanType { - /** Instant-hit ray weapons */ - HITSCAN, - /** Area of effect attack */ - RADIUS, - }; - const ScanType type; float damage; @@ -29,10 +29,10 @@ struct WeaponScan { WeaponData* weapon; GameObject* source; - // Constructor for a RADIUS hitscan + // Constructor for Radius WeaponScan(float damage, const glm::vec3& center, float radius, WeaponData* weapon = nullptr, GameObject* source = nullptr) - : type(RADIUS) + : type(ScanType::Radius) , damage(damage) , center(center) , radius(radius) @@ -40,10 +40,10 @@ struct WeaponScan { , source(source) { } - // Constructor for a ray hitscan + // Constructor for HitScan WeaponScan(float damage, const glm::vec3& start, const glm::vec3& end, WeaponData* weapon = nullptr, GameObject* source = nullptr) - : type(HITSCAN) + : type(ScanType::HitScan) , damage(damage) , center(start) , end(end) diff --git a/tests/test_Weapon.cpp b/tests/test_Weapon.cpp index 8edbeb5a..ff57ce27 100644 --- a/tests/test_Weapon.cpp +++ b/tests/test_Weapon.cpp @@ -5,16 +5,20 @@ #include #include "test_Globals.hpp" +auto& operator<<(std::ostream& s, const ScanType& type) { + return s << static_cast(type); +} + BOOST_AUTO_TEST_SUITE(WeaponTests) BOOST_AUTO_TEST_CASE(radius_ctor_creates_radius_scan) { WeaponScan scan{10.f, {1.f, 1.f, 1.f}, 5.f}; - BOOST_CHECK_EQUAL(scan.type, WeaponScan::RADIUS); + BOOST_CHECK_EQUAL(scan.type, ScanType::Radius); } BOOST_AUTO_TEST_CASE(hitscan_ctor_creates_radius_scan) { WeaponScan scan{10.f, {1.f, 1.f, 1.f}, {0.f, 0.f, 0.f}}; - BOOST_CHECK_EQUAL(scan.type, WeaponScan::HITSCAN); + BOOST_CHECK_EQUAL(scan.type, ScanType::HitScan); } struct WeaponScanFixture { From f59653eeea70ebe4fa4d30afd450bbffb9d3fb2b Mon Sep 17 00:00:00 2001 From: Daniel Evans Date: Sat, 5 Jan 2019 02:22:38 +0000 Subject: [PATCH 6/6] Make DamageInfo construction more explicit --- rwengine/src/engine/GameWorld.cpp | 72 ++++++++++++----------- rwengine/src/objects/GameObject.hpp | 9 ++- rwengine/src/objects/ProjectileObject.cpp | 5 +- rwgame/states/DebugState.cpp | 10 +++- tests/test_Character.cpp | 8 ++- 5 files changed, 61 insertions(+), 43 deletions(-) diff --git a/rwengine/src/engine/GameWorld.cpp b/rwengine/src/engine/GameWorld.cpp index e512145a..11d01968 100644 --- a/rwengine/src/engine/GameWorld.cpp +++ b/rwengine/src/engine/GameWorld.cpp @@ -570,41 +570,39 @@ void GameWorld::destroyEffect(VisualFX& effect) { void GameWorld::doWeaponScan(const WeaponScan& scan) { if (scan.type == ScanType::Radius) { HitTest test {*dynamicsWorld}; - const auto result = test.sphereTest(scan.center, scan.radius); + const auto& result = test.sphereTest(scan.center, scan.radius); for(const auto& target : result) { if (!scan.doesDamage(target.object)) { continue; } - GameObject::DamageInfo di; - di.damageSource = scan.center; - di.type = GameObject::DamageInfo::Melee; - di.hitpoints = scan.damage; - target.object->takeDamage(di); + target.object->takeDamage( + { + GameObject::DamageInfo::DamageType::Melee, + {}, scan.center, scan.damage + }); } } else if (scan.type == ScanType::HitScan) { btVector3 from(scan.center.x, scan.center.y, scan.center.z), to(scan.end.x, scan.end.y, scan.end.z); - glm::vec3 hitEnd = scan.end; btCollisionWorld::ClosestRayResultCallback cb(from, to); cb.m_collisionFilterGroup = btBroadphaseProxy::AllFilter; dynamicsWorld->rayTest(from, to, cb); - // TODO: did any weapons penetrate? - - if (cb.hasHit()) { - GameObject* go = static_cast( - cb.m_collisionObject->getUserPointer()); - GameObject::DamageInfo di; - hitEnd = di.damageLocation = - glm::vec3(cb.m_hitPointWorld.x(), cb.m_hitPointWorld.y(), - cb.m_hitPointWorld.z()); - di.damageSource = scan.center; - di.type = GameObject::DamageInfo::Bullet; - di.hitpoints = scan.damage; - go->takeDamage(di); + if (!cb.hasHit()) { + return; } + + auto go = static_cast( + cb.m_collisionObject->getUserPointer()); + go->takeDamage( + { + GameObject::DamageInfo::DamageType::Bullet, + {cb.m_hitPointWorld.x(), cb.m_hitPointWorld.y(), + cb.m_hitPointWorld.z()}, + scan.center, scan.damage + }); } } @@ -671,11 +669,13 @@ void handleVehicleResponse(GameObject* object, btManifoldPoint& mp, bool isA) { dmg = mp.getPositionWorldOnB(); } - object->takeDamage({{dmg.x(), dmg.y(), dmg.z()}, - {src.x(), src.y(), src.z()}, - 0.f, - GameObject::DamageInfo::Physics, - mp.getAppliedImpulse()}); + object->takeDamage({ + GameObject::DamageInfo::DamageType::Physics, + {dmg.x(), dmg.y(), dmg.z()}, + {src.x(), src.y(), src.z()}, + 0.f, + mp.getAppliedImpulse() + }); } void handleInstanceResponse(InstanceObject* instance, const btManifoldPoint& mp, @@ -687,16 +687,20 @@ void handleInstanceResponse(InstanceObject* instance, const btManifoldPoint& mp, auto dmg = isA ? mp.m_positionWorldOnA : mp.m_positionWorldOnB; auto impulse = mp.getAppliedImpulse(); - if (impulse > 0.0f) { - ///@ todo Correctness: object damage calculation - constexpr auto kMinimumDamageImpulse = 500.f; - const auto hp = std::max(0.f, impulse - kMinimumDamageImpulse); - instance->takeDamage({{dmg.x(), dmg.y(), dmg.z()}, - {dmg.x(), dmg.y(), dmg.z()}, - hp, - GameObject::DamageInfo::Physics, - impulse}); + if (impulse <= 0.0f) { + return; } + + ///@ todo Correctness: object damage calculation + constexpr auto kMinimumDamageImpulse = 500.f; + const auto hp = std::max(0.f, impulse - kMinimumDamageImpulse); + instance->takeDamage({ + GameObject::DamageInfo::DamageType::Physics, + {dmg.x(), dmg.y(), dmg.z()}, + {dmg.x(), dmg.y(), dmg.z()}, + hp, + impulse + }); } } // namespace diff --git a/rwengine/src/objects/GameObject.hpp b/rwengine/src/objects/GameObject.hpp index f9ada284..74910f46 100644 --- a/rwengine/src/objects/GameObject.hpp +++ b/rwengine/src/objects/GameObject.hpp @@ -164,7 +164,9 @@ public: } struct DamageInfo { - enum DamageType { Explosion, Burning, Bullet, Physics, Melee }; + enum class DamageType { + Explosion, Burning, Bullet, Physics, Melee + }; /** * World position of damage @@ -190,6 +192,11 @@ public: * Physics impulse. */ float impulse; + + DamageInfo(DamageType type, const glm::vec3 &location, + const glm::vec3 &source, float damage, float impulse = 0.f) + : damageLocation(location), damageSource(source), hitpoints(damage), + type(type), impulse(impulse) {} }; virtual bool takeDamage(const DamageInfo& damage) { diff --git a/rwengine/src/objects/ProjectileObject.cpp b/rwengine/src/objects/ProjectileObject.cpp index e14627e5..a26dc552 100644 --- a/rwengine/src/objects/ProjectileObject.cpp +++ b/rwengine/src/objects/ProjectileObject.cpp @@ -78,8 +78,9 @@ void ProjectileObject::explode() { float d = glm::distance(getPosition(), o->getPosition()); if (d > damageSize) continue; - o->takeDamage({getPosition(), getPosition(), - damage / glm::max(d, 1.f), DamageInfo::Explosion, + o->takeDamage({DamageInfo::DamageType::Explosion, + getPosition(), getPosition(), + damage / glm::max(d, 1.f), 0.f}); } diff --git a/rwgame/states/DebugState.cpp b/rwgame/states/DebugState.cpp index a9b29d16..1165a209 100644 --- a/rwgame/states/DebugState.cpp +++ b/rwgame/states/DebugState.cpp @@ -207,9 +207,13 @@ Menu DebugState::createAIMenu() { if (pedestrianPtr->getLifetime() == GameObject::PlayerLifetime) { continue; } - pedestrianPtr->takeDamage({pedestrianPtr->getPosition(), - pedestrianPtr->getPosition(), 100.f, - GameObject::DamageInfo::Explosion, 0.f}); + pedestrianPtr->takeDamage( + { + GameObject::DamageInfo::DamageType::Explosion, + pedestrianPtr->getPosition(), + pedestrianPtr->getPosition(), 100.f, + 0.f + }); } }); diff --git a/tests/test_Character.cpp b/tests/test_Character.cpp index d52eafb6..65726e59 100644 --- a/tests/test_Character.cpp +++ b/tests/test_Character.cpp @@ -129,9 +129,11 @@ BOOST_AUTO_TEST_CASE(test_death) { BOOST_CHECK_EQUAL(character->getCurrentState().health, 100.f); BOOST_CHECK(character->isAlive()); - GameObject::DamageInfo dmg; - dmg.type = GameObject::DamageInfo::Bullet; - dmg.hitpoints = character->getCurrentState().health + 1.f; + GameObject::DamageInfo dmg { + GameObject::DamageInfo::DamageType::Bullet, + {}, {}, + character->getCurrentState().health + 1.f + }; // Do some damage BOOST_CHECK(character->takeDamage(dmg));