Merge branch 'fix-infinite-fall-into-void' into 'master'

Fix infinite fall into void (#1415)

Closes #1415

See merge request OpenMW/openmw!4100
esm4-texture
Alexei Kotov 7 months ago
commit a3bfd3a337

@ -174,6 +174,7 @@
Bug #7943: Using "addSoulGem" and "dropSoulGem" commands to creatures works only with "Weapon & Shield" flagged ones
Bug #7970: Difference of GetPCSleep (?) behavior between vanilla and OpenMW
Bug #7980: Paralyzed NPCs' lips move
Feature #1415: Infinite fall failsafe
Feature #2566: Handle NAM9 records for manual cell references
Feature #3537: Shader-based water ripples
Feature #5173: Support for NiFogProperty

@ -39,6 +39,8 @@
#include "../mwphysics/object.hpp"
#include "../mwphysics/physicssystem.hpp"
#include "../mwworld/actionteleport.hpp"
#include "cellpreloader.hpp"
#include "cellstore.hpp"
#include "cellvisitors.hpp"
@ -138,10 +140,22 @@ namespace
}
void addObject(const MWWorld::Ptr& ptr, const MWWorld::World& world, const MWPhysics::PhysicsSystem& physics,
DetourNavigator::Navigator& navigator, const DetourNavigator::UpdateGuard* navigatorUpdateGuard = nullptr)
float& lowestPoint, bool isInterior, DetourNavigator::Navigator& navigator,
const DetourNavigator::UpdateGuard* navigatorUpdateGuard = nullptr)
{
if (const auto object = physics.getObject(ptr))
{
// Find the lowest point of this collision object in world space from its AABB if interior
// this point is used to determine the infinite fall cutoff from lowest point in the cell
if (isInterior)
{
btVector3 aabbMin;
btVector3 aabbMax;
const auto transform = object->getTransform();
object->getShapeInstance()->mCollisionShape->getAabb(transform, aabbMin, aabbMax);
lowestPoint = std::min(lowestPoint, static_cast<float>(aabbMin.z()));
}
const DetourNavigator::ObjectTransform objectTransform{ ptr.getRefData().getPosition(),
ptr.getCellRef().getScale() };
@ -526,6 +540,7 @@ namespace MWWorld
navigatorUpdateGuard.reset();
assert(mActiveCells.empty());
mCurrentCell = nullptr;
mLowestPoint = std::numeric_limits<float>::max();
mPreloader->clear();
}
@ -556,13 +571,40 @@ namespace MWWorld
void Scene::playerMoved(const osg::Vec3f& pos)
{
if (!mCurrentCell || !mCurrentCell->isExterior())
if (!mCurrentCell)
return;
// The player is reset when z is 90 units below the lowest reference bound z.
constexpr float lowestPointAdjustment = -90.0f;
if (mCurrentCell->isExterior())
{
osg::Vec2i newCell = getNewGridCenter(pos, &mCurrentGridCenter);
if (newCell != mCurrentGridCenter)
requestChangeCellGrid(pos, newCell);
}
else if (pos.z() < mLowestPoint + lowestPointAdjustment)
{
// Player has fallen into the void, reset to interior marker/coc (#1415)
const std::string_view cellNameId = mCurrentCell->getCell()->getNameId();
MWBase::World* world = MWBase::Environment::get().getWorld();
MWWorld::Ptr playerPtr = world->getPlayerPtr();
// Check that collision is enabled, which is opposite to Vanilla
// this change was decided in MR #4100 as the behaviour is preferable
if (world->isActorCollisionEnabled(playerPtr))
{
ESM::Position newPos;
const ESM::RefId refId = world->findInteriorPosition(cellNameId, newPos);
// Only teleport if that teleport point is > the lowest point, rare edge case
if (!refId.empty() && newPos.pos[2] >= mLowestPoint - lowestPointAdjustment)
{
MWWorld::ActionTeleport(refId, newPos, false).execute(playerPtr);
Log(Debug::Warning) << "Player position has been reset due to falling into the void";
}
}
}
}
void Scene::requestChangeCellGrid(const osg::Vec3f& position, const osg::Vec2i& cell, bool changeEvent)
{
@ -840,6 +882,7 @@ namespace MWWorld
, mPreloadDoors(Settings::cells().mPreloadDoors)
, mPreloadFastTravel(Settings::cells().mPreloadFastTravel)
, mPredictionTime(Settings::cells().mPredictionTime)
, mLowestPoint(std::numeric_limits<float>::max())
{
mPreloader = std::make_unique<CellPreloader>(rendering.getResourceSystem(), physics->getShapeManager(),
rendering.getTerrain(), rendering.getLandManager());
@ -970,20 +1013,23 @@ namespace MWWorld
void Scene::insertCell(
CellStore& cell, Loading::Listener* loadingListener, const DetourNavigator::UpdateGuard* navigatorUpdateGuard)
{
const bool isInterior = !cell.isExterior();
InsertVisitor insertVisitor(cell, loadingListener);
cell.forEach(insertVisitor);
insertVisitor.insert(
[&](const MWWorld::Ptr& ptr) { addObject(ptr, mWorld, mPagedRefs, *mPhysics, mRendering); });
insertVisitor.insert(
[&](const MWWorld::Ptr& ptr) { addObject(ptr, mWorld, *mPhysics, mNavigator, navigatorUpdateGuard); });
insertVisitor.insert([&](const MWWorld::Ptr& ptr) {
addObject(ptr, mWorld, *mPhysics, mLowestPoint, isInterior, mNavigator, navigatorUpdateGuard);
});
}
void Scene::addObjectToScene(const Ptr& ptr)
{
const bool isInterior = mCurrentCell && !mCurrentCell->isExterior();
try
{
addObject(ptr, mWorld, mPagedRefs, *mPhysics, mRendering);
addObject(ptr, mWorld, *mPhysics, mNavigator);
addObject(ptr, mWorld, *mPhysics, mLowestPoint, isInterior, mNavigator);
mWorld.scaleObject(ptr, ptr.getCellRef().getScale());
}
catch (std::exception& e)

@ -102,6 +102,7 @@ namespace MWWorld
bool mPreloadDoors;
bool mPreloadFastTravel;
float mPredictionTime;
float mLowestPoint;
int mHalfGridSize = Constants::CellGridRadius;

Loading…
Cancel
Save