Check for obstacle before back up (bug #4656)

pull/1946/head
Andrei Kortunov 6 years ago
parent 60241431ab
commit ca07e3a364

@ -134,6 +134,7 @@
Bug #4649: Levelup fully restores health
Bug #4653: Length of non-ASCII strings is handled incorrectly in ESM reader
Bug #4654: Editor: UpdateVisitor does not initialize skeletons for animated objects
Bug #4656: Combat AI: back up behaviour is incorrect
Bug #4668: Editor: Light source color is displayed as an integer
Bug #4669: ToggleCollision should trace the player down after collision being enabled
Bug #4671: knownEffect functions should use modified Alchemy skill

@ -297,9 +297,11 @@ namespace MWBase
///< Queues movement for \a ptr (in local space), to be applied in the next call to
/// doPhysics.
virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2, bool ignoreDoors=false) = 0;
virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2, int mask) = 0;
///< cast a Ray and return true if there is an object in the ray path.
virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) = 0;
virtual bool toggleCollisionMode() = 0;
///< Toggle collision mode for player. If disabled player object should ignore
/// collisions and gravity.

@ -4,6 +4,10 @@
#include <components/esm/aisequence.hpp>
#include <components/sceneutil/positionattitudetransform.hpp>
#include "../mwphysics/collisiontype.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
@ -456,7 +460,48 @@ namespace MWMechanics
mTimerCombatMove = 0.1f + 0.1f * Misc::Rng::rollClosedProbability();
mCombatMove = true;
}
else if (isDistantCombat)
{
// Backing up behaviour
// Actor backs up slightly further away than opponent's weapon range
// (in vanilla - only as far as oponent's weapon range),
// or not at all if opponent is using a ranged weapon
if (targetUsesRanged || distToTarget > rangeAttackOfTarget*1.5) // Don't back up if the target is wielding ranged weapon
return;
// actor should not back up into water
if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(actor), 0.5f))
return;
int mask = MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap | MWPhysics::CollisionType_Door;
// Actor can not back up if there is no free space behind
// Currently we take the 35% of actor's height from the ground as vector height.
// This approach allows us to detect small obstacles (e.g. crates) and curved walls.
osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor);
osg::Vec3f pos = actor.getRefData().getPosition().asVec3();
osg::Vec3f source = pos + osg::Vec3f(0, 0, 0.75f * halfExtents.z());
osg::Vec3f fallbackDirection = actor.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,-1,0);
osg::Vec3f destination = source + fallbackDirection * (halfExtents.y() + 16);
bool isObstacleDetected = MWBase::Environment::get().getWorld()->castRay(source.x(), source.y(), source.z(), destination.x(), destination.y(), destination.z(), mask);
if (isObstacleDetected)
return;
// Check if there is nothing behind - probably actor is near cliff.
// A current approach: cast ray 1.5-yard ray down in 1.5 yard behind actor from 35% of actor's height.
// If we did not hit anything, there is a cliff behind actor.
source = pos + osg::Vec3f(0, 0, 0.75f * halfExtents.z()) + fallbackDirection * (halfExtents.y() + 96);
destination = source - osg::Vec3f(0, 0, 0.75f * halfExtents.z() + 96);
bool isCliffDetected = !MWBase::Environment::get().getWorld()->castRay(source.x(), source.y(), source.z(), destination.x(), destination.y(), destination.z(), mask);
if (isCliffDetected)
return;
mMovement.mPosition[1] = -1;
}
// dodge movements (for NPCs and bipedal creatures)
// Note: do not use for ranged combat yet since in couple with back up behaviour can move actor out of cliff
else if (actor.getClass().isBipedal(actor))
{
// apply sideway movement (kind of dodging) with some probability
@ -468,20 +513,6 @@ namespace MWMechanics
mCombatMove = true;
}
}
// Backing up behaviour
// Actor backs up slightly further away than opponent's weapon range
// (in vanilla - only as far as oponent's weapon range),
// or not at all if opponent is using a ranged weapon
if (isDistantCombat)
{
// actor should not back up into water
if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(actor), 0.5f))
return;
if (!targetUsesRanged && distToTarget <= rangeAttackOfTarget*1.5) // Don't back up if the target is wielding ranged weapon
mMovement.mPosition[1] = -1;
}
}
void AiCombatStorage::updateCombatMove(float duration)

@ -5,6 +5,8 @@
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwphysics/collisiontype.hpp"
#include "../mwworld/cellstore.hpp"
#include "pathgrid.hpp"
@ -246,8 +248,9 @@ namespace MWMechanics
converter.toWorld(temp);
// Add Z offset since path node can overlap with other objects.
// Also ignore doors in raytesting.
int mask = MWPhysics::CollisionType_World;
bool isPathClear = !MWBase::Environment::get().getWorld()->castRay(
startPoint.mX, startPoint.mY, startPoint.mZ+16, temp.mX, temp.mY, temp.mZ+16, true);
startPoint.mX, startPoint.mY, startPoint.mZ+16, temp.mX, temp.mY, temp.mZ+16, mask);
if (isPathClear)
mPath.pop_front();
}

@ -1505,15 +1505,18 @@ namespace MWWorld
moveObjectImp(player->first, player->second.x(), player->second.y(), player->second.z(), false);
}
bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2, bool ignoreDoors)
bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2)
{
int mask = MWPhysics::CollisionType_World | MWPhysics::CollisionType_Door;
bool result = castRay(x1, y1, z1, x2, y2, z2, mask);
return result;
}
bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2, int mask)
{
osg::Vec3f a(x1,y1,z1);
osg::Vec3f b(x2,y2,z2);
int mask = MWPhysics::CollisionType_World;
if (!ignoreDoors)
mask |= MWPhysics::CollisionType_Door;
MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(a, b, MWWorld::Ptr(), std::vector<MWWorld::Ptr>(), mask);
return result.mHit;
}

@ -402,9 +402,11 @@ namespace MWWorld
///< Queues movement for \a ptr (in local space), to be applied in the next call to
/// doPhysics.
bool castRay (float x1, float y1, float z1, float x2, float y2, float z2, bool ignoreDoors=false) override;
bool castRay (float x1, float y1, float z1, float x2, float y2, float z2, int mask) override;
///< cast a Ray and return true if there is an object in the ray path.
bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) override;
bool toggleCollisionMode() override;
///< Toggle collision mode for player. If disabled player object should ignore
/// collisions and gravity.

Loading…
Cancel
Save