From ab433102a49057d8ae4eb7219674e75960313045 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 9 May 2018 10:31:53 +0400 Subject: [PATCH 1/6] Increase hit distance for player by halfExtents --- apps/openmw/mwworld/worldimp.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 13970ee13..61bed46f7 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1074,6 +1074,7 @@ 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::Vec3f pos = ptr.getRefData().getPosition().asVec3(); + osg::Vec3f halfExtents = mPhysics->getHalfExtents(ptr); if (ptr == getPlayerPtr()) pos = getActorHeadTransform(ptr).getTrans(); // special cased for better aiming with the camera @@ -1082,11 +1083,12 @@ namespace MWWorld // 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(); } + // the origin of hitbox is an actor's front, not center + distance += halfExtents.y(); + std::pair result = mPhysics->getHitContact(ptr, pos, rot, distance, targets); if(result.first.isEmpty()) return std::make_pair(MWWorld::Ptr(), osg::Vec3f()); From f5dc9f01620306c147c1428a1bb39eee0c286acf Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 9 May 2018 10:53:53 +0400 Subject: [PATCH 2/6] Use hitbox cone only as fallback --- apps/openmw/mwphysics/physicssystem.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index f3c34bc4e..4830760af 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -846,6 +846,16 @@ namespace MWPhysics const osg::Quat &orient, float queryDistance, std::vector 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, 0xff, hitmask); + + if (result.mHit) + { + return std::make_pair(result.mHitObject, result.mHitPos); + } + + // Use cone shape as fallback const MWWorld::Store &store = MWBase::Environment::get().getWorld()->getStore().get(); btConeShape shape (osg::DegreesToRadians(store.find("fCombatAngleXY")->getFloat()/2.0f), queryDistance); From 9e5d577a71dab1fffec695121b400e9b4f675b93 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 9 May 2018 11:35:51 +0400 Subject: [PATCH 3/6] Aim from center of attacker to center of target --- apps/openmw/mwworld/worldimp.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 61bed46f7..586fc7816 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1083,7 +1083,7 @@ namespace MWWorld // 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 - pos.z() += halfExtents.z() * 2 * 0.75; + pos.z() += halfExtents.z(); } // the origin of hitbox is an actor's front, not center @@ -3468,7 +3468,7 @@ namespace MWWorld osg::Vec3f World::aimToTarget(const ConstPtr &actor, const MWWorld::ConstPtr& target) { 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); return (targetPos - weaponPos); } @@ -3477,7 +3477,7 @@ namespace MWWorld { osg::Vec3f weaponPos = actor.getRefData().getPosition().asVec3(); osg::Vec3f halfExtents = mPhysics->getHalfExtents(actor); - weaponPos.z() += halfExtents.z() * 2 * 0.75; + weaponPos.z() += halfExtents.z(); return mPhysics->getHitDistance(weaponPos, target) - halfExtents.y(); } From 4666a6a0abc221806fda56c85c5d5b910ef5f51d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 9 May 2018 12:53:58 +0400 Subject: [PATCH 4/6] Use default hit formula as fallback --- apps/openmw/mwworld/worldimp.cpp | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 586fc7816..8423adedc 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1073,21 +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::Vec3f pos = ptr.getRefData().getPosition().asVec3(); 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()) - pos = getActorHeadTransform(ptr).getTrans(); // special cased for better aiming with the camera - else { - // 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 - pos.z() += halfExtents.z(); + osg::Vec3f pos = getActorHeadTransform(ptr).getTrans(); + + std::pair result = mPhysics->getHitContact(ptr, pos, rot, distance, targets); + if(!result.first.isEmpty()) + return std::make_pair(result.first, result.second); } - // the origin of hitbox is an actor's front, not center - distance += halfExtents.y(); + osg::Vec3f pos = ptr.getRefData().getPosition().asVec3(); + + // 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 + pos.z() += halfExtents.z(); std::pair result = mPhysics->getHitContact(ptr, pos, rot, distance, targets); if(result.first.isEmpty()) From bde1d07d4eb756da90f31698aa154920fa102cd1 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 15 May 2018 18:57:36 +0400 Subject: [PATCH 5/6] Use hitboxes and focused object for touch spells (bug #3374) --- apps/openmw/mwphysics/physicssystem.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 94 +++++++++++++------------ 2 files changed, 49 insertions(+), 47 deletions(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 4830760af..625ae5573 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -848,7 +848,7 @@ namespace MWPhysics { // 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, 0xff, hitmask); + RayResult result = castRay(origin, origin + (orient * osg::Vec3f(0.0f, queryDistance, 0.0f)), actor, targets, CollisionType_Actor, hitmask); if (result.mHit) { diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8423adedc..cbd5b9340 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2761,64 +2761,66 @@ namespace MWWorld { 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. std::vector targetActors; if (!actor.isEmpty() && actor != MWMechanics::getPlayer()) actor.getClass().getCreatureStats(actor).getAiSequence().getCombatTargets(targetActors); - // For actor targets, we want to use bounding boxes (physics raycast). - // 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. + const float fCombatDistance = getStore().get().find("fCombatDistance")->getFloat(); - MWPhysics::PhysicsSystem::RayResult result1 = mPhysics->castRay(origin, dest, actor, targetActors, MWPhysics::CollisionType_Actor); - - MWRender::RenderingManager::RayResult result2 = mRendering->castRay(origin, dest, true, true); + osg::Vec3f hitPosition = actor.getRefData().getPosition().asVec3(); - float dist1 = std::numeric_limits::max(); - float dist2 = std::numeric_limits::max(); + // for player we can take faced object first + MWWorld::Ptr target; + if (actor == MWMechanics::getPlayer()) + target = getFacedObject(); - if (result1.mHit) - dist1 = (origin - result1.mHitPos).length(); - if (result2.mHit) - dist2 = (origin - result2.mHitPointWorld).length(); + // if the faced object can not be activated, do not use it + if (!target.isEmpty() && !target.getClass().canBeActivated(target)) + target = NULL; - if (result1.mHit) - { - target = result1.mHitObject; - hitPosition = result1.mHitPos; - if (dist1 > getMaxActivationDistance() && !target.isEmpty() && (target.getClass().isActor() || !target.getClass().canBeActivated(target))) - target = NULL; - } - else if (result2.mHit) + if (target.isEmpty()) { - target = result2.mHitObject; - hitPosition = result2.mHitPointWorld; - if (dist2 > getMaxActivationDistance() && !target.isEmpty() && !target.getClass().canBeActivated(target)) - target = NULL; - } + // For actor targets, we want to use hit contact with bounding boxes. + // 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. + std::pair result1 = getHitContact(actor, fCombatDistance, targetActors); - // When targeting an actor that is in combat with an "on touch" spell, - // compare against the minimum of activation distance and combat distance. + // Get the target to use for "on touch" effects, using the facing direction from Head node + osg::Vec3f origin = getActorHeadTransform(actor).getTrans(); - if (!target.isEmpty() && target.getClass().isActor() && target.getClass().getCreatureStats (target).getAiSequence().isInCombat()) - { - distance = std::min (distance, getStore().get().find("fCombatDistance")->getFloat()); - if (distance < dist1) - target = NULL; + 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); + float distance = getMaxActivationDistance(); + osg::Vec3f dest = origin + direction * distance; + + MWRender::RenderingManager::RayResult result2 = mRendering->castRay(origin, dest, true, true); + + float dist1 = std::numeric_limits::max(); + float dist2 = std::numeric_limits::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(); From 05026b891ea52b06d056e080cfb4e264c71a9ce9 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 7 Jun 2018 20:42:18 +0400 Subject: [PATCH 6/6] Add changelog entries --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cbef559d..30b2af930 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ 0.45.0 ------ + Bug #3374: Touch spells not hitting kwama foragers + Bug #3591: Angled hit distance too low Bug #4293: Faction members are not aware of faction ownerships in barter Bug #4426: RotateWorld behavior is incorrect Bug #4433: Guard behaviour is incorrect with Alarm = 0