From f89471818356539e77342903dd2a8acdce9b8a07 Mon Sep 17 00:00:00 2001 From: Daniel Evans Date: Wed, 2 Jan 2019 21:02:51 +0000 Subject: [PATCH] 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);