1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-03-28 19:21:04 +00:00

Merge branch 'teleport_here_not_there' into 'master'

Fix  #5919 (and another bug)

Closes #5919

See merge request OpenMW/openmw!722
This commit is contained in:
psi29a 2021-04-10 12:51:42 +00:00
commit 603e4206fd
8 changed files with 54 additions and 75 deletions

View File

@ -281,13 +281,13 @@ namespace MWBase
virtual void deleteObject (const MWWorld::Ptr& ptr) = 0; virtual void deleteObject (const MWWorld::Ptr& ptr) = 0;
virtual void undeleteObject (const MWWorld::Ptr& ptr) = 0; virtual void undeleteObject (const MWWorld::Ptr& ptr) = 0;
virtual MWWorld::Ptr moveObject (const MWWorld::Ptr& ptr, float x, float y, float z, bool moveToActive=false) = 0; virtual MWWorld::Ptr moveObject (const MWWorld::Ptr& ptr, float x, float y, float z, bool movePhysics=true, bool moveToActive=false) = 0;
///< @return an updated Ptr in case the Ptr's cell changes ///< @return an updated Ptr in case the Ptr's cell changes
virtual MWWorld::Ptr moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore* newCell, float x, float y, float z, bool movePhysics=true) = 0; virtual MWWorld::Ptr moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore* newCell, float x, float y, float z, bool movePhysics=true) = 0;
///< @return an updated Ptr ///< @return an updated Ptr
virtual MWWorld::Ptr moveObjectBy(const MWWorld::Ptr &ptr, osg::Vec3f vec) = 0; virtual MWWorld::Ptr moveObjectBy(const MWWorld::Ptr &ptr, osg::Vec3f vec, bool moveToActive) = 0;
///< @return an updated Ptr ///< @return an updated Ptr
virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0; virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0;

View File

@ -3,8 +3,6 @@
#include <BulletCollision/CollisionShapes/btBoxShape.h> #include <BulletCollision/CollisionShapes/btBoxShape.h>
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h> #include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
#include <apps/openmw/mwmechanics/actorutil.hpp>
#include <apps/openmw/mwworld/cellstore.hpp>
#include <components/sceneutil/positionattitudetransform.hpp> #include <components/sceneutil/positionattitudetransform.hpp>
#include <components/resource/bulletshape.hpp> #include <components/resource/bulletshape.hpp>
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
@ -181,6 +179,7 @@ bool Actor::setPosition(const osg::Vec3f& position)
if (mSkipSimulation) if (mSkipSimulation)
return false; return false;
bool hasChanged = mPosition != position || mPositionOffset.length() != 0 || mWorldPositionChanged; bool hasChanged = mPosition != position || mPositionOffset.length() != 0 || mWorldPositionChanged;
updateWorldPosition();
applyOffsetChange(); applyOffsetChange();
mPreviousPosition = mPosition; mPreviousPosition = mPosition;
mPosition = position; mPosition = position;
@ -197,15 +196,6 @@ void Actor::applyOffsetChange()
{ {
if (mPositionOffset.length() == 0) if (mPositionOffset.length() == 0)
return; return;
if (mPositionOffset.z() != 0)
{
// Often, offset are set in sequence x, y, z
// We don't want actors to be moved under the ground
// Check terrain height at new coordinate and update z offset if necessary
const auto pos = mWorldPosition + mPositionOffset;
const auto terrainHeight = mPtr.getCell()->isExterior() ? MWBase::Environment::get().getWorld()->getTerrainHeightAt(pos) : -std::numeric_limits<float>::max();
mPositionOffset.z() = std::max(pos.z(), terrainHeight) - mWorldPosition.z();
}
mWorldPosition += mPositionOffset; mWorldPosition += mPositionOffset;
mPosition += mPositionOffset; mPosition += mPositionOffset;
mPreviousPosition += mPositionOffset; mPreviousPosition += mPositionOffset;

View File

@ -317,7 +317,7 @@ namespace MWPhysics
// init // init
for (auto& data : actorsData) for (auto& data : actorsData)
data.updatePosition(); data.updatePosition(mCollisionWorld);
mPrevStepCount = numSteps; mPrevStepCount = numSteps;
mRemainingSteps = numSteps; mRemainingSteps = numSteps;
mTimeAccum = timeAccum; mTimeAccum = timeAccum;

View File

@ -60,6 +60,22 @@
#include "movementsolver.hpp" #include "movementsolver.hpp"
#include "mtphysics.hpp" #include "mtphysics.hpp"
namespace
{
bool canMoveToWaterSurface(const MWPhysics::Actor* physicActor, const float waterlevel, btCollisionWorld* world)
{
if (!physicActor)
return false;
const float halfZ = physicActor->getHalfExtents().z();
const osg::Vec3f actorPosition = physicActor->getPosition();
const osg::Vec3f startingPosition(actorPosition.x(), actorPosition.y(), actorPosition.z() + halfZ);
const osg::Vec3f destinationPosition(actorPosition.x(), actorPosition.y(), waterlevel + halfZ);
MWPhysics::ActorTracer tracer;
tracer.doTrace(physicActor->getCollisionObject(), startingPosition, destinationPosition, world);
return (tracer.mFraction >= 1.0f);
}
}
namespace MWPhysics namespace MWPhysics
{ {
PhysicsSystem::PhysicsSystem(Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Group> parentNode) PhysicsSystem::PhysicsSystem(Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Group> parentNode)
@ -347,16 +363,7 @@ namespace MWPhysics
bool PhysicsSystem::canMoveToWaterSurface(const MWWorld::ConstPtr &actor, const float waterlevel) bool PhysicsSystem::canMoveToWaterSurface(const MWWorld::ConstPtr &actor, const float waterlevel)
{ {
const Actor* physicActor = getActor(actor); return ::canMoveToWaterSurface(getActor(actor), waterlevel, mCollisionWorld.get());
if (!physicActor)
return false;
const float halfZ = physicActor->getHalfExtents().z();
const osg::Vec3f actorPosition = physicActor->getPosition();
const osg::Vec3f startingPosition(actorPosition.x(), actorPosition.y(), actorPosition.z() + halfZ);
const osg::Vec3f destinationPosition(actorPosition.x(), actorPosition.y(), waterlevel + halfZ);
ActorTracer tracer;
tracer.doTrace(physicActor->getCollisionObject(), startingPosition, destinationPosition, mCollisionWorld.get());
return (tracer.mFraction >= 1.0f);
} }
osg::Vec3f PhysicsSystem::getHalfExtents(const MWWorld::ConstPtr &actor) const osg::Vec3f PhysicsSystem::getHalfExtents(const MWWorld::ConstPtr &actor) const
@ -772,16 +779,10 @@ namespace MWPhysics
const MWMechanics::MagicEffects& effects = character.getClass().getCreatureStats(character).getMagicEffects(); const MWMechanics::MagicEffects& effects = character.getClass().getCreatureStats(character).getMagicEffects();
bool waterCollision = false; bool waterCollision = false;
bool moveToWaterSurface = false;
if (cell->getCell()->hasWater() && effects.get(ESM::MagicEffect::WaterWalking).getMagnitude()) if (cell->getCell()->hasWater() && effects.get(ESM::MagicEffect::WaterWalking).getMagnitude())
{ {
if (!world->isUnderwater(character.getCell(), osg::Vec3f(character.getRefData().getPosition().asVec3()))) if (physicActor->getCollisionMode() || !world->isUnderwater(character.getCell(), osg::Vec3f(character.getRefData().getPosition().asVec3())))
waterCollision = true; waterCollision = true;
else if (physicActor->getCollisionMode() && canMoveToWaterSurface(character, waterlevel))
{
moveToWaterSurface = true;
waterCollision = true;
}
} }
physicActor->setCanWaterWalk(waterCollision); physicActor->setCanWaterWalk(waterCollision);
@ -794,7 +795,7 @@ namespace MWPhysics
if (!willSimulate) if (!willSimulate)
standingOn = physicActor->getStandingOnPtr(); standingOn = physicActor->getStandingOnPtr();
actorsFrameData.emplace_back(std::move(physicActor), standingOn, moveToWaterSurface, movement, slowFall, waterlevel); actorsFrameData.emplace_back(std::move(physicActor), standingOn, waterCollision, movement, slowFall, waterlevel);
} }
mMovementQueue.clear(); mMovementQueue.clear();
return actorsFrameData; return actorsFrameData;
@ -937,9 +938,9 @@ namespace MWPhysics
} }
ActorFrameData::ActorFrameData(const std::shared_ptr<Actor>& actor, const MWWorld::Ptr standingOn, ActorFrameData::ActorFrameData(const std::shared_ptr<Actor>& actor, const MWWorld::Ptr standingOn,
bool moveToWaterSurface, osg::Vec3f movement, float slowFall, float waterlevel) bool waterCollision, osg::Vec3f movement, float slowFall, float waterlevel)
: mActor(actor), mActorRaw(actor.get()), mStandingOn(standingOn), : mActor(actor), mActorRaw(actor.get()), mStandingOn(standingOn),
mDidJump(false), mNeedLand(false), mMoveToWaterSurface(moveToWaterSurface), mDidJump(false), mNeedLand(false), mWaterCollision(waterCollision),
mWaterlevel(waterlevel), mSlowFall(slowFall), mOldHeight(0), mFallHeight(0), mMovement(movement), mPosition(), mRefpos() mWaterlevel(waterlevel), mSlowFall(slowFall), mOldHeight(0), mFallHeight(0), mMovement(movement), mPosition(), mRefpos()
{ {
const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWBase::World *world = MWBase::Environment::get().getWorld();
@ -953,7 +954,7 @@ namespace MWPhysics
mWasOnGround = actor->getOnGround(); mWasOnGround = actor->getOnGround();
} }
void ActorFrameData::updatePosition() void ActorFrameData::updatePosition(btCollisionWorld* world)
{ {
mActorRaw->updateWorldPosition(); mActorRaw->updateWorldPosition();
// If physics runs "fast enough", position are interpolated without simulation // If physics runs "fast enough", position are interpolated without simulation
@ -961,10 +962,10 @@ namespace MWPhysics
// regardless of simulation speed. // regardless of simulation speed.
mActorRaw->applyOffsetChange(); mActorRaw->applyOffsetChange();
mPosition = mActorRaw->getPosition(); mPosition = mActorRaw->getPosition();
if (mMoveToWaterSurface) if (mWaterCollision && mPosition.z() < mWaterlevel && canMoveToWaterSurface(mActorRaw, mWaterlevel, world))
{ {
mPosition.z() = mWaterlevel; mPosition.z() = mWaterlevel;
MWBase::Environment::get().getWorld()->moveObject(mActorRaw->getPtr(), mPosition.x(), mPosition.y(), mPosition.z()); MWBase::Environment::get().getWorld()->moveObject(mActorRaw->getPtr(), mPosition.x(), mPosition.y(), mPosition.z(), false);
} }
mOldHeight = mPosition.z(); mOldHeight = mPosition.z();
mRefpos = mActorRaw->getPtr().getRefData().getPosition(); mRefpos = mActorRaw->getPtr().getRefData().getPosition();

View File

@ -79,7 +79,7 @@ namespace MWPhysics
struct ActorFrameData struct ActorFrameData
{ {
ActorFrameData(const std::shared_ptr<Actor>& actor, const MWWorld::Ptr standingOn, bool moveToWaterSurface, osg::Vec3f movement, float slowFall, float waterlevel); ActorFrameData(const std::shared_ptr<Actor>& actor, const MWWorld::Ptr standingOn, bool moveToWaterSurface, osg::Vec3f movement, float slowFall, float waterlevel);
void updatePosition(); void updatePosition(btCollisionWorld* world);
std::weak_ptr<Actor> mActor; std::weak_ptr<Actor> mActor;
Actor* mActorRaw; Actor* mActorRaw;
MWWorld::Ptr mStandingOn; MWWorld::Ptr mStandingOn;
@ -90,7 +90,7 @@ namespace MWPhysics
bool mDidJump; bool mDidJump;
bool mFloatToSurface; bool mFloatToSurface;
bool mNeedLand; bool mNeedLand;
bool mMoveToWaterSurface; bool mWaterCollision;
float mWaterlevel; float mWaterlevel;
float mSlowFall; float mSlowFall;
float mOldHeight; float mOldHeight;

View File

@ -32,7 +32,7 @@ namespace MWScript
std::vector<MWWorld::Ptr> actors; std::vector<MWWorld::Ptr> actors;
MWBase::Environment::get().getWorld()->getActorsStandingOn (ptr, actors); MWBase::Environment::get().getWorld()->getActorsStandingOn (ptr, actors);
for (auto& actor : actors) for (auto& actor : actors)
MWBase::Environment::get().getWorld()->moveObjectBy(actor, diff); MWBase::Environment::get().getWorld()->moveObjectBy(actor, diff, false);
} }
template<class R> template<class R>
@ -284,6 +284,17 @@ namespace MWScript
} }
else if(axis == "z") else if(axis == "z")
{ {
// We should not place actors under ground
if (ptr.getClass().isActor())
{
float terrainHeight = -std::numeric_limits<float>::max();
if (ptr.getCell()->isExterior())
terrainHeight = MWBase::Environment::get().getWorld()->getTerrainHeightAt(curPos);
if (pos < terrainHeight)
pos = terrainHeight;
}
newPos[2] = pos; newPos[2] = pos;
} }
else else
@ -292,7 +303,7 @@ namespace MWScript
} }
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr, dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr,
MWBase::Environment::get().getWorld()->moveObjectBy(ptr, newPos - curPos)); MWBase::Environment::get().getWorld()->moveObjectBy(ptr, newPos - curPos, true));
} }
}; };
@ -428,7 +439,7 @@ namespace MWScript
} }
else else
{ {
ptr = MWBase::Environment::get().getWorld()->moveObject(ptr, x, y, z, true); ptr = MWBase::Environment::get().getWorld()->moveObject(ptr, x, y, z, true, true);
} }
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(base,ptr); dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(base,ptr);
@ -715,7 +726,7 @@ namespace MWScript
// This approach can be used to create elevators. // This approach can be used to create elevators.
moveStandingActors(ptr, diff); moveStandingActors(ptr, diff);
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr, dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr,
MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff)); MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff, false));
} }
}; };
@ -751,7 +762,7 @@ namespace MWScript
// This approach can be used to create elevators. // This approach can be used to create elevators.
moveStandingActors(ptr, diff); moveStandingActors(ptr, diff);
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr, dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr,
MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff)); MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff, false));
} }
}; };

View File

@ -862,19 +862,6 @@ namespace MWWorld
if (reference == getPlayerPtr()) if (reference == getPlayerPtr())
throw std::runtime_error("can not disable player object"); throw std::runtime_error("can not disable player object");
// A common pattern to teleport NPC in scripts is a sequence of SetPos/Disable/Enable
// Disable/Enable create a new physics actor, and so the SetPos call is lost
// Call moveObject so that the newly created physics actor will have up-to-date position
if (reference.getClass().isActor())
{
auto* physactor = mPhysics->getActor(reference);
if (physactor)
{
physactor->applyOffsetChange();
const auto position = physactor->getSimulationPosition();
moveObject(reference, position.x(), position.y(), position.z(), true);
}
}
reference.getRefData().disable(); reference.getRefData().disable();
if (reference.getCellRef().getRefNum().hasContentFile()) if (reference.getCellRef().getRefNum().hasContentFile())
@ -1251,7 +1238,7 @@ namespace MWWorld
return newPtr; return newPtr;
} }
MWWorld::Ptr World::moveObjectImp(const Ptr& ptr, float x, float y, float z, bool movePhysics, bool moveToActive) MWWorld::Ptr World::moveObject (const Ptr& ptr, float x, float y, float z, bool movePhysics, bool moveToActive)
{ {
int cellX, cellY; int cellX, cellY;
positionToIndex(x, y, cellX, cellY); positionToIndex(x, y, cellX, cellY);
@ -1266,21 +1253,14 @@ namespace MWWorld
return moveObject(ptr, cell, x, y, z, movePhysics); return moveObject(ptr, cell, x, y, z, movePhysics);
} }
MWWorld::Ptr World::moveObject (const Ptr& ptr, float x, float y, float z, bool moveToActive) MWWorld::Ptr World::moveObjectBy(const Ptr& ptr, osg::Vec3f vec, bool moveToActive)
{
return moveObjectImp(ptr, x, y, z, true, moveToActive);
}
MWWorld::Ptr World::moveObjectBy(const Ptr& ptr, osg::Vec3f vec)
{ {
auto* actor = mPhysics->getActor(ptr); auto* actor = mPhysics->getActor(ptr);
if (actor) if (actor)
{
actor->adjustPosition(vec); actor->adjustPosition(vec);
return ptr;
}
osg::Vec3f newpos = ptr.getRefData().getPosition().asVec3() + vec; osg::Vec3f newpos = ptr.getRefData().getPosition().asVec3() + vec;
return moveObject(ptr, newpos.x(), newpos.y(), newpos.z()); return moveObject(ptr, newpos.x(), newpos.y(), newpos.z(), false, moveToActive && ptr != getPlayerPtr());
} }
void World::scaleObject (const Ptr& ptr, float scale) void World::scaleObject (const Ptr& ptr, float scale)
@ -1546,7 +1526,7 @@ namespace MWWorld
auto* physactor = mPhysics->getActor(actor); auto* physactor = mPhysics->getActor(actor);
assert(physactor); assert(physactor);
const auto position = physactor->getSimulationPosition(); const auto position = physactor->getSimulationPosition();
moveObjectImp(actor, position.x(), position.y(), position.z(), false); moveObject(actor, position.x(), position.y(), position.z(), false, false);
} }
} }
@ -1556,7 +1536,7 @@ namespace MWWorld
auto* physactor = mPhysics->getActor(*player); auto* physactor = mPhysics->getActor(*player);
assert(physactor); assert(physactor);
const auto position = physactor->getSimulationPosition(); const auto position = physactor->getSimulationPosition();
moveObjectImp(*player, position.x(), position.y(), position.z(), false); moveObject(*player, position.x(), position.y(), position.z(), false, false);
} }
} }

View File

@ -138,9 +138,6 @@ namespace MWWorld
void rotateObjectImp (const Ptr& ptr, const osg::Vec3f& rot, MWBase::RotationFlags flags); void rotateObjectImp (const Ptr& ptr, const osg::Vec3f& rot, MWBase::RotationFlags flags);
Ptr moveObjectImp (const Ptr& ptr, float x, float y, float z, bool movePhysics=true, bool moveToActive=false);
///< @return an updated Ptr in case the Ptr's cell changes
Ptr copyObjectToCell(const ConstPtr &ptr, CellStore* cell, ESM::Position pos, int count, bool adjustPos); Ptr copyObjectToCell(const ConstPtr &ptr, CellStore* cell, ESM::Position pos, int count, bool adjustPos);
void updateSoundListener(); void updateSoundListener();
@ -376,13 +373,13 @@ namespace MWWorld
void undeleteObject (const Ptr& ptr) override; void undeleteObject (const Ptr& ptr) override;
MWWorld::Ptr moveObject (const Ptr& ptr, float x, float y, float z, bool moveToActive=false) override; MWWorld::Ptr moveObject (const Ptr& ptr, float x, float y, float z, bool movePhysics=true, bool moveToActive=false) override;
///< @return an updated Ptr in case the Ptr's cell changes ///< @return an updated Ptr in case the Ptr's cell changes
MWWorld::Ptr moveObject (const Ptr& ptr, CellStore* newCell, float x, float y, float z, bool movePhysics=true) override; MWWorld::Ptr moveObject (const Ptr& ptr, CellStore* newCell, float x, float y, float z, bool movePhysics=true) override;
///< @return an updated Ptr ///< @return an updated Ptr
MWWorld::Ptr moveObjectBy(const Ptr& ptr, osg::Vec3f vec) override; MWWorld::Ptr moveObjectBy(const Ptr& ptr, osg::Vec3f vec, bool moveToActive) override;
///< @return an updated Ptr ///< @return an updated Ptr
void scaleObject (const Ptr& ptr, float scale) override; void scaleObject (const Ptr& ptr, float scale) override;