|
|
@ -2761,64 +2761,66 @@ namespace MWWorld
|
|
|
|
{
|
|
|
|
{
|
|
|
|
MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
|
|
|
MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
|
|
|
|
|
|
|
|
|
|
|
// Get the target to use for "on touch" effects, using the facing direction from Head node
|
|
|
|
|
|
|
|
MWWorld::Ptr target;
|
|
|
|
|
|
|
|
float distance = getActivationDistancePlusTelekinesis();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
osg::Vec3f hitPosition = actor.getRefData().getPosition().asVec3();
|
|
|
|
|
|
|
|
osg::Vec3f origin = getActorHeadTransform(actor).getTrans();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
osg::Quat orient = osg::Quat(actor.getRefData().getPosition().rot[0], osg::Vec3f(-1,0,0))
|
|
|
|
|
|
|
|
* osg::Quat(actor.getRefData().getPosition().rot[2], osg::Vec3f(0,0,-1));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
osg::Vec3f direction = orient * osg::Vec3f(0,1,0);
|
|
|
|
|
|
|
|
osg::Vec3f dest = origin + direction * distance;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result.
|
|
|
|
// For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result.
|
|
|
|
std::vector<MWWorld::Ptr> targetActors;
|
|
|
|
std::vector<MWWorld::Ptr> targetActors;
|
|
|
|
if (!actor.isEmpty() && actor != MWMechanics::getPlayer())
|
|
|
|
if (!actor.isEmpty() && actor != MWMechanics::getPlayer())
|
|
|
|
actor.getClass().getCreatureStats(actor).getAiSequence().getCombatTargets(targetActors);
|
|
|
|
actor.getClass().getCreatureStats(actor).getAiSequence().getCombatTargets(targetActors);
|
|
|
|
|
|
|
|
|
|
|
|
// For actor targets, we want to use bounding boxes (physics raycast).
|
|
|
|
const float fCombatDistance = getStore().get<ESM::GameSetting>().find("fCombatDistance")->getFloat();
|
|
|
|
// This is to give a slight tolerance for errors, especially with creatures like the Skeleton that would be very hard to aim at otherwise.
|
|
|
|
|
|
|
|
// For object targets, we want the detailed shapes (rendering raycast).
|
|
|
|
|
|
|
|
// If we used the bounding boxes for static objects, then we would not be able to target e.g. objects lying on a shelf.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MWPhysics::PhysicsSystem::RayResult result1 = mPhysics->castRay(origin, dest, actor, targetActors, MWPhysics::CollisionType_Actor);
|
|
|
|
osg::Vec3f hitPosition = actor.getRefData().getPosition().asVec3();
|
|
|
|
|
|
|
|
|
|
|
|
MWRender::RenderingManager::RayResult result2 = mRendering->castRay(origin, dest, true, true);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
float dist1 = std::numeric_limits<float>::max();
|
|
|
|
// for player we can take faced object first
|
|
|
|
float dist2 = std::numeric_limits<float>::max();
|
|
|
|
MWWorld::Ptr target;
|
|
|
|
|
|
|
|
if (actor == MWMechanics::getPlayer())
|
|
|
|
|
|
|
|
target = getFacedObject();
|
|
|
|
|
|
|
|
|
|
|
|
if (result1.mHit)
|
|
|
|
// if the faced object can not be activated, do not use it
|
|
|
|
dist1 = (origin - result1.mHitPos).length();
|
|
|
|
if (!target.isEmpty() && !target.getClass().canBeActivated(target))
|
|
|
|
if (result2.mHit)
|
|
|
|
target = NULL;
|
|
|
|
dist2 = (origin - result2.mHitPointWorld).length();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (result1.mHit)
|
|
|
|
if (target.isEmpty())
|
|
|
|
{
|
|
|
|
|
|
|
|
target = result1.mHitObject;
|
|
|
|
|
|
|
|
hitPosition = result1.mHitPos;
|
|
|
|
|
|
|
|
if (dist1 > getMaxActivationDistance() && !target.isEmpty() && (target.getClass().isActor() || !target.getClass().canBeActivated(target)))
|
|
|
|
|
|
|
|
target = NULL;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (result2.mHit)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
target = result2.mHitObject;
|
|
|
|
// For actor targets, we want to use hit contact with bounding boxes.
|
|
|
|
hitPosition = result2.mHitPointWorld;
|
|
|
|
// This is to give a slight tolerance for errors, especially with creatures like the Skeleton that would be very hard to aim at otherwise.
|
|
|
|
if (dist2 > getMaxActivationDistance() && !target.isEmpty() && !target.getClass().canBeActivated(target))
|
|
|
|
// For object targets, we want the detailed shapes (rendering raycast).
|
|
|
|
target = NULL;
|
|
|
|
// If we used the bounding boxes for static objects, then we would not be able to target e.g. objects lying on a shelf.
|
|
|
|
}
|
|
|
|
std::pair<MWWorld::Ptr,osg::Vec3f> result1 = getHitContact(actor, fCombatDistance, targetActors);
|
|
|
|
|
|
|
|
|
|
|
|
// When targeting an actor that is in combat with an "on touch" spell,
|
|
|
|
// Get the target to use for "on touch" effects, using the facing direction from Head node
|
|
|
|
// compare against the minimum of activation distance and combat distance.
|
|
|
|
osg::Vec3f origin = getActorHeadTransform(actor).getTrans();
|
|
|
|
|
|
|
|
|
|
|
|
if (!target.isEmpty() && target.getClass().isActor() && target.getClass().getCreatureStats (target).getAiSequence().isInCombat())
|
|
|
|
osg::Quat orient = osg::Quat(actor.getRefData().getPosition().rot[0], osg::Vec3f(-1,0,0))
|
|
|
|
{
|
|
|
|
* osg::Quat(actor.getRefData().getPosition().rot[2], osg::Vec3f(0,0,-1));
|
|
|
|
distance = std::min (distance, getStore().get<ESM::GameSetting>().find("fCombatDistance")->getFloat());
|
|
|
|
|
|
|
|
if (distance < dist1)
|
|
|
|
osg::Vec3f direction = orient * osg::Vec3f(0,1,0);
|
|
|
|
target = NULL;
|
|
|
|
float distance = getMaxActivationDistance();
|
|
|
|
|
|
|
|
osg::Vec3f dest = origin + direction * distance;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MWRender::RenderingManager::RayResult result2 = mRendering->castRay(origin, dest, true, true);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
float dist1 = std::numeric_limits<float>::max();
|
|
|
|
|
|
|
|
float dist2 = std::numeric_limits<float>::max();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!result1.first.isEmpty() && result1.first.getClass().isActor())
|
|
|
|
|
|
|
|
dist1 = (origin - result1.second).length();
|
|
|
|
|
|
|
|
if (result2.mHit)
|
|
|
|
|
|
|
|
dist2 = (origin - result2.mHitPointWorld).length();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!result1.first.isEmpty() && result1.first.getClass().isActor())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
target = result1.first;
|
|
|
|
|
|
|
|
hitPosition = result1.second;
|
|
|
|
|
|
|
|
if (dist1 > getMaxActivationDistance())
|
|
|
|
|
|
|
|
target = NULL;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (result2.mHit)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
target = result2.mHitObject;
|
|
|
|
|
|
|
|
hitPosition = result2.mHitPointWorld;
|
|
|
|
|
|
|
|
if (dist2 > getMaxActivationDistance() && !target.isEmpty() && !target.getClass().canBeActivated(target))
|
|
|
|
|
|
|
|
target = NULL;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::string selectedSpell = stats.getSpells().getSelectedSpell();
|
|
|
|
std::string selectedSpell = stats.getSpells().getSelectedSpell();
|
|
|
|