From b43c7238e92699b33ffd888fdf6083f522ee073c Mon Sep 17 00:00:00 2001 From: Sam Hellawell Date: Wed, 15 May 2024 04:59:55 +0100 Subject: [PATCH 1/6] Fix infinite fall into void (#1415) --- CHANGELOG.md | 1 + apps/openmw/mwworld/scene.cpp | 51 +++++++++++++++++++++++++++++------ apps/openmw/mwworld/scene.hpp | 1 + 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d9ef163dc..000019935f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -172,6 +172,7 @@ Bug #7901: Editor: Teleport-related fields shouldn't be editable if a ref does not teleport Bug #7908: Key bindings names in the settings menu are layout-specific Bug #7943: Using "addSoulGem" and "dropSoulGem" commands to creatures works only with "Weapon & Shield" flagged ones + 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 diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 169e4bfd21..881d114ffc 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -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(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::max(); mPreloader->clear(); } @@ -556,12 +571,28 @@ namespace MWWorld void Scene::playerMoved(const osg::Vec3f& pos) { - if (!mCurrentCell || !mCurrentCell->isExterior()) + if (!mCurrentCell) return; - osg::Vec2i newCell = getNewGridCenter(pos, &mCurrentGridCenter); - if (newCell != mCurrentGridCenter) - requestChangeCellGrid(pos, newCell); + // 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(); + + ESM::Position newPos; + const ESM::RefId refId = MWBase::Environment::get().getWorld()->findInteriorPosition(cellNameId, newPos); + MWWorld::ActionTeleport(refId, newPos, false).execute(playerPtr); + } } void Scene::requestChangeCellGrid(const osg::Vec3f& position, const osg::Vec2i& cell, bool changeEvent) @@ -840,6 +871,7 @@ namespace MWWorld , mPreloadDoors(Settings::cells().mPreloadDoors) , mPreloadFastTravel(Settings::cells().mPreloadFastTravel) , mPredictionTime(Settings::cells().mPredictionTime) + , mLowestPoint(std::numeric_limits::max()) { mPreloader = std::make_unique(rendering.getResourceSystem(), physics->getShapeManager(), rendering.getTerrain(), rendering.getLandManager()); @@ -970,20 +1002,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) diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 91feff8746..09653eab88 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -101,6 +101,7 @@ namespace MWWorld bool mPreloadDoors; bool mPreloadFastTravel; float mPredictionTime; + float mLowestPoint; int mHalfGridSize = Constants::CellGridRadius; From e1cfd46f87f0a807266f4d819c8c79470d729df2 Mon Sep 17 00:00:00 2001 From: Sam Hellawell Date: Wed, 15 May 2024 20:41:48 +0100 Subject: [PATCH 2/6] Do not teleport if dest < lowest point or collision disabled, add debug log --- apps/openmw/mwworld/scene.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 881d114ffc..77ef4686d8 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -591,7 +591,14 @@ namespace MWWorld ESM::Position newPos; const ESM::RefId refId = MWBase::Environment::get().getWorld()->findInteriorPosition(cellNameId, newPos); - MWWorld::ActionTeleport(refId, newPos, false).execute(playerPtr); + + // Only teleport if that teleport point is > the lowest point, rare edge case + // also check that collision is enabled, which is opposite to Vanilla + if (world->isActorCollisionEnabled(playerPtr) && 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."; + } } } From e98c4f86d68baa947351ad9d75e098d94b0ab94c Mon Sep 17 00:00:00 2001 From: Sam Hellawell Date: Thu, 16 May 2024 00:14:09 +0100 Subject: [PATCH 3/6] Remove period --- apps/openmw/mwworld/scene.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 77ef4686d8..dd18c66a29 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -597,7 +597,7 @@ namespace MWWorld if (world->isActorCollisionEnabled(playerPtr) && 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."; + Log(Debug::Warning) << "Player position has been reset due to falling into the void"; } } } From 6f7fff409ced488c5a53f09ce5d0f30575a62fa1 Mon Sep 17 00:00:00 2001 From: Sam Hellawell Date: Thu, 16 May 2024 19:38:29 +0100 Subject: [PATCH 4/6] Empty refid check --- apps/openmw/mwworld/scene.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index dd18c66a29..211d656ec3 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -594,7 +594,7 @@ namespace MWWorld // Only teleport if that teleport point is > the lowest point, rare edge case // also check that collision is enabled, which is opposite to Vanilla - if (world->isActorCollisionEnabled(playerPtr) && newPos.pos[2] >= mLowestPoint - lowestPointAdjustment) + if (!refId.empty() && world->isActorCollisionEnabled(playerPtr) && 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"; From 72dbad6fb4e02a4639ae40dfb4ec64f15b8f14a6 Mon Sep 17 00:00:00 2001 From: Sam Hellawell Date: Thu, 16 May 2024 20:10:32 +0100 Subject: [PATCH 5/6] Satisfy king clang --- apps/openmw/mwworld/scene.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 211d656ec3..4a3de77992 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -594,7 +594,8 @@ namespace MWWorld // Only teleport if that teleport point is > the lowest point, rare edge case // also check that collision is enabled, which is opposite to Vanilla - if (!refId.empty() && world->isActorCollisionEnabled(playerPtr) && newPos.pos[2] >= mLowestPoint - lowestPointAdjustment) + if (!refId.empty() && world->isActorCollisionEnabled(playerPtr) + && 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"; From c1cd8bd900c2d1e69993656610869282ee314483 Mon Sep 17 00:00:00 2001 From: Sam Hellawell Date: Sat, 18 May 2024 06:33:06 +0100 Subject: [PATCH 6/6] Check collision before findInteriorPositionfindInteriorPos --- apps/openmw/mwworld/scene.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 4a3de77992..fad477438c 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -589,16 +589,19 @@ namespace MWWorld MWBase::World* world = MWBase::Environment::get().getWorld(); MWWorld::Ptr playerPtr = world->getPlayerPtr(); - ESM::Position newPos; - const ESM::RefId refId = MWBase::Environment::get().getWorld()->findInteriorPosition(cellNameId, newPos); - - // Only teleport if that teleport point is > the lowest point, rare edge case - // also check that collision is enabled, which is opposite to Vanilla - if (!refId.empty() && world->isActorCollisionEnabled(playerPtr) - && newPos.pos[2] >= mLowestPoint - lowestPointAdjustment) + // 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)) { - MWWorld::ActionTeleport(refId, newPos, false).execute(playerPtr); - Log(Debug::Warning) << "Player position has been reset due to falling into the void"; + 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"; + } } } }