mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-03-29 04:20:29 +00:00
Improve hit detection emulation (#7900)
This commit is contained in:
parent
6f8123998c
commit
86b6eee62b
@ -587,18 +587,20 @@ namespace MWMechanics
|
|||||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||||
const MWWorld::Store<ESM::GameSetting>& store = world->getStore().get<ESM::GameSetting>();
|
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 ESM::Position& posdata = actor.getRefData().getPosition();
|
||||||
const osg::Vec3f actorPos(posdata.asVec3());
|
const osg::Vec3f actorPos(posdata.asVec3());
|
||||||
|
const osg::Vec3f actorDirXY = osg::Quat(posdata.rot[2], osg::Vec3(0, 0, -1)) * osg::Vec3f(0, 1, 0);
|
||||||
// Morrowind uses body orientation or camera orientation if available
|
// Only the player can look up, apparently.
|
||||||
// The difference between that and this is subtle
|
const float actorVerticalAngle = actor == getPlayer() ? -std::sin(posdata.rot[0]) : 0.f;
|
||||||
osg::Quat actorRot
|
const float actorEyeLevel = world->getHalfExtents(actor, true).z() * 2.f * 0.85f;
|
||||||
= osg::Quat(posdata.rot[0], osg::Vec3f(-1, 0, 0)) * osg::Quat(posdata.rot[2], osg::Vec3f(0, 0, -1));
|
const osg::Vec3f actorEyePos{ actorPos.x(), actorPos.y(), actorPos.z() + actorEyeLevel };
|
||||||
|
const bool canMoveByZ = canActorMoveByZAxis(actor);
|
||||||
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));
|
|
||||||
|
|
||||||
// The player can target any active actor, non-playable actors only target their targets
|
// The player can target any active actor, non-playable actors only target their targets
|
||||||
std::vector<MWWorld::Ptr> targets;
|
std::vector<MWWorld::Ptr> targets;
|
||||||
@ -612,26 +614,40 @@ namespace MWMechanics
|
|||||||
{
|
{
|
||||||
if (actor == target || target.getClass().getCreatureStats(target).isDead())
|
if (actor == target || target.getClass().getCreatureStats(target).isDead())
|
||||||
continue;
|
continue;
|
||||||
float dist = getDistanceToBounds(actor, target);
|
const float dist = getDistanceToBounds(actor, target);
|
||||||
osg::Vec3f targetPos(target.getRefData().getPosition().asVec3());
|
const osg::Vec3f targetPos(target.getRefData().getPosition().asVec3());
|
||||||
osg::Vec3f dirToTarget = targetPos - actorPos;
|
if (dist >= reach || dist >= minDist || std::abs(targetPos.z() - actorPos.z()) >= reach)
|
||||||
if (dist >= reach || dist >= minDist || std::abs(dirToTarget.z()) >= reach)
|
|
||||||
continue;
|
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
|
// Use dot product to check if the target is behind first...
|
||||||
// in XY and YZ planes of the coordinate system where the actor's orientation
|
if (actorToTargetXY.x() * actorDirXY.x() + actorToTargetXY.y() * actorDirXY.y() <= 0.f)
|
||||||
// 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())
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Nice cliff racer hack Todd
|
// And then perp dot product to calculate the hit angle sine.
|
||||||
if (combatAngleZcos * std::abs(hitDir.z()) > hitDir.y() && !MWMechanics::canActorMoveByZAxis(target))
|
// 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;
|
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!
|
// Gotta use physics somehow!
|
||||||
if (!world->getLOS(actor, target))
|
if (!world->getLOS(actor, target))
|
||||||
continue;
|
continue;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user