diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 007338095ff..9224f6f0d87 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -236,11 +236,8 @@ namespace MWClass } MWBase::World* world = MWBase::Environment::get().getWorld(); - const MWWorld::Store& store = world->getStore().get(); - float dist = store.find("fCombatDistance")->mValue.getFloat(); - if (!weapon.isEmpty()) - dist *= weapon.get()->mBase->mData.mReach; + const float dist = MWMechanics::getMeleeWeaponReach(ptr, weapon); const std::pair result = MWMechanics::getHitContact(ptr, dist); if (result.first.isEmpty()) // Didn't hit anything return true; @@ -281,6 +278,9 @@ namespace MWClass if (otherstats.isDead()) // Can't hit dead actors return; + if (!MWMechanics::isInMeleeReach(ptr, victim, MWMechanics::getMeleeWeaponReach(ptr, weapon))) + return; + if (!success) { victim.getClass().onHit( diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 22c953d27dc..0b61436d113 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -572,12 +572,8 @@ namespace MWClass weapon = *weaponslot; MWBase::World* world = MWBase::Environment::get().getWorld(); - const MWWorld::Store& store = world->getStore().get(); - const float fCombatDistance = store.find("fCombatDistance")->mValue.getFloat(); - float dist = fCombatDistance - * (!weapon.isEmpty() ? weapon.get()->mBase->mData.mReach - : store.find("fHandToHandReach")->mValue.getFloat()); + const float dist = MWMechanics::getMeleeWeaponReach(ptr, weapon); const std::pair result = MWMechanics::getHitContact(ptr, dist); if (result.first.isEmpty()) // Didn't hit anything return true; @@ -615,6 +611,9 @@ namespace MWClass if (otherstats.isDead()) // Can't hit dead actors return; + if (!MWMechanics::isInMeleeReach(ptr, victim, MWMechanics::getMeleeWeaponReach(ptr, weapon))) + return; + if (ptr == MWMechanics::getPlayer()) MWBase::Environment::get().getWindowManager()->setEnemy(victim); diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 5d283214a3a..e7c7342284e 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -578,6 +578,24 @@ namespace MWMechanics return dist; } + float getMeleeWeaponReach(const MWWorld::Ptr& actor, const MWWorld::Ptr& weapon) + { + MWBase::World* world = MWBase::Environment::get().getWorld(); + const MWWorld::Store& store = world->getStore().get(); + const float fCombatDistance = store.find("fCombatDistance")->mValue.getFloat(); + if (!weapon.isEmpty()) + return fCombatDistance * weapon.get()->mBase->mData.mReach; + if (actor.getClass().isNpc()) + return fCombatDistance * store.find("fHandToHandReach")->mValue.getFloat(); + return fCombatDistance; + } + + bool isInMeleeReach(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, const float reach) + { + const float heightDiff = actor.getRefData().getPosition().pos[2] - target.getRefData().getPosition().pos[2]; + return std::abs(heightDiff) < reach && getDistanceToBounds(actor, target) < reach; + } + std::pair getHitContact(const MWWorld::Ptr& actor, float reach) { // Lasciate ogne speranza, voi ch'entrate @@ -614,11 +632,13 @@ namespace MWMechanics { if (actor == target || target.getClass().getCreatureStats(target).isDead()) continue; + const float dist = getDistanceToBounds(actor, target); - const osg::Vec3f targetPos(target.getRefData().getPosition().asVec3()); - if (dist >= reach || dist >= minDist || std::abs(targetPos.z() - actorPos.z()) >= reach) + if (dist >= minDist || !isInMeleeReach(actor, target, reach)) continue; + const osg::Vec3f targetPos(target.getRefData().getPosition().asVec3()); + // Horizontal angle checks. osg::Vec2f actorToTargetXY{ targetPos.x() - actorPos.x(), targetPos.y() - actorPos.y() }; actorToTargetXY.normalize(); diff --git a/apps/openmw/mwmechanics/combat.hpp b/apps/openmw/mwmechanics/combat.hpp index 92033c7e776..fa3660a0165 100644 --- a/apps/openmw/mwmechanics/combat.hpp +++ b/apps/openmw/mwmechanics/combat.hpp @@ -64,6 +64,10 @@ namespace MWMechanics // Cursed distance calculation used for combat proximity and hit checks in Morrowind float getDistanceToBounds(const MWWorld::Ptr& actor, const MWWorld::Ptr& target); + float getMeleeWeaponReach(const MWWorld::Ptr& actor, const MWWorld::Ptr& weapon); + + bool isInMeleeReach(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, const float reach); + // Similarly cursed hit target selection std::pair getHitContact(const MWWorld::Ptr& actor, float reach);