1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-06-27 12:41:34 +00:00

Merged pull request #1709

This commit is contained in:
Marc Zinnschlag 2018-06-09 10:46:02 +02:00
commit 76972bb2f6
3 changed files with 82 additions and 59 deletions

View file

@ -1,5 +1,7 @@
0.45.0 0.45.0
------ ------
Bug #3374: Touch spells not hitting kwama foragers
Bug #3591: Angled hit distance too low
Bug #2835: Player able to slowly move when overencumbered Bug #2835: Player able to slowly move when overencumbered
Bug #3897: Have Goodbye give all choices the effects of Goodbye Bug #3897: Have Goodbye give all choices the effects of Goodbye
Bug #4221: Characters get stuck in V-shaped terrain Bug #4221: Characters get stuck in V-shaped terrain

View file

@ -848,6 +848,16 @@ namespace MWPhysics
const osg::Quat &orient, const osg::Quat &orient,
float queryDistance, std::vector<MWWorld::Ptr> targets) float queryDistance, std::vector<MWWorld::Ptr> targets)
{ {
// First of all, try to hit where you aim to
int hitmask = CollisionType_World | CollisionType_Door | CollisionType_HeightMap | CollisionType_Actor;
RayResult result = castRay(origin, origin + (orient * osg::Vec3f(0.0f, queryDistance, 0.0f)), actor, targets, CollisionType_Actor, hitmask);
if (result.mHit)
{
return std::make_pair(result.mHitObject, result.mHitPos);
}
// Use cone shape as fallback
const MWWorld::Store<ESM::GameSetting> &store = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting> &store = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
btConeShape shape (osg::DegreesToRadians(store.find("fCombatAngleXY")->getFloat()/2.0f), queryDistance); btConeShape shape (osg::DegreesToRadians(store.find("fCombatAngleXY")->getFloat()/2.0f), queryDistance);

View file

@ -1073,19 +1073,28 @@ namespace MWWorld
osg::Quat rot = osg::Quat(posdata.rot[0], osg::Vec3f(-1,0,0)) * osg::Quat(posdata.rot[2], osg::Vec3f(0,0,-1)); osg::Quat rot = osg::Quat(posdata.rot[0], osg::Vec3f(-1,0,0)) * osg::Quat(posdata.rot[2], osg::Vec3f(0,0,-1));
osg::Vec3f halfExtents = mPhysics->getHalfExtents(ptr);
// the origin of hitbox is an actor's front, not center
distance += halfExtents.y();
// special cased for better aiming with the camera
// if we do not hit anything, will use the default approach as fallback
if (ptr == getPlayerPtr())
{
osg::Vec3f pos = getActorHeadTransform(ptr).getTrans();
std::pair<MWWorld::Ptr,osg::Vec3f> result = mPhysics->getHitContact(ptr, pos, rot, distance, targets);
if(!result.first.isEmpty())
return std::make_pair(result.first, result.second);
}
osg::Vec3f pos = ptr.getRefData().getPosition().asVec3(); osg::Vec3f pos = ptr.getRefData().getPosition().asVec3();
if (ptr == getPlayerPtr()) // general case, compatible with all types of different creatures
pos = getActorHeadTransform(ptr).getTrans(); // special cased for better aiming with the camera // note: we intentionally do *not* use the collision box offset here, this is required to make
else // some flying creatures work that have their collision box offset in the air
{ pos.z() += halfExtents.z();
// general case, compatible with all types of different creatures
// note: we intentionally do *not* use the collision box offset here, this is required to make
// some flying creatures work that have their collision box offset in the air
osg::Vec3f halfExtents = mPhysics->getHalfExtents(ptr);
pos.z() += halfExtents.z() * 2 * 0.75;
distance += halfExtents.y();
}
std::pair<MWWorld::Ptr,osg::Vec3f> result = mPhysics->getHitContact(ptr, pos, rot, distance, targets); std::pair<MWWorld::Ptr,osg::Vec3f> result = mPhysics->getHitContact(ptr, pos, rot, distance, targets);
if(result.first.isEmpty()) if(result.first.isEmpty())
@ -2752,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); // for player we can take faced object first
MWWorld::Ptr target;
if (actor == MWMechanics::getPlayer())
target = getFacedObject();
float dist1 = std::numeric_limits<float>::max(); // if the faced object can not be activated, do not use it
float dist2 = std::numeric_limits<float>::max(); if (!target.isEmpty() && !target.getClass().canBeActivated(target))
target = NULL;
if (result1.mHit) if (target.isEmpty())
dist1 = (origin - result1.mHitPos).length();
if (result2.mHit)
dist2 = (origin - result2.mHitPointWorld).length();
if (result1.mHit)
{ {
target = result1.mHitObject; // For actor targets, we want to use hit contact with bounding boxes.
hitPosition = result1.mHitPos; // 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 (dist1 > getMaxActivationDistance() && !target.isEmpty() && (target.getClass().isActor() || !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);
else if (result2.mHit)
{
target = result2.mHitObject;
hitPosition = result2.mHitPointWorld;
if (dist2 > getMaxActivationDistance() && !target.isEmpty() && !target.getClass().canBeActivated(target))
target = NULL;
}
// 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();
@ -3466,7 +3477,7 @@ namespace MWWorld
osg::Vec3f World::aimToTarget(const ConstPtr &actor, const MWWorld::ConstPtr& target) osg::Vec3f World::aimToTarget(const ConstPtr &actor, const MWWorld::ConstPtr& target)
{ {
osg::Vec3f weaponPos = actor.getRefData().getPosition().asVec3(); osg::Vec3f weaponPos = actor.getRefData().getPosition().asVec3();
weaponPos.z() += mPhysics->getHalfExtents(actor).z() * 2 * 0.75; weaponPos.z() += mPhysics->getHalfExtents(actor).z();
osg::Vec3f targetPos = mPhysics->getCollisionObjectPosition(target); osg::Vec3f targetPos = mPhysics->getCollisionObjectPosition(target);
return (targetPos - weaponPos); return (targetPos - weaponPos);
} }
@ -3475,7 +3486,7 @@ namespace MWWorld
{ {
osg::Vec3f weaponPos = actor.getRefData().getPosition().asVec3(); osg::Vec3f weaponPos = actor.getRefData().getPosition().asVec3();
osg::Vec3f halfExtents = mPhysics->getHalfExtents(actor); osg::Vec3f halfExtents = mPhysics->getHalfExtents(actor);
weaponPos.z() += halfExtents.z() * 2 * 0.75; weaponPos.z() += halfExtents.z();
return mPhysics->getHitDistance(weaponPos, target) - halfExtents.y(); return mPhysics->getHitDistance(weaponPos, target) - halfExtents.y();
} }