Skip to content

Commit

Permalink
Merge branch 'nooneactuallyknowshowmorrowindcombatworks' into 'master'
Browse files Browse the repository at this point in the history
Check if the victim is within weapon reach upon hit (#8280)

See merge request OpenMW/openmw!4518
  • Loading branch information
psi29a committed Jan 18, 2025
2 parents e515e99 + 2a62dd7 commit c16064e
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 11 deletions.
8 changes: 4 additions & 4 deletions apps/openmw/mwclass/creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,11 +236,8 @@ namespace MWClass
}

MWBase::World* world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting>& store = world->getStore().get<ESM::GameSetting>();
float dist = store.find("fCombatDistance")->mValue.getFloat();
if (!weapon.isEmpty())
dist *= weapon.get<ESM::Weapon>()->mBase->mData.mReach;

const float dist = MWMechanics::getMeleeWeaponReach(ptr, weapon);
const std::pair<MWWorld::Ptr, osg::Vec3f> result = MWMechanics::getHitContact(ptr, dist);
if (result.first.isEmpty()) // Didn't hit anything
return true;
Expand Down Expand Up @@ -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(
Expand Down
9 changes: 4 additions & 5 deletions apps/openmw/mwclass/npc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -572,12 +572,8 @@ namespace MWClass
weapon = *weaponslot;

MWBase::World* world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting>& store = world->getStore().get<ESM::GameSetting>();
const float fCombatDistance = store.find("fCombatDistance")->mValue.getFloat();
float dist = fCombatDistance
* (!weapon.isEmpty() ? weapon.get<ESM::Weapon>()->mBase->mData.mReach
: store.find("fHandToHandReach")->mValue.getFloat());

const float dist = MWMechanics::getMeleeWeaponReach(ptr, weapon);
const std::pair<MWWorld::Ptr, osg::Vec3f> result = MWMechanics::getHitContact(ptr, dist);
if (result.first.isEmpty()) // Didn't hit anything
return true;
Expand Down Expand Up @@ -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);

Expand Down
24 changes: 22 additions & 2 deletions apps/openmw/mwmechanics/combat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ESM::GameSetting>& store = world->getStore().get<ESM::GameSetting>();
const float fCombatDistance = store.find("fCombatDistance")->mValue.getFloat();
if (!weapon.isEmpty())
return fCombatDistance * weapon.get<ESM::Weapon>()->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<MWWorld::Ptr, osg::Vec3f> getHitContact(const MWWorld::Ptr& actor, float reach)
{
// Lasciate ogne speranza, voi ch'entrate
Expand Down Expand Up @@ -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();
Expand Down
4 changes: 4 additions & 0 deletions apps/openmw/mwmechanics/combat.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<MWWorld::Ptr, osg::Vec3f> getHitContact(const MWWorld::Ptr& actor, float reach);

Expand Down

0 comments on commit c16064e

Please sign in to comment.