From 47b1b0ac39c722de9409390b31e4371fcfb438a5 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 10 Oct 2018 22:44:26 +0300 Subject: [PATCH 01/14] Re-fix water reflections while making a no-GUI screenshot --- apps/openmw/mwgui/loadingscreen.cpp | 13 ++++---- apps/openmw/mwrender/renderingmanager.cpp | 37 +++++------------------ 2 files changed, 13 insertions(+), 37 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 38ab70d544..3bb3ee260d 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -169,19 +169,18 @@ namespace MWGui // We are already using node masks to avoid the scene from being updated/rendered, but node masks don't work for computeBound() mViewer->getSceneData()->setComputeBoundingSphereCallback(new DontComputeBoundCallback); - mShowWallpaper = visible && (MWBase::Environment::get().getStateManager()->getState() - == MWBase::StateManager::State_NoGame); + mVisible = visible; + mLoadingBox->setVisible(mVisible); + setVisible(true); - if (!visible) + if (!mVisible) { + mShowWallpaper = false; draw(); return; } - mVisible = visible; - mLoadingBox->setVisible(mVisible); - - setVisible(true); + mShowWallpaper = MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame; if (mShowWallpaper) { diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index bb0bb2f130..476beb990b 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -805,7 +805,7 @@ namespace MWRender cubeTexture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::NEAREST); cubeTexture->setFilter(osg::Texture::MAG_FILTER,osg::Texture::NEAREST); - + cubeTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); cubeTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); @@ -830,19 +830,14 @@ namespace MWRender stateset->addUniform(new osg::Uniform("cubeMap",0)); stateset->addUniform(new osg::Uniform("mapping",screenshotMapping)); stateset->setTextureAttributeAndModes(0,cubeTexture,osg::StateAttribute::ON); - + quad->setStateSet(stateset); quad->setUpdateCallback(nullptr); screenshotCamera->addChild(quad); - mRootNode->addChild(screenshotCamera); - renderCameraToImage(screenshotCamera,image,screenshotW,screenshotH); - screenshotCamera->removeChildren(0,screenshotCamera->getNumChildren()); - mRootNode->removeChild(screenshotCamera); - return true; } @@ -867,6 +862,8 @@ namespace MWRender image->setDataType(GL_UNSIGNED_BYTE); image->setPixelFormat(texture->getInternalFormat()); + mRootNode->addChild(camera); + // The draw needs to complete before we can copy back our image. osg::ref_ptr callback (new NotifyDrawCompletedCallback); camera->setFinalDrawCallback(callback); @@ -882,32 +879,17 @@ namespace MWRender // now that we've "used up" the current frame, get a fresh framenumber for the next frame() following after the screenshot is completed mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); + + camera->removeChildren(0, camera->getNumChildren()); + mRootNode->removeChild(camera); } void RenderingManager::screenshot(osg::Image *image, int w, int h, osg::Matrixd cameraTransform) { osg::ref_ptr rttCamera (new osg::Camera); - rttCamera->setNodeMask(Mask_RenderToTexture); - rttCamera->attach(osg::Camera::COLOR_BUFFER, image); - rttCamera->setRenderOrder(osg::Camera::PRE_RENDER); - rttCamera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); - rttCamera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT); rttCamera->setProjectionMatrixAsPerspective(mFieldOfView, w/float(h), mNearClip, mViewDistance); rttCamera->setViewMatrix(mViewer->getCamera()->getViewMatrix() * cameraTransform); - rttCamera->setViewport(0, 0, w, h); - - osg::ref_ptr texture (new osg::Texture2D); - texture->setInternalFormat(GL_RGB); - texture->setTextureSize(w, h); - texture->setResizeNonPowerOfTwoHint(false); - texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); - texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); - rttCamera->attach(osg::Camera::COLOR_BUFFER, texture); - - image->setDataType(GL_UNSIGNED_BYTE); - image->setPixelFormat(texture->getInternalFormat()); - rttCamera->setUpdateCallback(new NoTraverseCallback); rttCamera->addChild(mSceneRoot); @@ -916,14 +898,9 @@ namespace MWRender rttCamera->setCullMask(mViewer->getCamera()->getCullMask() & (~Mask_GUI)); - mRootNode->addChild(rttCamera); - rttCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); renderCameraToImage(rttCamera.get(),image,w,h); - - rttCamera->removeChildren(0, rttCamera->getNumChildren()); - mRootNode->removeChild(rttCamera); } osg::Vec4f RenderingManager::getScreenBounds(const MWWorld::Ptr& ptr) From f9271fd7f090d75b2022373aa1028afaf49bac5c Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 12 Oct 2018 10:36:50 +0400 Subject: [PATCH 02/14] Fix crash in the ESM reader, when list is empty (bug #4677) --- CHANGELOG.md | 1 + components/esm/transport.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ad7e9aa6e..2c45c8eb46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -139,6 +139,7 @@ Bug #4671: knownEffect functions should use modified Alchemy skill Bug #4672: Pitch factor is handled incorrectly for crossbow animations Bug #4674: Journal can be opened when settings window is open + Bug #4677: Crash in ESM reader when NPC record has DNAM record without DODT one Feature #912: Editor: Add missing icons to UniversalId tables Feature #1221: Editor: Creature/NPC rendering Feature #1617: Editor: Enchantment effect record verifier diff --git a/components/esm/transport.cpp b/components/esm/transport.cpp index 063c033299..11676ea723 100644 --- a/components/esm/transport.cpp +++ b/components/esm/transport.cpp @@ -1,5 +1,7 @@ #include "transport.hpp" +#include + #include #include @@ -16,7 +18,11 @@ namespace ESM } else if (esm.retSubName().intval == ESM::FourCC<'D','N','A','M'>::value) { - mList.back().mCellName = esm.getHString(); + const std::string name = esm.getHString(); + if (mList.empty()) + Log(Debug::Warning) << "Encountered DNAM record without DODT record, skipped."; + else + mList.back().mCellName = name; } } From ca8744af56812d7d51420b937b3a157304b10a3d Mon Sep 17 00:00:00 2001 From: jvoisin Date: Fri, 12 Oct 2018 09:28:18 +0200 Subject: [PATCH 03/14] Fix crash in the ESM reader, when SCVR has no variable names Fixes bug [#4678](https://gitlab.com/OpenMW/openmw/issues/4678) The `loadSCVR` method assumes that the list of variable names won't be empty, which it might. Instead of crashing, we show a warning and ignore the record. --- CHANGELOG.md | 1 + components/esm/loadscpt.cpp | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c45c8eb46..320e486937 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -140,6 +140,7 @@ Bug #4672: Pitch factor is handled incorrectly for crossbow animations Bug #4674: Journal can be opened when settings window is open Bug #4677: Crash in ESM reader when NPC record has DNAM record without DODT one + Bug #4678: Crash in ESP parser when SCVR has no variable names Feature #912: Editor: Add missing icons to UniversalId tables Feature #1221: Editor: Creature/NPC rendering Feature #1617: Editor: Enchantment effect record verifier diff --git a/components/esm/loadscpt.cpp b/components/esm/loadscpt.cpp index 474c2b4234..808b416d7b 100644 --- a/components/esm/loadscpt.cpp +++ b/components/esm/loadscpt.cpp @@ -30,6 +30,12 @@ namespace ESM // The tmp buffer is a null-byte separated string list, we // just have to pick out one string at a time. char* str = &tmp[0]; + if (!str && mVarNames.size() > 0) + { + Log(Debug::Warning) << "SCVR with no variable names"; + return; + } + for (size_t i = 0; i < mVarNames.size(); i++) { // Support '\r' terminated strings like vanilla. See Bug #1324. From dad0b78901b1ddb57949982c4099ea641749bfb2 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 12 Oct 2018 19:41:34 +0400 Subject: [PATCH 04/14] Avoid overflow when handling output characters (bug #4676) --- apps/openmw/mwgui/bookpage.cpp | 8 ++++---- apps/openmw/mwgui/bookpage.hpp | 5 ++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index 29dfe7f3a6..80e92f15ae 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -506,7 +506,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter while (!stream.eof () && !ucsLineBreak (stream.peek ()) && ucsBreakingSpace (stream.peek ())) { MWGui::GlyphInfo info = GlyphInfo(style->mFont, stream.peek()); - if (info.codePoint >= 0) + if (info.charFound) space_width += static_cast(info.advance + info.bearingX); stream.consume (); } @@ -516,7 +516,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter while (!stream.eof () && !ucsLineBreak (stream.peek ()) && !ucsBreakingSpace (stream.peek ())) { MWGui::GlyphInfo info = GlyphInfo(style->mFont, stream.peek()); - if (info.codePoint >= 0) + if (info.charFound) word_width += static_cast(info.advance + info.bearingX); stream.consume (); } @@ -765,7 +765,7 @@ namespace { MWGui::GlyphInfo info = GlyphInfo(mFont, ch); - if (info.codePoint < 0) + if (!info.charFound) return; MyGUI::FloatRect vr; @@ -787,7 +787,7 @@ namespace { MWGui::GlyphInfo info = GlyphInfo(mFont, ch); - if (info.codePoint >= 0) + if (info.charFound) mCursor.left += static_cast(info.bearingX + info.advance); } diff --git a/apps/openmw/mwgui/bookpage.hpp b/apps/openmw/mwgui/bookpage.hpp index 4cf99794d7..4ea59414df 100644 --- a/apps/openmw/mwgui/bookpage.hpp +++ b/apps/openmw/mwgui/bookpage.hpp @@ -43,6 +43,7 @@ namespace MWGui float advance; float bearingX; float bearingY; + bool charFound; MyGUI::FloatRect uvRect; GlyphInfo(MyGUI::IFont* font, MyGUI::Char ch) @@ -61,15 +62,17 @@ namespace MWGui height = (int) gi->height / scale; advance = (int) gi->advance / scale; uvRect = gi->uvRect; + charFound = true; } else { - codePoint = -1; + codePoint = 0; bearingX = 0; bearingY = 0; width = 0; height = 0; advance = 0; + charFound = false; } } }; From 292f4fcafe204be73543cfd862078bdc1e472815 Mon Sep 17 00:00:00 2001 From: rhtucker Date: Fri, 12 Oct 2018 21:09:27 -0700 Subject: [PATCH 05/14] Unified settings list and cfg file names. Added brief tutorial. --- docs/source/conf.py | 4 ++-- .../reference/modding/settings/index.rst | 18 +++++++++++++++++- .../reference/modding/settings/shaders.rst | 4 ++-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index da59c02e1d..60b25ae57b 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -169,8 +169,8 @@ def setup(app): # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static', - 'manuals/openmw-cs/_static' +html_static_path = [ + '_static' ] # Add any extra paths that contain custom files (such as robots.txt or diff --git a/docs/source/reference/modding/settings/index.rst b/docs/source/reference/modding/settings/index.rst index 56d76a8d10..f7c86b5678 100644 --- a/docs/source/reference/modding/settings/index.rst +++ b/docs/source/reference/modding/settings/index.rst @@ -8,10 +8,26 @@ If you are familiar with ``.ini`` tweaks in Morrowind or the other games, this w All settings described in this section are changed in ``settings.cfg``, located in your OpenMW user directory. See :doc:`../paths` for this location. +Changing Settings +################# + +#. Once you have located your ``settings.cfg`` file, open it in a plain text editor. +#. Find the setting(s) you wish to change in the following pages. +#. If the setting is not already in ``settings.cfg``, + add it by copy and pasting the name exactly as written in this guide. +#. Set the value of the setting by typing ``= `` after the setting on the same line, + using an appropriate value in place of ````. +#. If this is the first setting from it's category that you're adding, + be sure to add the heading in square brackets ``[]`` above it using just the setting type, + i.e. without the word "Settings". + + For example, to delay tooltips popping up by 1 second, add the line ``tooltip delay = 1.0``. + Then to the line above, type ``[GUI]``, as the tooltip delay setting comes from the "GUI Settings" section. + Although this guide attempts to be comprehensive and up to date, you will always be able to find the full list of settings available and their default values in ``settings-default.cfg`` in your main OpenMW installation directory. -The ranges I have included with each setting are the physically possible ranges, not recommendations. +The ranges included with each setting are the physically possible ranges, not recommendations. .. warning:: As the title suggests, these are advanced settings. diff --git a/docs/source/reference/modding/settings/shaders.rst b/docs/source/reference/modding/settings/shaders.rst index 17d0929174..be1ecebf0c 100644 --- a/docs/source/reference/modding/settings/shaders.rst +++ b/docs/source/reference/modding/settings/shaders.rst @@ -1,5 +1,5 @@ -Shader Settings -############### +Shaders Settings +################ force shaders ------------- From b5df385111241e14fcee20795b8c0a88431ffdbb Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 12 Oct 2018 14:11:41 +0400 Subject: [PATCH 06/14] Allow apps without logging system to display log messages --- components/debug/debuglog.hpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/components/debug/debuglog.hpp b/components/debug/debuglog.hpp index f4a8e17bef..f5cdffeda1 100644 --- a/components/debug/debuglog.hpp +++ b/components/debug/debuglog.hpp @@ -8,12 +8,13 @@ namespace Debug { enum Level { - NoLevel = 0, Error = 1, Warning = 2, Info = 3, Verbose = 4, - Marker = Verbose + Marker = Verbose, + + NoLevel = 5 // Do not filter messages in this case }; extern Level CurrentDebugLevel; @@ -30,6 +31,11 @@ public: mLock(sLock), mLevel(level) { + // If the app has no logging system enabled, log level is not specified. + // Show all messages without marker - we just use the plain cout in this case. + if (Debug::CurrentDebugLevel == Debug::NoLevel) + return; + if (mLevel <= Debug::CurrentDebugLevel) std::cout << static_cast(mLevel); } From ca07e3a36449e09ee7f8346161e7cb6beb6ffe3b Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 30 Sep 2018 08:38:55 +0400 Subject: [PATCH 07/14] Check for obstacle before back up (bug #4656) --- CHANGELOG.md | 1 + apps/openmw/mwbase/world.hpp | 4 +- apps/openmw/mwmechanics/aicombat.cpp | 59 +++++++++++++++++++------ apps/openmw/mwmechanics/pathfinding.cpp | 5 ++- apps/openmw/mwworld/worldimp.cpp | 13 +++--- apps/openmw/mwworld/worldimp.hpp | 4 +- 6 files changed, 64 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 320e486937..abed0ba91c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index e17935abcd..027d1fd102 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -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. diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 8f9545f99d..a96832b698 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -4,6 +4,10 @@ #include +#include + +#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) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 1da97a6459..c16cff9e10 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -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(); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 0872e589dc..0f20fa05a1 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -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(), mask); return result.mHit; } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 7df8d1af5b..1592453a20 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -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. From 19fd404b7ba0fd32e45473f91ae3c361758b7513 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 14 Oct 2018 14:44:32 +0300 Subject: [PATCH 08/14] Support soundgen calls for activators (feature #4285) --- CHANGELOG.md | 1 + apps/openmw/mwclass/activator.cpp | 70 +++++++++++++++++++++++++++++++ apps/openmw/mwclass/activator.hpp | 4 ++ 3 files changed, 75 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 320e486937..ddf20fa7f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -155,6 +155,7 @@ Feature #4012: Editor: Write a log file if OpenCS crashes Feature #4222: 360° screenshots Feature #4256: Implement ToggleBorders (TB) console command + Feature #4285: Support soundgen calls for activators Feature #4324: Add CFBundleIdentifier in Info.plist to allow for macOS function key shortcuts Feature #4345: Add equivalents for the command line commands to Launcher Feature #4404: Editor: All EnumDelegate fields should have their items sorted alphabetically diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 8df262f240..7f53709d6c 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -1,6 +1,7 @@ #include "activator.hpp" #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -134,4 +135,73 @@ namespace MWClass return MWWorld::Ptr(cell.insert(ref), &cell); } + + std::string Activator::getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const + { + std::string model = getModel(ptr); + if (model.empty()) + return std::string(); + + const MWWorld::Store &creaturestore = MWBase::Environment::get().getWorld()->getStore().get(); + std::string creatureId; + + for (const ESM::Creature &iter : creaturestore) + { + if (iter.mModel.empty()) + continue; + + if (Misc::StringUtils::ciEqual(model, "meshes\\" + iter.mModel)) + { + creatureId = !iter.mOriginal.empty() ? iter.mOriginal : iter.mId; + break; + } + } + + if (creatureId.empty()) + return std::string(); + + const MWWorld::Store &store = MWBase::Environment::get().getWorld()->getStore().get(); + + int type = getSndGenTypeFromName(name); + std::vector sounds; + + MWWorld::Store::iterator sound = store.begin(); + + while (sound != store.end()) + { + if (type == sound->mType && !sound->mCreature.empty() && (Misc::StringUtils::ciEqual(creatureId, sound->mCreature))) + sounds.push_back(&*sound); + ++sound; + } + + if (!sounds.empty()) + return sounds[Misc::Rng::rollDice(sounds.size())]->mSound; + + if (type == ESM::SoundGenerator::Land) + return "Body Fall Large"; + + return std::string(); + } + + int Activator::getSndGenTypeFromName(const std::string &name) const + { + if (name == "left") + return 0; + if (name == "right") + return 1; + if (name == "swimleft") + return 2; + if (name == "swimright") + return 3; + if (name == "moan") + return 4; + if (name == "roar") + return 5; + if (name == "scream") + return 6; + if (name == "land") + return 7; + + throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); + } } diff --git a/apps/openmw/mwclass/activator.hpp b/apps/openmw/mwclass/activator.hpp index 3f333f4cbe..0f66b74b45 100644 --- a/apps/openmw/mwclass/activator.hpp +++ b/apps/openmw/mwclass/activator.hpp @@ -44,6 +44,10 @@ namespace MWClass ///< Whether or not to use animated variant of model (default false) virtual bool isActivator() const; + + virtual std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const; + + virtual int getSndGenTypeFromName(const std::string &name) const; }; } From 6ef7be3fd3e50925b26bd8d0cd2cb3b87e1846db Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 14 Oct 2018 20:11:21 +0300 Subject: [PATCH 09/14] Re-enable using soundgen land for creatures --- apps/openmw/mwclass/npc.cpp | 10 ++-------- apps/openmw/mwmechanics/character.cpp | 22 ++++++++++++---------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4339f37e29..d6dafd2a25 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1242,15 +1242,9 @@ namespace MWClass return ""; } + // Morrowind ignores land soundgen for NPCs if(name == "land") - { - MWBase::World *world = MWBase::Environment::get().getWorld(); - osg::Vec3f pos(ptr.getRefData().getPosition().asVec3()); - if (world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr)) - return "DefaultLandWater"; - - return "DefaultLand"; - } + return ""; if(name == "swimleft") return "Swim Left"; if(name == "swimright") diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 7ad8d61cd2..0931aeda12 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -979,17 +979,13 @@ void CharacterController::handleTextKey(const std::string &groupname, const std: } } - if (soundgen == "land") // Morrowind ignores land soundgen for some reason - return; - std::string sound = mPtr.getClass().getSoundIdFromSndGen(mPtr, soundgen); if(!sound.empty()) { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - if(soundgen == "left" || soundgen == "right") + // NB: landing sound is not played for NPCs here + if(soundgen == "left" || soundgen == "right" || soundgen == "land") { - // Don't make foot sounds local for the player, it makes sense to keep them - // positioned on the ground. sndMgr->playSound3D(mPtr, sound, volume, pitch, MWSound::Type::Foot, MWSound::PlayMode::NoPlayerLocal); } @@ -2071,11 +2067,17 @@ void CharacterController::update(float duration) } } - // Play landing sound - MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - std::string sound = cls.getSoundIdFromSndGen(mPtr, "land"); - if (!sound.empty()) + // Play landing sound for NPCs + if (mPtr.getClass().isNpc()) + { + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + std::string sound = "DefaultLand"; + osg::Vec3f pos(mPtr.getRefData().getPosition().asVec3()); + if (world->isUnderwater(mPtr.getCell(), pos) || world->isWalkingOnWater(mPtr)) + sound = "DefaultLandWater"; + sndMgr->playSound3D(mPtr, sound, 1.f, 1.f, MWSound::Type::Foot, MWSound::PlayMode::NoPlayerLocal); + } } else { From bf3f82b9d4c6f259fe50fe12063b6838d688283a Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 14 Oct 2018 20:37:40 +0300 Subject: [PATCH 10/14] Cleanup --- apps/openmw/mwclass/activator.cpp | 43 +++++++++++-------------------- apps/openmw/mwclass/activator.hpp | 4 +-- apps/openmw/mwclass/creature.cpp | 20 +++++++------- 3 files changed, 27 insertions(+), 40 deletions(-) diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 7f53709d6c..e0e2013912 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -138,19 +138,13 @@ namespace MWClass std::string Activator::getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const { - std::string model = getModel(ptr); - if (model.empty()) - return std::string(); - - const MWWorld::Store &creaturestore = MWBase::Environment::get().getWorld()->getStore().get(); + std::string model = getModel(ptr); // Assume it's not empty, since we wouldn't have gotten the soundgen otherwise + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); std::string creatureId; - - for (const ESM::Creature &iter : creaturestore) - { - if (iter.mModel.empty()) - continue; - if (Misc::StringUtils::ciEqual(model, "meshes\\" + iter.mModel)) + for (const ESM::Creature &iter : store.get()) + { + if (!iter.mModel.empty() && Misc::StringUtils::ciEqual(model, "meshes\\" + iter.mModel)) { creatureId = !iter.mOriginal.empty() ? iter.mOriginal : iter.mId; break; @@ -160,19 +154,12 @@ namespace MWClass if (creatureId.empty()) return std::string(); - const MWWorld::Store &store = MWBase::Environment::get().getWorld()->getStore().get(); - int type = getSndGenTypeFromName(name); std::vector sounds; - MWWorld::Store::iterator sound = store.begin(); - - while (sound != store.end()) - { + for (auto sound = store.get().begin(); sound != store.get().end(); ++sound) if (type == sound->mType && !sound->mCreature.empty() && (Misc::StringUtils::ciEqual(creatureId, sound->mCreature))) sounds.push_back(&*sound); - ++sound; - } if (!sounds.empty()) return sounds[Misc::Rng::rollDice(sounds.size())]->mSound; @@ -183,24 +170,24 @@ namespace MWClass return std::string(); } - int Activator::getSndGenTypeFromName(const std::string &name) const + int Activator::getSndGenTypeFromName(const std::string &name) { if (name == "left") - return 0; + return ESM::SoundGenerator::LeftFoot; if (name == "right") - return 1; + return ESM::SoundGenerator::RightFoot; if (name == "swimleft") - return 2; + return ESM::SoundGenerator::SwimLeft; if (name == "swimright") - return 3; + return ESM::SoundGenerator::SwimRight; if (name == "moan") - return 4; + return ESM::SoundGenerator::Moan; if (name == "roar") - return 5; + return ESM::SoundGenerator::Roar; if (name == "scream") - return 6; + return ESM::SoundGenerator::Scream; if (name == "land") - return 7; + return ESM::SoundGenerator::Land; throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); } diff --git a/apps/openmw/mwclass/activator.hpp b/apps/openmw/mwclass/activator.hpp index 0f66b74b45..b92dc75cbd 100644 --- a/apps/openmw/mwclass/activator.hpp +++ b/apps/openmw/mwclass/activator.hpp @@ -10,6 +10,8 @@ namespace MWClass virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; + static int getSndGenTypeFromName(const std::string &name); + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; @@ -46,8 +48,6 @@ namespace MWClass virtual bool isActivator() const; virtual std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const; - - virtual int getSndGenTypeFromName(const std::string &name) const; }; } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 6d0b42bfeb..788e5cd689 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -688,9 +688,9 @@ namespace MWClass MWBase::World *world = MWBase::Environment::get().getWorld(); osg::Vec3f pos(ptr.getRefData().getPosition().asVec3()); if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr)) - return 2; + return ESM::SoundGenerator::SwimLeft; if(world->isOnGround(ptr)) - return 0; + return ESM::SoundGenerator::LeftFoot; return -1; } if(name == "right") @@ -698,23 +698,23 @@ namespace MWClass MWBase::World *world = MWBase::Environment::get().getWorld(); osg::Vec3f pos(ptr.getRefData().getPosition().asVec3()); if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr)) - return 3; + return ESM::SoundGenerator::SwimRight; if(world->isOnGround(ptr)) - return 1; + return ESM::SoundGenerator::RightFoot; return -1; } if(name == "swimleft") - return 2; + return ESM::SoundGenerator::SwimLeft; if(name == "swimright") - return 3; + return ESM::SoundGenerator::SwimRight; if(name == "moan") - return 4; + return ESM::SoundGenerator::Moan; if(name == "roar") - return 5; + return ESM::SoundGenerator::Roar; if(name == "scream") - return 6; + return ESM::SoundGenerator::Scream; if(name == "land") - return 7; + return ESM::SoundGenerator::Land; throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); } From 13bd81f8969808d6bd13fc4d24e375b67c2ab504 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 16 Oct 2018 22:28:19 +0400 Subject: [PATCH 11/14] Try to use collisions from basic actor model if an animated one has no collisions (feature #4682) --- CHANGELOG.md | 1 + apps/openmw/mwphysics/physicssystem.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ddf20fa7f3..cda9150b5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -175,6 +175,7 @@ Feature #4632: AI priority: utilize vanilla AI GMSTs for priority rating Feature #4636: Use sTo GMST in spellmaking menu Feature #4642: Batching potion creation + Feature #4682: Use the collision box from basic creature mesh if the X one have no collisions Task #2490: Don't open command prompt window on Release-mode builds automatically Task #4545: Enable is_pod string test Task #4605: Optimize skinning diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 363f28e70f..2ce498f377 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -1337,6 +1337,16 @@ namespace MWPhysics if (!shape) return; + // Try to get shape from basic model as fallback for creatures + if (!ptr.getClass().isNpc() && shape->mCollisionBoxHalfExtents.length2() == 0) + { + const std::string fallbackModel = ptr.getClass().getModel(ptr); + if (fallbackModel != mesh) + { + shape = mShapeManager->getShape(fallbackModel); + } + } + Actor* actor = new Actor(ptr, shape, mCollisionWorld); mActors.insert(std::make_pair(ptr, actor)); } From d7d9050d4ae558708a4a291a3e0709d14cee452a Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 18 Oct 2018 11:42:03 +0400 Subject: [PATCH 12/14] Force actor to the 'weapon equipped' state if the weapon disappeared in the middle of attack (bug #4646) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/character.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a4d5d34f7..44932f2ffe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -130,6 +130,7 @@ Bug #4633: Sneaking stance affects speed even if the actor is not able to crouch Bug #4641: GetPCJumping is handled incorrectly Bug #4644: %Name should be available for all actors, not just for NPCs + Bug #4646: Weapon force-equipment messes up ongoing attack animations Bug #4648: Hud thinks that throwing weapons have condition Bug #4649: Levelup fully restores health Bug #4653: Length of non-ASCII strings is handled incorrectly in ESM reader diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 0931aeda12..ce844674f4 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1278,6 +1278,18 @@ bool CharacterController::updateWeaponState(CharacterState& idle) bool isStillWeapon = weaptype > WeapType_HandToHand && weaptype < WeapType_Spell && mWeaponType > WeapType_HandToHand && mWeaponType < WeapType_Spell; + // If the current weapon type was changed in the middle of attack (e.g. by Equip console command or when bound spell expires), + // we should force actor to the "weapon equipped" state, interrupt attack and update animations. + if (isStillWeapon && mWeaponType != weaptype && mUpperBodyState > UpperCharState_WeapEquiped) + { + forcestateupdate = true; + mUpperBodyState = UpperCharState_WeapEquiped; + mAttackingOrSpell = false; + mAnimation->disable(mCurrentWeapon); + if (mPtr == getPlayer()) + MWBase::Environment::get().getWorld()->getPlayer().setAttackingOrSpell(false); + } + if(!isKnockedOut() && !isKnockedDown() && !isRecovery()) { std::string weapgroup; From 8fa0ffcfe4e0753651c1435fc1aed0dc735b6cce Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 18 Oct 2018 14:59:39 +0400 Subject: [PATCH 13/14] Catch exceptions inside the loadVoice() (bug #4685) --- CHANGELOG.md | 1 + apps/openmw/mwsound/soundmanagerimp.cpp | 36 +++++++++++++++++-------- apps/openmw/mwsound/soundmanagerimp.hpp | 2 +- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a4d5d34f7..14abe86b74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -142,6 +142,7 @@ Bug #4674: Journal can be opened when settings window is open Bug #4677: Crash in ESM reader when NPC record has DNAM record without DODT one Bug #4678: Crash in ESP parser when SCVR has no variable names + Bug #4685: Missing sound causes an exception inside Say command Feature #912: Editor: Add missing icons to UniversalId tables Feature #1221: Editor: Creature/NPC rendering Feature #1617: Editor: Enchantment effect record verifier diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 8e3488906c..7b722a8352 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -245,20 +245,30 @@ namespace MWSound DecoderPtr SoundManager::loadVoice(const std::string &voicefile) { - DecoderPtr decoder = getDecoder(); - // Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav. - if(mVFS->exists(voicefile)) - decoder->open(voicefile); - else + try + { + DecoderPtr decoder = getDecoder(); + + // Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav. + if(mVFS->exists(voicefile)) + decoder->open(voicefile); + else + { + std::string file = voicefile; + std::string::size_type pos = file.rfind('.'); + if(pos != std::string::npos) + file = file.substr(0, pos)+".mp3"; + decoder->open(file); + } + + return decoder; + } + catch(std::exception &e) { - std::string file = voicefile; - std::string::size_type pos = file.rfind('.'); - if(pos != std::string::npos) - file = file.substr(0, pos)+".mp3"; - decoder->open(file); + Log(Debug::Error) << "Failed to load audio from " << voicefile << ": " << e.what(); } - return decoder; + return nullptr; } Sound *SoundManager::getSoundRef() @@ -471,6 +481,8 @@ namespace MWSound mVFS->normalizeFilename(voicefile); DecoderPtr decoder = loadVoice(voicefile); + if (!decoder) + return; MWBase::World *world = MWBase::Environment::get().getWorld(); const osg::Vec3f pos = world->getActorHeadTransform(ptr).getTrans(); @@ -503,6 +515,8 @@ namespace MWSound mVFS->normalizeFilename(voicefile); DecoderPtr decoder = loadVoice(voicefile); + if (!decoder) + return; stopSay(MWWorld::ConstPtr()); Stream *sound = playVoice(decoder, osg::Vec3f(), true); diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index 4064a05afe..d8a4cfc8cc 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -117,7 +117,7 @@ namespace MWSound Sound_Buffer *lookupSound(const std::string &soundId) const; Sound_Buffer *loadSound(const std::string &soundId); - // returns a decoder to start streaming + // returns a decoder to start streaming, or nullptr if the sound was not found DecoderPtr loadVoice(const std::string &voicefile); Sound *getSoundRef(); From 4ce35c6ad558cd25d9ddf5e1d1f94b8e84c476f0 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 24 Oct 2018 01:40:57 +0300 Subject: [PATCH 14/14] Fix fixme behavior in interiors --- apps/openmw/mwbase/world.hpp | 4 ++-- apps/openmw/mwscript/transformationextensions.cpp | 3 +-- apps/openmw/mwworld/worldimp.cpp | 5 +++-- apps/openmw/mwworld/worldimp.hpp | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 027d1fd102..8da3e61125 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -262,8 +262,8 @@ namespace MWBase ///< Adjust position after load to be on ground. Must be called after model load. /// @param force do this even if the ptr is flying - virtual void fixPosition (const MWWorld::Ptr& actor) = 0; - ///< Attempt to fix position so that the Ptr is no longer inside collision geometry. + virtual void fixPosition () = 0; + ///< Attempt to fix position so that the player is not stuck inside the geometry. /// @note No-op for items in containers. Use ContainerStore::removeItem instead. virtual void deleteObject (const MWWorld::Ptr& ptr) = 0; diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 2695aed764..9f0784d6c0 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -738,8 +738,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { - const MWWorld::Ptr ptr = MWMechanics::getPlayer(); - MWBase::Environment::get().getWorld()->fixPosition(ptr); + MWBase::Environment::get().getWorld()->fixPosition(); } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 0f20fa05a1..84dbe921ec 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1351,8 +1351,9 @@ namespace MWWorld moveObject(ptr, ptr.getCell(), pos.x(), pos.y(), pos.z()); } - void World::fixPosition(const Ptr &actor) + void World::fixPosition() { + const MWWorld::Ptr actor = getPlayerPtr(); const float distance = 128.f; ESM::Position esmPos = actor.getRefData().getPosition(); osg::Quat orientation(esmPos.rot[2], osg::Vec3f(0,0,-1)); @@ -1382,7 +1383,7 @@ namespace MWWorld esmPos.pos[0] = traced.x(); esmPos.pos[1] = traced.y(); esmPos.pos[2] = traced.z(); - MWWorld::ActionTeleport("", esmPos, false).execute(actor); + MWWorld::ActionTeleport(actor.getCell()->isExterior() ? "" : actor.getCell()->getCell()->mName, esmPos, false).execute(actor); } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 1592453a20..3a42a7ee0a 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -291,8 +291,8 @@ namespace MWWorld ///< Adjust position after load to be on ground. Must be called after model load. /// @param force do this even if the ptr is flying - void fixPosition (const Ptr& actor) override; - ///< Attempt to fix position so that the Ptr is no longer inside collision geometry. + void fixPosition () override; + ///< Attempt to fix position so that the player is not stuck inside the geometry. void enable (const Ptr& ptr) override;