mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-29 09:32:45 +00:00
Check for obstacle before back up (bug #4656)
This commit is contained in:
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…
x
Reference in New Issue
Block a user