mirror of
https://github.com/rwengine/openrw.git
synced 2024-11-25 11:52:40 +01:00
Merge pull request #683 from danhedron/feat/melee
Melee Weapon implementation
This commit is contained in:
commit
08c90fdfb8
@ -688,9 +688,37 @@ bool Activities::UseItem::update(CharacterObject *character,
|
|||||||
character->playCycle(shootcycle);
|
character->playCycle(shootcycle);
|
||||||
}
|
}
|
||||||
} else if (weapon->fireType == WeaponData::MELEE) {
|
} else if (weapon->fireType == WeaponData::MELEE) {
|
||||||
RW_CHECK(weapon->fireType != WeaponData::MELEE,
|
auto currentAnim = character->getCurrentCycle();
|
||||||
"Melee attacks not implemented");
|
if (currentAnim == shootcycle || currentAnim == throwcycle) {
|
||||||
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 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const auto onGround = Weapon::targetOnGround(weapon, character);
|
||||||
|
if (onGround) {
|
||||||
|
character->playCycle(throwcycle);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
character->playCycle(shootcycle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
RW_ERROR("Unrecognized fireType: " << weapon->fireType);
|
RW_ERROR("Unrecognized fireType: " << weapon->fireType);
|
||||||
return true;
|
return true;
|
||||||
|
@ -34,49 +34,4 @@ struct WeaponData {
|
|||||||
std::uint32_t inventorySlot;
|
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
|
#endif
|
||||||
|
@ -23,9 +23,12 @@
|
|||||||
#include "ai/PlayerController.hpp"
|
#include "ai/PlayerController.hpp"
|
||||||
#include "ai/TrafficDirector.hpp"
|
#include "ai/TrafficDirector.hpp"
|
||||||
|
|
||||||
|
#include "dynamics/HitTest.hpp"
|
||||||
|
|
||||||
#include "data/CutsceneData.hpp"
|
#include "data/CutsceneData.hpp"
|
||||||
#include "data/InstanceData.hpp"
|
#include "data/InstanceData.hpp"
|
||||||
#include "data/WeaponData.hpp"
|
|
||||||
|
#include "items/Weapon.hpp"
|
||||||
|
|
||||||
#include "loaders/LoaderCutsceneDAT.hpp"
|
#include "loaders/LoaderCutsceneDAT.hpp"
|
||||||
#include "loaders/LoaderIFP.hpp"
|
#include "loaders/LoaderIFP.hpp"
|
||||||
@ -565,33 +568,41 @@ void GameWorld::destroyEffect(VisualFX& effect) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GameWorld::doWeaponScan(const WeaponScan& scan) {
|
void GameWorld::doWeaponScan(const WeaponScan& scan) {
|
||||||
RW_CHECK(scan.type != WeaponScan::RADIUS,
|
if (scan.type == ScanType::Radius) {
|
||||||
"Radius scans not implemented yet");
|
HitTest test {*dynamicsWorld};
|
||||||
|
const auto& result = test.sphereTest(scan.center, scan.radius);
|
||||||
|
|
||||||
if (scan.type == WeaponScan::RADIUS) {
|
for(const auto& target : result) {
|
||||||
// TODO
|
if (!scan.doesDamage(target.object)) {
|
||||||
// Requires custom ConvexResultCallback
|
continue;
|
||||||
} else if (scan.type == WeaponScan::HITSCAN) {
|
}
|
||||||
|
|
||||||
|
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),
|
btVector3 from(scan.center.x, scan.center.y, scan.center.z),
|
||||||
to(scan.end.x, scan.end.y, scan.end.z);
|
to(scan.end.x, scan.end.y, scan.end.z);
|
||||||
glm::vec3 hitEnd = scan.end;
|
|
||||||
btCollisionWorld::ClosestRayResultCallback cb(from, to);
|
btCollisionWorld::ClosestRayResultCallback cb(from, to);
|
||||||
cb.m_collisionFilterGroup = btBroadphaseProxy::AllFilter;
|
cb.m_collisionFilterGroup = btBroadphaseProxy::AllFilter;
|
||||||
dynamicsWorld->rayTest(from, to, cb);
|
dynamicsWorld->rayTest(from, to, cb);
|
||||||
// TODO: did any weapons penetrate?
|
if (!cb.hasHit()) {
|
||||||
|
return;
|
||||||
if (cb.hasHit()) {
|
|
||||||
GameObject* go = static_cast<GameObject*>(
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto go = static_cast<GameObject *>(
|
||||||
|
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
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -658,11 +669,13 @@ void handleVehicleResponse(GameObject* object, btManifoldPoint& mp, bool isA) {
|
|||||||
dmg = mp.getPositionWorldOnB();
|
dmg = mp.getPositionWorldOnB();
|
||||||
}
|
}
|
||||||
|
|
||||||
object->takeDamage({{dmg.x(), dmg.y(), dmg.z()},
|
object->takeDamage({
|
||||||
{src.x(), src.y(), src.z()},
|
GameObject::DamageInfo::DamageType::Physics,
|
||||||
0.f,
|
{dmg.x(), dmg.y(), dmg.z()},
|
||||||
GameObject::DamageInfo::Physics,
|
{src.x(), src.y(), src.z()},
|
||||||
mp.getAppliedImpulse()});
|
0.f,
|
||||||
|
mp.getAppliedImpulse()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleInstanceResponse(InstanceObject* instance, const btManifoldPoint& mp,
|
void handleInstanceResponse(InstanceObject* instance, const btManifoldPoint& mp,
|
||||||
@ -674,16 +687,20 @@ void handleInstanceResponse(InstanceObject* instance, const btManifoldPoint& mp,
|
|||||||
auto dmg = isA ? mp.m_positionWorldOnA : mp.m_positionWorldOnB;
|
auto dmg = isA ? mp.m_positionWorldOnA : mp.m_positionWorldOnB;
|
||||||
auto impulse = mp.getAppliedImpulse();
|
auto impulse = mp.getAppliedImpulse();
|
||||||
|
|
||||||
if (impulse > 0.0f) {
|
if (impulse <= 0.0f) {
|
||||||
///@ todo Correctness: object damage calculation
|
return;
|
||||||
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});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///@ 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
|
} // namespace
|
||||||
|
|
||||||
|
@ -5,10 +5,16 @@
|
|||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
#include "data/WeaponData.hpp"
|
#include "data/WeaponData.hpp"
|
||||||
|
#include "dynamics/HitTest.hpp"
|
||||||
#include "engine/GameWorld.hpp"
|
#include "engine/GameWorld.hpp"
|
||||||
#include "objects/CharacterObject.hpp"
|
#include "objects/CharacterObject.hpp"
|
||||||
#include "objects/ProjectileObject.hpp"
|
#include "objects/ProjectileObject.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
bool WeaponScan::doesDamage(GameObject* target) const {
|
||||||
|
return target != source;
|
||||||
|
}
|
||||||
|
|
||||||
void Weapon::fireHitscan(WeaponData* weapon, CharacterObject* owner) {
|
void Weapon::fireHitscan(WeaponData* weapon, CharacterObject* owner) {
|
||||||
auto handFrame = owner->getClump()->findFrame("srhand");
|
auto handFrame = owner->getClump()->findFrame("srhand");
|
||||||
glm::mat4 handMatrix = handFrame->getWorldTransform();
|
glm::mat4 handMatrix = handFrame->getWorldTransform();
|
||||||
@ -18,7 +24,7 @@ void Weapon::fireHitscan(WeaponData* weapon, CharacterObject* owner) {
|
|||||||
auto fireOrigin = glm::vec3(handMatrix[3]);
|
auto fireOrigin = glm::vec3(handMatrix[3]);
|
||||||
float dmg = static_cast<float>(weapon->damage);
|
float dmg = static_cast<float>(weapon->damage);
|
||||||
|
|
||||||
owner->engine->doWeaponScan({dmg, fireOrigin, rayend, weapon});
|
owner->engine->doWeaponScan({dmg, fireOrigin, rayend, weapon, owner});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Weapon::fireProjectile(WeaponData* weapon, CharacterObject* owner,
|
void Weapon::fireProjectile(WeaponData* weapon, CharacterObject* owner,
|
||||||
@ -45,3 +51,31 @@ void Weapon::fireProjectile(WeaponData* weapon, CharacterObject* owner,
|
|||||||
pool.insert(std::move(projectile));
|
pool.insert(std::move(projectile));
|
||||||
owner->engine->allObjects.push_back(ptr);
|
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<float>(weapon->damage),
|
||||||
|
center, weapon->meleeRadius, weapon,
|
||||||
|
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<CharacterObject *>(r.object)->isKnockedDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ground;
|
||||||
|
}
|
||||||
|
@ -1,12 +1,64 @@
|
|||||||
#ifndef _RWENGINE_WEAPON_HPP_
|
#ifndef _RWENGINE_WEAPON_HPP_
|
||||||
#define _RWENGINE_WEAPON_HPP_
|
#define _RWENGINE_WEAPON_HPP_
|
||||||
|
#include <glm/vec3.hpp>
|
||||||
|
|
||||||
class CharacterObject;
|
class CharacterObject;
|
||||||
|
class GameObject;
|
||||||
struct WeaponData;
|
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 {
|
||||||
|
const ScanType type;
|
||||||
|
|
||||||
|
float damage;
|
||||||
|
|
||||||
|
glm::vec3 center{};
|
||||||
|
float radius;
|
||||||
|
|
||||||
|
glm::vec3 end{};
|
||||||
|
|
||||||
|
WeaponData* weapon;
|
||||||
|
GameObject* source;
|
||||||
|
|
||||||
|
// Constructor for Radius
|
||||||
|
WeaponScan(float damage, const glm::vec3& center, float radius,
|
||||||
|
WeaponData* weapon = nullptr, GameObject* source = nullptr)
|
||||||
|
: type(ScanType::Radius)
|
||||||
|
, damage(damage)
|
||||||
|
, center(center)
|
||||||
|
, radius(radius)
|
||||||
|
, weapon(weapon)
|
||||||
|
, source(source) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructor for HitScan
|
||||||
|
WeaponScan(float damage, const glm::vec3& start, const glm::vec3& end,
|
||||||
|
WeaponData* weapon = nullptr, GameObject* source = nullptr)
|
||||||
|
: type(ScanType::HitScan)
|
||||||
|
, damage(damage)
|
||||||
|
, center(start)
|
||||||
|
, end(end)
|
||||||
|
, weapon(weapon)
|
||||||
|
, source(source) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool doesDamage(GameObject* target) const;
|
||||||
|
};
|
||||||
|
|
||||||
namespace Weapon {
|
namespace Weapon {
|
||||||
void fireProjectile(WeaponData* weapon, CharacterObject* character, float force);
|
void fireProjectile(WeaponData* weapon, CharacterObject* character, float force);
|
||||||
void fireHitscan(WeaponData *weapon, CharacterObject* character);
|
void fireHitscan(WeaponData *weapon, CharacterObject* character);
|
||||||
|
void meleeHit(WeaponData *weapon, CharacterObject* character);
|
||||||
|
bool targetOnGround(WeaponData *weapon, CharacterObject* character);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "loaders/LoaderIFP.hpp"
|
#include "loaders/LoaderIFP.hpp"
|
||||||
#include "objects/VehicleObject.hpp"
|
#include "objects/VehicleObject.hpp"
|
||||||
|
|
||||||
|
|
||||||
#ifndef BT_BULLET_VERSION
|
#ifndef BT_BULLET_VERSION
|
||||||
#error Unable to find BT_BULLET_VERSION
|
#error Unable to find BT_BULLET_VERSION
|
||||||
#endif
|
#endif
|
||||||
@ -468,6 +469,12 @@ void CharacterObject::SetDead() {
|
|||||||
currentState.isDead = true;
|
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) {
|
bool CharacterObject::enterVehicle(VehicleObject* vehicle, size_t seat) {
|
||||||
if (vehicle) {
|
if (vehicle) {
|
||||||
// Check that the seat is free
|
// Check that the seat is free
|
||||||
|
@ -141,6 +141,8 @@ public:
|
|||||||
void Die();
|
void Die();
|
||||||
void SetDead();
|
void SetDead();
|
||||||
|
|
||||||
|
bool isKnockedDown() const;
|
||||||
|
|
||||||
bool takeDamage(const DamageInfo& damage) override;
|
bool takeDamage(const DamageInfo& damage) override;
|
||||||
|
|
||||||
bool enterVehicle(VehicleObject* vehicle, size_t seat);
|
bool enterVehicle(VehicleObject* vehicle, size_t seat);
|
||||||
|
@ -164,7 +164,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct DamageInfo {
|
struct DamageInfo {
|
||||||
enum DamageType { Explosion, Burning, Bullet, Physics };
|
enum class DamageType {
|
||||||
|
Explosion, Burning, Bullet, Physics, Melee
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* World position of damage
|
* World position of damage
|
||||||
@ -190,6 +192,11 @@ public:
|
|||||||
* Physics impulse.
|
* Physics impulse.
|
||||||
*/
|
*/
|
||||||
float 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) {
|
virtual bool takeDamage(const DamageInfo& damage) {
|
||||||
|
@ -78,8 +78,9 @@ void ProjectileObject::explode() {
|
|||||||
float d = glm::distance(getPosition(), o->getPosition());
|
float d = glm::distance(getPosition(), o->getPosition());
|
||||||
if (d > damageSize) continue;
|
if (d > damageSize) continue;
|
||||||
|
|
||||||
o->takeDamage({getPosition(), getPosition(),
|
o->takeDamage({DamageInfo::DamageType::Explosion,
|
||||||
damage / glm::max(d, 1.f), DamageInfo::Explosion,
|
getPosition(), getPosition(),
|
||||||
|
damage / glm::max(d, 1.f),
|
||||||
0.f});
|
0.f});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,9 +207,13 @@ Menu DebugState::createAIMenu() {
|
|||||||
if (pedestrianPtr->getLifetime() == GameObject::PlayerLifetime) {
|
if (pedestrianPtr->getLifetime() == GameObject::PlayerLifetime) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
pedestrianPtr->takeDamage({pedestrianPtr->getPosition(),
|
pedestrianPtr->takeDamage(
|
||||||
pedestrianPtr->getPosition(), 100.f,
|
{
|
||||||
GameObject::DamageInfo::Explosion, 0.f});
|
GameObject::DamageInfo::DamageType::Explosion,
|
||||||
|
pedestrianPtr->getPosition(),
|
||||||
|
pedestrianPtr->getPosition(), 100.f,
|
||||||
|
0.f
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -129,9 +129,11 @@ BOOST_AUTO_TEST_CASE(test_death) {
|
|||||||
BOOST_CHECK_EQUAL(character->getCurrentState().health, 100.f);
|
BOOST_CHECK_EQUAL(character->getCurrentState().health, 100.f);
|
||||||
BOOST_CHECK(character->isAlive());
|
BOOST_CHECK(character->isAlive());
|
||||||
|
|
||||||
GameObject::DamageInfo dmg;
|
GameObject::DamageInfo dmg {
|
||||||
dmg.type = GameObject::DamageInfo::Bullet;
|
GameObject::DamageInfo::DamageType::Bullet,
|
||||||
dmg.hitpoints = character->getCurrentState().health + 1.f;
|
{}, {},
|
||||||
|
character->getCurrentState().health + 1.f
|
||||||
|
};
|
||||||
|
|
||||||
// Do some damage
|
// Do some damage
|
||||||
BOOST_CHECK(character->takeDamage(dmg));
|
BOOST_CHECK(character->takeDamage(dmg));
|
||||||
|
@ -1,13 +1,41 @@
|
|||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
#include <data/WeaponData.hpp>
|
#include <data/WeaponData.hpp>
|
||||||
|
#include <items/Weapon.hpp>
|
||||||
#include <objects/CharacterObject.hpp>
|
#include <objects/CharacterObject.hpp>
|
||||||
#include <objects/ProjectileObject.hpp>
|
#include <objects/ProjectileObject.hpp>
|
||||||
#include "test_Globals.hpp"
|
#include "test_Globals.hpp"
|
||||||
|
|
||||||
|
auto& operator<<(std::ostream& s, const ScanType& type) {
|
||||||
|
return s << static_cast<int>(type);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE(WeaponTests)
|
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, 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, ScanType::HitScan);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WeaponScanFixture {
|
||||||
|
GameObject* source = reinterpret_cast<GameObject*>(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<GameObject*>(0x0000BEEF)));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(weapon_scan_does_injur_others, WeaponScanFixture) {
|
||||||
|
BOOST_CHECK(scan.doesDamage(reinterpret_cast<GameObject*>(0xDEADBEEF)));
|
||||||
|
}
|
||||||
|
|
||||||
#if RW_TEST_WITH_DATA
|
#if RW_TEST_WITH_DATA
|
||||||
BOOST_AUTO_TEST_CASE(TestWeaponScan) {
|
BOOST_AUTO_TEST_CASE(TestDoWeaponScan) {
|
||||||
{
|
{
|
||||||
// Test RADIUS scan
|
// Test RADIUS scan
|
||||||
auto character = Global::get().e->createPedestrian(1, {0.f, 0.f, 0.f});
|
auto character = Global::get().e->createPedestrian(1, {0.f, 0.f, 0.f});
|
||||||
|
Loading…
Reference in New Issue
Block a user