mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-06 07:15:36 +00:00
Merge branch 'mattengarde' into 'master'
Improve hit detection emulation (#7900) Closes #7900 See merge request OpenMW/openmw!3984
This commit is contained in:
commit
0fa4b0137a
1 changed files with 39 additions and 23 deletions
|
@ -587,18 +587,20 @@ namespace MWMechanics
|
|||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||
const MWWorld::Store<ESM::GameSetting>& store = world->getStore().get<ESM::GameSetting>();
|
||||
|
||||
// These GMSTs are not in degrees. They're tolerance angle sines multiplied by 90.
|
||||
// With the default values of 60, the actual tolerance angles are roughly 41.8 degrees.
|
||||
// Don't think too hard about it. In this place, thinking can cause permanent damage to your mental health.
|
||||
const float fCombatAngleXY = store.find("fCombatAngleXY")->mValue.getFloat() / 90.f;
|
||||
const float fCombatAngleZ = store.find("fCombatAngleZ")->mValue.getFloat() / 90.f;
|
||||
|
||||
const ESM::Position& posdata = actor.getRefData().getPosition();
|
||||
const osg::Vec3f actorPos(posdata.asVec3());
|
||||
|
||||
// Morrowind uses body orientation or camera orientation if available
|
||||
// The difference between that and this is subtle
|
||||
osg::Quat actorRot
|
||||
= osg::Quat(posdata.rot[0], osg::Vec3f(-1, 0, 0)) * osg::Quat(posdata.rot[2], osg::Vec3f(0, 0, -1));
|
||||
|
||||
const float fCombatAngleXY = store.find("fCombatAngleXY")->mValue.getFloat();
|
||||
const float fCombatAngleZ = store.find("fCombatAngleZ")->mValue.getFloat();
|
||||
const float combatAngleXYcos = std::cos(osg::DegreesToRadians(fCombatAngleXY));
|
||||
const float combatAngleZcos = std::cos(osg::DegreesToRadians(fCombatAngleZ));
|
||||
const osg::Vec3f actorDirXY = osg::Quat(posdata.rot[2], osg::Vec3(0, 0, -1)) * osg::Vec3f(0, 1, 0);
|
||||
// Only the player can look up, apparently.
|
||||
const float actorVerticalAngle = actor == getPlayer() ? -std::sin(posdata.rot[0]) : 0.f;
|
||||
const float actorEyeLevel = world->getHalfExtents(actor, true).z() * 2.f * 0.85f;
|
||||
const osg::Vec3f actorEyePos{ actorPos.x(), actorPos.y(), actorPos.z() + actorEyeLevel };
|
||||
const bool canMoveByZ = canActorMoveByZAxis(actor);
|
||||
|
||||
// The player can target any active actor, non-playable actors only target their targets
|
||||
std::vector<MWWorld::Ptr> targets;
|
||||
|
@ -612,26 +614,40 @@ namespace MWMechanics
|
|||
{
|
||||
if (actor == target || target.getClass().getCreatureStats(target).isDead())
|
||||
continue;
|
||||
float dist = getDistanceToBounds(actor, target);
|
||||
osg::Vec3f targetPos(target.getRefData().getPosition().asVec3());
|
||||
osg::Vec3f dirToTarget = targetPos - actorPos;
|
||||
if (dist >= reach || dist >= minDist || std::abs(dirToTarget.z()) >= reach)
|
||||
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)
|
||||
continue;
|
||||
|
||||
dirToTarget.normalize();
|
||||
// Horizontal angle checks.
|
||||
osg::Vec2f actorToTargetXY{ targetPos.x() - actorPos.x(), targetPos.y() - actorPos.y() };
|
||||
actorToTargetXY.normalize();
|
||||
|
||||
// The idea is to use fCombatAngleXY and fCombatAngleZ as tolerance angles
|
||||
// in XY and YZ planes of the coordinate system where the actor's orientation
|
||||
// corresponds to (0, 1, 0) vector. This is not exactly what Morrowind does
|
||||
// but Morrowind does something (even more) stupid here
|
||||
osg::Vec3f hitDir = actorRot.inverse() * dirToTarget;
|
||||
if (combatAngleXYcos * std::abs(hitDir.x()) > hitDir.y())
|
||||
// Use dot product to check if the target is behind first...
|
||||
if (actorToTargetXY.x() * actorDirXY.x() + actorToTargetXY.y() * actorDirXY.y() <= 0.f)
|
||||
continue;
|
||||
|
||||
// Nice cliff racer hack Todd
|
||||
if (combatAngleZcos * std::abs(hitDir.z()) > hitDir.y() && !MWMechanics::canActorMoveByZAxis(target))
|
||||
// And then perp dot product to calculate the hit angle sine.
|
||||
// This gives us a horizontal hit range of [-asin(fCombatAngleXY / 90); asin(fCombatAngleXY / 90)]
|
||||
if (std::abs(actorToTargetXY.x() * actorDirXY.y() - actorToTargetXY.y() * actorDirXY.x()) > fCombatAngleXY)
|
||||
continue;
|
||||
|
||||
// Vertical angle checks. Nice cliff racer hack, Todd.
|
||||
if (!canMoveByZ)
|
||||
{
|
||||
// The idea is that the body should always be possible to hit.
|
||||
// fCombatAngleZ is the tolerance for hitting the target's feet or head.
|
||||
osg::Vec3f actorToTargetFeet = targetPos - actorEyePos;
|
||||
osg::Vec3f actorToTargetHead = actorToTargetFeet;
|
||||
actorToTargetFeet.normalize();
|
||||
actorToTargetHead.z() += world->getHalfExtents(target, true).z() * 2.f;
|
||||
actorToTargetHead.normalize();
|
||||
|
||||
if (actorVerticalAngle - actorToTargetHead.z() > fCombatAngleZ
|
||||
|| actorVerticalAngle - actorToTargetFeet.z() < -fCombatAngleZ)
|
||||
continue;
|
||||
}
|
||||
|
||||
// Gotta use physics somehow!
|
||||
if (!world->getLOS(actor, target))
|
||||
continue;
|
||||
|
|
Loading…
Reference in a new issue