From b3cd10dbeaeebb27cc5af27dda5bfac47fb46dee Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 15:18:25 +0100 Subject: [PATCH 01/18] Remove redundant setTeleported calls --- apps/openmw/mwmechanics/spellcasting.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 633771b39..c9750cd4c 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -250,14 +250,10 @@ namespace MWMechanics if (effectId == ESM::MagicEffect::DivineIntervention) { - MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); - MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "divinemarker", worldPos); } else if (effectId == ESM::MagicEffect::AlmsiviIntervention) { - MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); - MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "templemarker", worldPos); } From 69ba8a40bf0f6fe8642a8c2ac2510e88c2f5e60d Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 16:13:20 +0100 Subject: [PATCH 02/18] Fix weird formatting added during the merge --- apps/openmw/mwmechanics/spellcasting.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index c9750cd4c..7db8f5367 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -250,7 +250,7 @@ namespace MWMechanics if (effectId == ESM::MagicEffect::DivineIntervention) { - MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "divinemarker", worldPos); + MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "divinemarker", worldPos); } else if (effectId == ESM::MagicEffect::AlmsiviIntervention) { From 8e5cae1081ca926c5f7157f07457c0a4046eed54 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 17:06:21 +0100 Subject: [PATCH 03/18] Implement mark/recall magic effects --- apps/openmw/mwmechanics/spellcasting.cpp | 15 ++++++++++++--- apps/openmw/mwworld/player.cpp | 16 +++++++++++++++- apps/openmw/mwworld/player.hpp | 8 ++++++++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 7db8f5367..0b897c165 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -8,6 +8,7 @@ #include "../mwworld/containerstore.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/actionteleport.hpp" #include "../mwrender/animation.hpp" @@ -259,13 +260,21 @@ namespace MWMechanics else if (effectId == ESM::MagicEffect::Mark) { - // TODO + MWBase::Environment::get().getWorld()->getPlayer().markPosition( + target.getCell(), target.getRefData().getPosition()); } else if (effectId == ESM::MagicEffect::Recall) { - MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); + MWWorld::CellStore* markedCell = NULL; + ESM::Position markedPosition; - // TODO + MWBase::Environment::get().getWorld()->getPlayer().getMarkedPosition(markedCell, markedPosition); + if (markedCell) + { + MWWorld::ActionTeleport action(markedCell->isExterior() ? "" : markedCell->mCell->mName, + markedPosition); + action.execute(target); + } } } } diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 8be6a4d98..c59445402 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -21,7 +21,8 @@ namespace MWWorld mLastKnownExteriorPosition(0,0,0), mAutoMove(false), mForwardBackward (0), - mTeleported(false) + mTeleported(false), + mMarkedCell(NULL) { mPlayer.mBase = player; mPlayer.mRef.mRefID = "player"; @@ -157,4 +158,17 @@ namespace MWWorld { mTeleported = teleported; } + + void Player::markPosition(CellStore *markedCell, ESM::Position markedPosition) + { + mMarkedCell = markedCell; + mMarkedPosition = markedPosition; + } + + void Player::getMarkedPosition(CellStore*& markedCell, ESM::Position &markedPosition) const + { + markedCell = mMarkedCell; + if (mMarkedCell) + markedPosition = mMarkedPosition; + } } diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 48f5d06c1..1df848111 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -32,6 +32,10 @@ namespace MWWorld Ogre::Vector3 mLastKnownExteriorPosition; + ESM::Position mMarkedPosition; + // If no position was marked, this is NULL + CellStore* mMarkedCell; + bool mAutoMove; int mForwardBackward; bool mTeleported; @@ -39,6 +43,10 @@ namespace MWWorld Player(const ESM::NPC *player, const MWBase::World& world); + // For mark/recall magic effects + void markPosition (CellStore* markedCell, ESM::Position markedPosition); + void getMarkedPosition (CellStore*& markedCell, ESM::Position& markedPosition) const; + /// Interiors can not always be mapped to a world position. However /// world position is still required for divine / almsivi magic effects /// and the player arrow on the global map. From eab7ffd6b408995594896d7d33142240faa4dc49 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 18:05:28 +0100 Subject: [PATCH 04/18] Remove redundant finding of default exterior position height --- apps/openmw/mwworld/worldimp.cpp | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index db58a1280..5d7e9deeb 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1940,17 +1940,8 @@ namespace MWWorld int y = ext->getGridY(); indexToPosition(x, y, pos.pos[0], pos.pos[1], true); - ESM::Land* land = getStore().get().search(x, y); - if (land) { - if (!land->isDataLoaded(ESM::Land::DATA_VHGT)) { - land->loadData(ESM::Land::DATA_VHGT); - } - pos.pos[2] = land->mLandData->mHeights[ESM::Land::LAND_NUM_VERTS / 2 + 1]; - } - else { - std::cerr << "Land data for cell at (" << x << ", " << y << ") not found\n"; - pos.pos[2] = 0; - } + // Note: Z pos will be adjusted by adjustPosition later + pos.pos[2] = 0; return true; } From bfc9fcf2cdd5e405c08e3e1d475dfcf54b0bb978 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 21:20:37 +0100 Subject: [PATCH 05/18] Add starting spells when building player (F_PCStart flag) --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index c084c86e5..7740240b6 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -12,6 +12,8 @@ #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" +#include "spellcasting.hpp" + namespace MWMechanics { void MechanicsManager::buildPlayer() @@ -123,6 +125,19 @@ namespace MWMechanics npcStats.getSkill (index).setBase ( npcStats.getSkill (index).getBase() + bonus); } + + if (i==1) + { + // Major skill - add starting spells for this skill if existing + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + MWWorld::Store::iterator it = store.get().begin(); + for (; it != store.get().end(); ++it) + { + if (it->mData.mFlags & ESM::Spell::F_PCStart + && spellSchoolToSkill(getSpellSchool(&*it, ptr)) == index) + creatureStats.getSpells().add(it->mId); + } + } } } From 9245faf2aa882846bd62f72eb0d0e3fc3bacec66 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 22:19:02 +0100 Subject: [PATCH 06/18] Don't destroyRenderTarget with a NULL window --- libs/openengine/ogre/renderer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 07bc8f3c9..9e5ec5414 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -25,7 +25,8 @@ void OgreRenderer::cleanup() delete mFader; mFader = NULL; - Ogre::Root::getSingleton().destroyRenderTarget(mWindow); + if (mWindow) + Ogre::Root::getSingleton().destroyRenderTarget(mWindow); mWindow = NULL; delete mOgreInit; From 57296722624b58837858424caa1b74975a2d9644 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 22:37:52 +0100 Subject: [PATCH 07/18] Show marked position on map. Implement Detect X magic effects. --- apps/openmw/mwbase/world.hpp | 14 +- apps/openmw/mwclass/misc.cpp | 7 + apps/openmw/mwclass/misc.hpp | 2 + apps/openmw/mwgui/mapwindow.cpp | 254 +++++++++++++++++++-------- apps/openmw/mwgui/mapwindow.hpp | 7 + apps/openmw/mwrender/localmap.cpp | 100 +++++------ apps/openmw/mwworld/cellfunctors.hpp | 8 +- apps/openmw/mwworld/cellstore.hpp | 2 +- apps/openmw/mwworld/class.hpp | 2 + apps/openmw/mwworld/worldimp.cpp | 93 +++++++++- apps/openmw/mwworld/worldimp.hpp | 8 +- 11 files changed, 357 insertions(+), 140 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index d9640f4d2..3a8897114 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -130,7 +130,7 @@ namespace MWBase virtual Ogre::Vector2 getNorthVector (MWWorld::CellStore* cell) = 0; ///< get north vector (OGRE coordinates) for given interior cell - virtual std::vector getDoorMarkers (MWWorld::CellStore* cell) = 0; + virtual void getDoorMarkers (MWWorld::CellStore* cell, std::vector& out) = 0; ///< get a list of teleport door markers for a given cell, to be displayed on the local map virtual void getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y) = 0; @@ -438,6 +438,18 @@ namespace MWBase /// @note id must be lower case virtual void teleportToClosestMarker (const MWWorld::Ptr& ptr, const std::string& id, Ogre::Vector3 worldPos) = 0; + + enum DetectionType + { + Detect_Enchantment, + Detect_Key, + Detect_Creature + }; + /// List all references (filtered by \a type) detected by \a ptr. The range + /// is determined by the current magnitude of the "Detect X" magic effect belonging to \a type. + /// @note This also works for references in containers. + virtual void listDetectedReferences (const MWWorld::Ptr& ptr, std::vector& out, + DetectionType type) = 0; }; } diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 1a40c4555..5e216fed4 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -252,4 +252,11 @@ namespace MWClass return ref->mBase->mData.mWeight; } + bool Miscellaneous::isKey(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mIsKey; + } + } diff --git a/apps/openmw/mwclass/misc.hpp b/apps/openmw/mwclass/misc.hpp index 16a8e8c05..16e9ca10b 100644 --- a/apps/openmw/mwclass/misc.hpp +++ b/apps/openmw/mwclass/misc.hpp @@ -57,6 +57,8 @@ namespace MWClass virtual float getWeight (const MWWorld::Ptr& ptr) const; virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; + + virtual bool isKey (const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 00380aefd..2f34df236 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -103,27 +103,80 @@ namespace MWGui void LocalMapBase::onMarkerFocused (MyGUI::Widget* w1, MyGUI::Widget* w2) { + // Workaround to not make the marker visible if it's under fog of war applyFogOfWar (); } void LocalMapBase::onMarkerUnfocused (MyGUI::Widget* w1, MyGUI::Widget* w2) { + // Workaround to not make the marker visible if it's under fog of war applyFogOfWar (); } + MyGUI::IntPoint LocalMapBase::getMarkerPosition(float worldX, float worldY, MarkerPosition& markerPos) + { + MyGUI::IntPoint widgetPos; + // normalized cell coordinates + float nX,nY; + + markerPos.interior = mInterior; + + if (!mInterior) + { + int cellX, cellY; + MWBase::Environment::get().getWorld()->positionToIndex(worldX, worldY, cellX, cellY); + const int cellSize = 8192; + nX = (worldX - cellSize * cellX) / cellSize; + // Image space is -Y up, cells are Y up + nY = 1 - (worldY - cellSize * cellY) / cellSize; + + float cellDx = cellX - mCurX; + float cellDy = cellY - mCurY; + + markerPos.cellX = cellX; + markerPos.cellY = cellY; + + widgetPos = MyGUI::IntPoint(nX * 512 + (1+cellDx) * 512, + nY * 512 - (cellDy-1) * 512); + } + else + { + int cellX, cellY; + Ogre::Vector2 worldPos (worldX, worldY); + MWBase::Environment::get().getWorld ()->getInteriorMapPosition (worldPos, nX, nY, cellX, cellY); + + markerPos.cellX = cellX; + markerPos.cellY = cellY; + + widgetPos = MyGUI::IntPoint(nX * 512 + (1+cellX-mCurX) * 512, + nY * 512 + (1+cellY-mCurY) * 512); + } + + markerPos.nX = nX; + markerPos.nY = nY; + return widgetPos; + } + void LocalMapBase::setActiveCell(const int x, const int y, bool interior) { - if (x==mCurX && y==mCurY && mInterior==interior && !mChanged) return; // don't do anything if we're still in the same cell + if (x==mCurX && y==mCurY && mInterior==interior && !mChanged) + return; // don't do anything if we're still in the same cell + + mCurX = x; + mCurY = y; + mInterior = interior; + mChanged = false; // clear all previous markers for (unsigned int i=0; i< mLocalMap->getChildCount(); ++i) { - if (mLocalMap->getChildAt(i)->getName ().substr (0, 6) == "Marker") + if (mLocalMap->getChildAt(i)->getName ().substr (0, 4) == "Door") { MyGUI::Gui::getInstance ().destroyWidget (mLocalMap->getChildAt(i)); } } + // Update the map textures for (int mx=0; mx<3; ++mx) { for (int my=0; my<3; ++my) @@ -138,78 +191,57 @@ namespace MWGui box->setImageTexture(image); else box->setImageTexture("black.png"); + } + } + MWBase::World* world = MWBase::Environment::get().getWorld(); - // door markers - - // interior map only consists of one cell, so handle the markers only once - if (interior && (mx != 2 || my != 2)) - continue; - - MWWorld::CellStore* cell; - if (interior) - cell = MWBase::Environment::get().getWorld ()->getInterior (mPrefix); - else - cell = MWBase::Environment::get().getWorld ()->getExterior (x+mx-1, y-(my-1)); - - std::vector doors = MWBase::Environment::get().getWorld ()->getDoorMarkers (cell); - - for (std::vector::iterator it = doors.begin(); it != doors.end(); ++it) + // Retrieve the door markers we want to show + std::vector doors; + if (interior) + { + MWWorld::CellStore* cell = world->getInterior (mPrefix); + world->getDoorMarkers(cell, doors); + } + else + { + for (int dX=-1; dX<2; ++dX) + { + for (int dY=-1; dY<2; ++dY) { - MWBase::World::DoorMarker marker = *it; - - // convert world coordinates to normalized cell coordinates - MyGUI::IntCoord widgetCoord; - float nX,nY; - int cellDx, cellDy; - if (!interior) - { - const int cellSize = 8192; - - nX = (marker.x - cellSize * (x+mx-1)) / cellSize; - nY = 1 - (marker.y - cellSize * (y-(my-1))) / cellSize; - - widgetCoord = MyGUI::IntCoord(nX * 512 - 4 + mx * 512, nY * 512 - 4 + my * 512, 8, 8); - } - else - { - Ogre::Vector2 position (marker.x, marker.y); - MWBase::Environment::get().getWorld ()->getInteriorMapPosition (position, nX, nY, cellDx, cellDy); - - widgetCoord = MyGUI::IntCoord(nX * 512 - 4 + (1+cellDx-x) * 512, nY * 512 - 4 + (1+cellDy-y) * 512, 8, 8); - } - - static int counter = 0; - ++counter; - MyGUI::Button* markerWidget = mLocalMap->createWidget("ButtonImage", - widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast(counter)); - markerWidget->setImageResource("DoorMarker"); - markerWidget->setUserString("ToolTipType", "Layout"); - markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); - markerWidget->setUserString("Caption_TextOneLine", marker.name); - markerWidget->setUserString("IsMarker", "true"); - markerWidget->eventMouseSetFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerFocused); - markerWidget->eventMouseLostFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerUnfocused); - - MarkerPosition markerPos; - markerPos.interior = interior; - markerPos.cellX = interior ? cellDx : x + mx - 1; - markerPos.cellY = interior ? cellDy : y + ((my - 1)*-1); - markerPos.nX = nX; - markerPos.nY = nY; - - markerWidget->setUserData(markerPos); + MWWorld::CellStore* cell = world->getExterior (mCurX+dX, mCurY+dY); + world->getDoorMarkers(cell, doors); } - - } } - mInterior = interior; - mCurX = x; - mCurY = y; - mChanged = false; - // fog of war + // Create a widget for each marker + int counter = 0; + for (std::vector::iterator it = doors.begin(); it != doors.end(); ++it) + { + MWBase::World::DoorMarker marker = *it; + + MarkerPosition markerPos; + MyGUI::IntPoint widgetPos = getMarkerPosition(marker.x, marker.y, markerPos); + MyGUI::IntCoord widgetCoord(widgetPos.left - 4, + widgetPos.top - 4, + 8, 8); + ++counter; + MyGUI::Button* markerWidget = mLocalMap->createWidget("ButtonImage", + widgetCoord, MyGUI::Align::Default, "Door" + boost::lexical_cast(counter)); + markerWidget->setImageResource("DoorMarker"); + markerWidget->setUserString("ToolTipType", "Layout"); + markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); + markerWidget->setUserString("Caption_TextOneLine", marker.name); + markerWidget->eventMouseSetFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerFocused); + markerWidget->eventMouseLostFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerUnfocused); + // Used by tooltips to not show the tooltip if marker is hidden by fog of war + markerWidget->setUserString("IsMarker", "true"); + markerWidget->setUserData(markerPos); + } + + updateMarkers(); + applyFogOfWar(); // set the compass texture again, because MyGUI determines sorting of ImageBox widgets @@ -222,6 +254,8 @@ namespace MWGui void LocalMapBase::setPlayerPos(const float x, const float y) { + updateMarkers(); + if (x == mLastPositionX && y == mLastPositionY) return; @@ -255,6 +289,88 @@ namespace MWGui mLastDirectionY = y; } + void LocalMapBase::addDetectionMarkers(int type) + { + std::vector markers; + MWBase::World* world = MWBase::Environment::get().getWorld(); + world->listDetectedReferences( + world->getPlayer().getPlayer(), + markers, MWBase::World::DetectionType(type)); + if (markers.empty()) + return; + + std::string markerTexture; + MyGUI::Colour markerColour; + if (type == MWBase::World::Detect_Creature) + { + markerTexture = "textures\\menu_map_dcreature.dds"; + markerColour = MyGUI::Colour(1,0,0,1); + } + if (type == MWBase::World::Detect_Key) + { + markerTexture = "textures\\menu_map_dkey.dds"; + markerColour = MyGUI::Colour(0,1,0,1); + } + if (type == MWBase::World::Detect_Enchantment) + { + markerTexture = "textures\\menu_map_dmagic.dds"; + markerColour = MyGUI::Colour(0,0,1,1); + } + + int counter = 0; + for (std::vector::iterator it = markers.begin(); it != markers.end(); ++it) + { + const ESM::Position& worldPos = it->getRefData().getPosition(); + MarkerPosition markerPos; + MyGUI::IntPoint widgetPos = getMarkerPosition(worldPos.pos[0], worldPos.pos[1], markerPos); + MyGUI::IntCoord widgetCoord(widgetPos.left - 4, + widgetPos.top - 4, + 8, 8); + ++counter; + MyGUI::ImageBox* markerWidget = mLocalMap->createWidget("ImageBox", + widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast(counter)); + markerWidget->setImageTexture(markerTexture); + markerWidget->setUserString("IsMarker", "true"); + markerWidget->setUserData(markerPos); + markerWidget->setColour(markerColour); + } + } + + void LocalMapBase::updateMarkers() + { + // clear all previous markers + for (unsigned int i=0; i< mLocalMap->getChildCount(); ++i) + { + if (mLocalMap->getChildAt(i)->getName ().substr (0, 6) == "Marker") + { + MyGUI::Gui::getInstance ().destroyWidget (mLocalMap->getChildAt(i)); + } + } + + addDetectionMarkers(MWBase::World::Detect_Creature); + addDetectionMarkers(MWBase::World::Detect_Key); + addDetectionMarkers(MWBase::World::Detect_Enchantment); + + // Add marker for the spot marked with Mark magic effect + MWWorld::CellStore* markedCell = NULL; + ESM::Position markedPosition; + MWBase::Environment::get().getWorld()->getPlayer().getMarkedPosition(markedCell, markedPosition); + if (markedCell && markedCell->isExterior() == !mInterior + && (!mInterior || Misc::StringUtils::ciEqual(markedCell->mCell->mName, mPrefix))) + { + MarkerPosition markerPos; + MyGUI::IntPoint widgetPos = getMarkerPosition(markedPosition.pos[0], markedPosition.pos[1], markerPos); + MyGUI::IntCoord widgetCoord(widgetPos.left - 4, + widgetPos.top - 4, + 8, 8); + MyGUI::ImageBox* markerWidget = mLocalMap->createWidget("ImageBox", + widgetCoord, MyGUI::Align::Default, "MarkerMarked"); + markerWidget->setImageTexture("textures\\menu_map_smark.dds"); + markerWidget->setUserString("IsMarker", "true"); + markerWidget->setUserData(markerPos); + } + } + // ------------------------------------------------------------------------------------------ MapWindow::MapWindow(const std::string& cacheDir) @@ -319,7 +435,7 @@ namespace MWGui static int _counter=0; MyGUI::Button* markerWidget = mGlobalMapImage->createWidget("ButtonImage", - widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast(_counter)); + widgetCoord, MyGUI::Align::Default, "Door" + boost::lexical_cast(_counter)); markerWidget->setImageResource("DoorMarker"); markerWidget->setUserString("ToolTipType", "Layout"); markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); @@ -385,7 +501,7 @@ namespace MWGui for (unsigned int i=0; igetChildCount (); ++i) { - if (mGlobalMapImage->getChildAt (i)->getName().substr(0,6) == "Marker") + if (mGlobalMapImage->getChildAt (i)->getName().substr(0,4) == "Door") mGlobalMapImage->getChildAt (i)->castType()->setImageResource("DoorMarker"); } diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 2c90c422e..7df2105dc 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -55,9 +55,16 @@ namespace MWGui void onMarkerFocused(MyGUI::Widget* w1, MyGUI::Widget* w2); void onMarkerUnfocused(MyGUI::Widget* w1, MyGUI::Widget* w2); + MyGUI::IntPoint getMarkerPosition (float worldX, float worldY, MarkerPosition& markerPos); + virtual void notifyPlayerUpdate() {} virtual void notifyMapChanged() {} + // Update markers (Detect X effects, Mark/Recall effects) + // Note, door markers handled in setActiveCell + void updateMarkers(); + void addDetectionMarkers(int type); + OEngine::GUI::Layout* mLayout; bool mMapDragAndDrop; diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 5f4128978..3ea3380e8 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -225,64 +225,54 @@ void LocalMap::render(const float x, const float y, tex = TextureManager::getSingleton().getByName(texture); if (tex.isNull()) { - // try loading from disk - //if (boost::filesystem::exists(texture+".jpg")) - //{ - /// \todo - //} - //else + // render + tex = TextureManager::getSingleton().createManual( + texture, + ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + TEX_TYPE_2D, + xw*sMapResolution/sSize, yw*sMapResolution/sSize, + 0, + PF_R8G8B8, + TU_RENDERTARGET); + + RenderTarget* rtt = tex->getBuffer()->getRenderTarget(); + + rtt->setAutoUpdated(false); + Viewport* vp = rtt->addViewport(mCellCamera); + vp->setOverlaysEnabled(false); + vp->setShadowsEnabled(false); + vp->setBackgroundColour(ColourValue(0, 0, 0)); + vp->setVisibilityMask(RV_Map); + vp->setMaterialScheme("local_map"); + + rtt->update(); + + // create "fog of war" texture + TexturePtr tex2 = TextureManager::getSingleton().createManual( + texture + "_fog", + ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + TEX_TYPE_2D, + xw*sFogOfWarResolution/sSize, yw*sFogOfWarResolution/sSize, + 0, + PF_A8R8G8B8, + TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); + + // create a buffer to use for dynamic operations + std::vector buffer; + buffer.resize(sFogOfWarResolution*sFogOfWarResolution); + + // initialize to (0, 0, 0, 1) + for (int p=0; pgetBuffer()->getRenderTarget(); - - rtt->setAutoUpdated(false); - Viewport* vp = rtt->addViewport(mCellCamera); - vp->setOverlaysEnabled(false); - vp->setShadowsEnabled(false); - vp->setBackgroundColour(ColourValue(0, 0, 0)); - vp->setVisibilityMask(RV_Map); - vp->setMaterialScheme("local_map"); - - rtt->update(); - - // create "fog of war" texture - TexturePtr tex2 = TextureManager::getSingleton().createManual( - texture + "_fog", - ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - TEX_TYPE_2D, - xw*sFogOfWarResolution/sSize, yw*sFogOfWarResolution/sSize, - 0, - PF_A8R8G8B8, - TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); - - // create a buffer to use for dynamic operations - std::vector buffer; - buffer.resize(sFogOfWarResolution*sFogOfWarResolution); - - // initialize to (0, 0, 0, 1) - for (int p=0; pgetBuffer()->lock(HardwareBuffer::HBL_DISCARD), &buffer[0], sFogOfWarResolution*sFogOfWarResolution*4); - tex2->getBuffer()->unlock(); + buffer[p] = (255 << 24); + } - mBuffers[texture] = buffer; + memcpy(tex2->getBuffer()->lock(HardwareBuffer::HBL_DISCARD), &buffer[0], sFogOfWarResolution*sFogOfWarResolution*4); + tex2->getBuffer()->unlock(); - // save to cache for next time - //rtt->writeContentsToFile("./" + texture + ".jpg"); - } + mBuffers[texture] = buffer; } + mRenderingManager->enableLights(true); mLight->setVisible(false); @@ -339,8 +329,6 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni Vector3 playerdirection = mCameraRotNode->convertWorldToLocalOrientation(orientation).yAxis(); - Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().y); - if (!mInterior) { x = std::ceil(pos.x / sSize)-1; diff --git a/apps/openmw/mwworld/cellfunctors.hpp b/apps/openmw/mwworld/cellfunctors.hpp index 4b1f70096..5115fa02d 100644 --- a/apps/openmw/mwworld/cellfunctors.hpp +++ b/apps/openmw/mwworld/cellfunctors.hpp @@ -4,7 +4,7 @@ #include #include -#include "refdata.hpp" +#include "ptr.hpp" namespace ESM { @@ -18,13 +18,13 @@ namespace MWWorld { std::vector mHandles; - bool operator() (ESM::CellRef& ref, RefData& data) + bool operator() (MWWorld::Ptr ptr) { - Ogre::SceneNode* handle = data.getBaseNode(); + Ogre::SceneNode* handle = ptr.getRefData().getBaseNode(); if (handle) mHandles.push_back (handle); - data.setBaseNode(0); + ptr.getRefData().setBaseNode(0); return true; } }; diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 8a01caf18..8731c42dc 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -143,7 +143,7 @@ namespace MWWorld { if (!iter->mData.getCount()) continue; - if (!functor (iter->mRef, iter->mData)) + if (!functor (MWWorld::Ptr(&*iter, this))) return false; } return true; diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index cb6690a46..d737c18a2 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -290,6 +290,8 @@ namespace MWWorld virtual bool isPersistent (const MWWorld::Ptr& ptr) const; + virtual bool isKey (const MWWorld::Ptr& ptr) const { return false; } + virtual Ptr copyToCell(const Ptr &ptr, CellStore &cell) const; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 5d7e9deeb..ff2ae7161 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1442,10 +1442,8 @@ namespace MWWorld return d; } - std::vector World::getDoorMarkers (CellStore* cell) + void World::getDoorMarkers (CellStore* cell, std::vector& out) { - std::vector result; - MWWorld::CellRefList& doors = cell->mDoors; CellRefList::List& refList = doors.mList; for (CellRefList::List::iterator it = refList.begin(); it != refList.end(); ++it) @@ -1461,11 +1459,9 @@ namespace MWWorld newMarker.x = pos.pos[0]; newMarker.y = pos.pos[1]; - result.push_back(newMarker); + out.push_back(newMarker); } } - - return result; } void World::getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y) @@ -1829,9 +1825,9 @@ namespace MWWorld { std::vector mHandles; - bool operator() (ESM::CellRef& ref, RefData& data) + bool operator() (Ptr ptr) { - Ogre::SceneNode* handle = data.getBaseNode(); + Ogre::SceneNode* handle = ptr.getRefData().getBaseNode(); if (handle) mHandles.push_back(handle->getName()); return true; @@ -2348,4 +2344,85 @@ namespace MWWorld mWeatherManager->update(duration); } + + struct AddDetectedReference + { + AddDetectedReference(std::vector& out, Ptr detector, World::DetectionType type, float squaredDist) + : mOut(out), mDetector(detector), mType(type), mSquaredDist(squaredDist) + { + } + + std::vector& mOut; + Ptr mDetector; + float mSquaredDist; + World::DetectionType mType; + bool operator() (MWWorld::Ptr ptr) + { + if (Ogre::Vector3(ptr.getRefData().getPosition().pos).squaredDistance( + Ogre::Vector3(mDetector.getRefData().getPosition().pos)) >= mSquaredDist) + return true; + + if (!ptr.getRefData().isEnabled()) + return true; + + // Consider references inside containers as well + if (ptr.getClass().isActor() || ptr.getClass().getTypeName() == typeid(ESM::Container).name()) + { + MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr); + { + for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) + { + if (needToAdd(*it)) + { + mOut.push_back(ptr); + return true; + } + } + } + } + + if (needToAdd(ptr)) + mOut.push_back(ptr); + + return true; + } + + bool needToAdd (MWWorld::Ptr ptr) + { + if (mType == World::Detect_Creature && ptr.getClass().getTypeName() != typeid(ESM::Creature).name()) + return false; + if (mType == World::Detect_Key && !ptr.getClass().isKey(ptr)) + return false; + if (mType == World::Detect_Enchantment && ptr.getClass().getEnchantment(ptr).empty()) + return false; + return true; + } + }; + + void World::listDetectedReferences(const Ptr &ptr, std::vector &out, DetectionType type) + { + const MWMechanics::MagicEffects& effects = ptr.getClass().getCreatureStats(ptr).getMagicEffects(); + float dist=0; + if (type == World::Detect_Creature) + dist = effects.get(MWMechanics::EffectKey(ESM::MagicEffect::DetectAnimal)).mMagnitude; + else if (type == World::Detect_Key) + dist = effects.get(MWMechanics::EffectKey(ESM::MagicEffect::DetectKey)).mMagnitude; + else if (type == World::Detect_Enchantment) + dist = effects.get(MWMechanics::EffectKey(ESM::MagicEffect::DetectEnchantment)).mMagnitude; + + if (!dist) + return; + + // TODO: "1 foot" = 20 game units? + dist *= 20; + + AddDetectedReference functor (out, ptr, type, dist*dist); + + const Scene::CellStoreCollection& active = mWorldScene->getActiveCells(); + for (Scene::CellStoreCollection::const_iterator it = active.begin(); it != active.end(); ++it) + { + MWWorld::CellStore* cellStore = *it; + cellStore->forEach(functor); + } + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index def8b2fa4..7e3b0befe 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -202,7 +202,7 @@ namespace MWWorld virtual Ogre::Vector2 getNorthVector (CellStore* cell); ///< get north vector (OGRE coordinates) for given interior cell - virtual std::vector getDoorMarkers (MWWorld::CellStore* cell); + virtual void getDoorMarkers (MWWorld::CellStore* cell, std::vector& out); ///< get a list of teleport door markers for a given cell, to be displayed on the local map virtual void getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y); @@ -526,6 +526,12 @@ namespace MWWorld /// @note id must be lower case virtual void teleportToClosestMarker (const MWWorld::Ptr& ptr, const std::string& id, Ogre::Vector3 worldPos); + + /// List all references (filtered by \a type) detected by \a ptr. The range + /// is determined by the current magnitude of the "Detect X" magic effect belonging to \a type. + /// @note This also works for references in containers. + virtual void listDetectedReferences (const MWWorld::Ptr& ptr, std::vector& out, + DetectionType type); }; } From 531bef6193c1e875cb5e5861e758c3c57a251576 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 22:46:10 +0100 Subject: [PATCH 08/18] Shorter Vector3 initialisation --- apps/openmw/mwsound/soundmanagerimp.cpp | 6 +++--- apps/openmw/mwworld/worldimp.cpp | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 372be8393..2e52739ac 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -252,7 +252,7 @@ namespace MWSound float basevol = volumeFromType(Play_TypeVoice); std::string filePath = "Sound/"+filename; const ESM::Position &pos = ptr.getRefData().getPosition(); - const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); + const Ogre::Vector3 objpos(pos.pos); MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, 1.0f, basevol, 1.0f, 20.0f, 12750.0f, Play_Normal|Play_TypeVoice, 0); @@ -354,7 +354,7 @@ namespace MWSound float min, max; std::string file = lookup(soundId, volume, min, max); const ESM::Position &pos = ptr.getRefData().getPosition(); - const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); + const Ogre::Vector3 objpos(pos.pos); sound = mOutput->playSound3D(file, objpos, volume, basevol, pitch, min, max, mode|type, offset); if((mode&Play_NoTrack)) @@ -584,7 +584,7 @@ namespace MWSound if(!ptr.isEmpty()) { const ESM::Position &pos = ptr.getRefData().getPosition(); - const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); + const Ogre::Vector3 objpos(pos.pos); snditer->first->setPosition(objpos); } //update fade out diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ff2ae7161..e6c095baa 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1063,7 +1063,7 @@ namespace MWWorld void World::adjustPosition(const Ptr &ptr) { - Ogre::Vector3 pos (ptr.getRefData().getPosition().pos[0], ptr.getRefData().getPosition().pos[1], ptr.getRefData().getPosition().pos[2]); + Ogre::Vector3 pos (ptr.getRefData().getPosition().pos); if(!ptr.getRefData().getBaseNode()) { @@ -1304,7 +1304,7 @@ namespace MWWorld if (mPlayer->getPlayer().getCell()->isExterior()) { ESM::Position pos = mPlayer->getPlayer().getRefData().getPosition(); - mPlayer->setLastKnownExteriorPosition(Ogre::Vector3(pos.pos[0], pos.pos[1], pos.pos[2])); + mPlayer->setLastKnownExteriorPosition(Ogre::Vector3(pos.pos)); } } @@ -1584,7 +1584,7 @@ namespace MWWorld pos.rot[1] = 0; Ogre::Vector3 orig = - Ogre::Vector3(pos.pos[0], pos.pos[1], pos.pos[2]); + Ogre::Vector3(pos.pos); Ogre::Vector3 dir = Ogre::Vector3(0, 0, -1); float len = (pos.pos[2] >= 0) ? pos.pos[2] : -pos.pos[2]; @@ -2299,7 +2299,7 @@ namespace MWWorld if (ref.mRef.mTeleport && ref.mRef.mDestCell.empty()) { ESM::Position pos = ref.mRef.mDoorDest; - result = Ogre::Vector3(pos.pos[0], pos.pos[1], pos.pos[2]); + result = Ogre::Vector3(pos.pos); return true; } } @@ -2320,7 +2320,7 @@ namespace MWWorld for (std::vector::iterator it = markers.begin(); it != markers.end(); ++it) { ESM::Position pos = it->getRefData().getPosition(); - Ogre::Vector3 markerPos = Ogre::Vector3(pos.pos[0], pos.pos[1], pos.pos[2]); + Ogre::Vector3 markerPos = Ogre::Vector3(pos.pos); float distance = worldPos.squaredDistance(markerPos); if (distance < closestDistance) { From f6387d5979a9d1571f61f4b217aff09c6b7ab09a Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 23:30:58 +0100 Subject: [PATCH 09/18] Implement Telekinesis magic effect. Remove some duplicate code. --- apps/openmw/mwworld/worldimp.cpp | 39 +++++++++----------------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e6c095baa..d2e8a21b7 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -796,32 +796,9 @@ namespace MWWorld MWWorld::Ptr World::getFacedObject() { - std::pair result; - - if (!mRendering->occlusionQuerySupported()) - result = mPhysics->getFacedHandle (getMaxActivationDistance ()); - else - result = std::make_pair (mFacedDistance, mFacedHandle); - - if (result.second.empty()) - return MWWorld::Ptr (); - - MWWorld::Ptr object = searchPtrViaHandle (result.second); - if (object.isEmpty()) - return object; - float ActivationDistance; - - if (MWBase::Environment::get().getWindowManager()->isConsoleMode()) - ActivationDistance = getObjectActivationDistance ()*50; - else if (object.getTypeName ().find("NPC") != std::string::npos) - ActivationDistance = getNpcActivationDistance (); - else - ActivationDistance = getObjectActivationDistance (); - - if (result.first > ActivationDistance) - return MWWorld::Ptr (); - - return object; + if (mFacedHandle.empty()) + return MWWorld::Ptr(); + return searchPtrViaHandle(mFacedHandle); } std::pair World::getHitContact(const MWWorld::Ptr &ptr, float distance) @@ -1346,6 +1323,12 @@ namespace MWWorld void World::updateFacedHandle () { + float telekinesisRangeBonus = + mPlayer->getPlayer().getClass().getCreatureStats(mPlayer->getPlayer()).getMagicEffects() + .get(ESM::MagicEffect::Telekinesis).mMagnitude * 22; + + float activationDistance = getMaxActivationDistance() + telekinesisRangeBonus; + // send new query // figure out which object we want to test against std::vector < std::pair < float, std::string > > results; @@ -1353,13 +1336,13 @@ namespace MWWorld { float x, y; MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); - results = mPhysics->getFacedHandles(x, y, getMaxActivationDistance ()); + results = mPhysics->getFacedHandles(x, y, activationDistance); if (MWBase::Environment::get().getWindowManager()->isConsoleMode()) results = mPhysics->getFacedHandles(x, y, getMaxActivationDistance ()*50); } else { - results = mPhysics->getFacedHandles(getMaxActivationDistance ()); + results = mPhysics->getFacedHandles(activationDistance); } // ignore the player and other things we're not interested in From 24aa743573464fabaf20db7b15c3f7c6bf18bbfe Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 23:34:18 +0100 Subject: [PATCH 10/18] Add function for converting feet to game units --- apps/openmw/mwworld/worldimp.cpp | 13 ++++++++++--- apps/openmw/mwworld/worldimp.hpp | 2 ++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index d2e8a21b7..c11de7753 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1325,7 +1325,8 @@ namespace MWWorld { float telekinesisRangeBonus = mPlayer->getPlayer().getClass().getCreatureStats(mPlayer->getPlayer()).getMagicEffects() - .get(ESM::MagicEffect::Telekinesis).mMagnitude * 22; + .get(ESM::MagicEffect::Telekinesis).mMagnitude; + telekinesisRangeBonus = feetToGameUnits(telekinesisRangeBonus); float activationDistance = getMaxActivationDistance() + telekinesisRangeBonus; @@ -2396,8 +2397,7 @@ namespace MWWorld if (!dist) return; - // TODO: "1 foot" = 20 game units? - dist *= 20; + dist = feetToGameUnits(dist); AddDetectedReference functor (out, ptr, type, dist*dist); @@ -2408,4 +2408,11 @@ namespace MWWorld cellStore->forEach(functor); } } + + float World::feetToGameUnits(float feet) + { + // Looks like there is no GMST for this. This factor was determined in experiments + // with the Telekinesis effect. + return feet * 22; + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 7e3b0befe..1aecb6fb6 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -155,6 +155,8 @@ namespace MWWorld /// Called when \a object is moved to an inactive cell void objectLeftActiveCell (MWWorld::Ptr object, MWWorld::Ptr movedPtr); + float feetToGameUnits(float feet); + public: World (OEngine::Render::OgreRenderer& renderer, From c6421276bdc5dc2a25b9f45de5283f4f3f688ced Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 23:59:17 +0100 Subject: [PATCH 11/18] Closes #841: Correct activation distance in third person mode --- apps/openmw/mwrender/camera.cpp | 5 +++++ apps/openmw/mwrender/camera.hpp | 2 ++ apps/openmw/mwrender/renderingmanager.cpp | 5 +++++ apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/worldimp.cpp | 1 + 5 files changed, 14 insertions(+) diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 8f54be3f8..b2ec9884d 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -278,6 +278,11 @@ namespace MWRender } } + float Camera::getCameraDistance() const + { + return mCamera->getPosition().z; + } + void Camera::setCameraDistance(float dist, bool adjust, bool override) { if(mFirstPersonView && !mPreviewMode && !mVanity.enabled) diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index 87e486629..d31d9e56c 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -105,6 +105,8 @@ namespace MWRender /// Restore default camera distance for current mode. void setCameraDistance(); + float getCameraDistance() const; + void setAnimation(NpcAnimation *anim); /// Stores focal and camera world positions in passed arguments diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index b739a9f95..f6e69b726 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1026,4 +1026,9 @@ void RenderingManager::enableTerrain(bool enable) mTerrain->setVisible(false); } +float RenderingManager::getCameraDistance() const +{ + return mCamera->getCameraDistance(); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index abc8fd71a..b13e546e8 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -90,6 +90,7 @@ public: bool vanityRotateCamera(const float *rot); void setCameraDistance(float dist, bool adjust = false, bool override = true); + float getCameraDistance() const; void setupPlayer(const MWWorld::Ptr &ptr); void renderPlayer(const MWWorld::Ptr &ptr); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c11de7753..715270780 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1329,6 +1329,7 @@ namespace MWWorld telekinesisRangeBonus = feetToGameUnits(telekinesisRangeBonus); float activationDistance = getMaxActivationDistance() + telekinesisRangeBonus; + activationDistance += mRendering->getCameraDistance(); // send new query // figure out which object we want to test against From 899214a906a7330b92c9fb6ddf3b7bee50143820 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 00:13:23 +0100 Subject: [PATCH 12/18] Use fVanityDelay --- apps/openmw/mwinput/inputmanagerimp.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index ffb2af81e..1ad409515 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -831,9 +831,11 @@ namespace MWInput void InputManager::updateIdleTime(float dt) { + static const float vanityDelay = MWBase::Environment::get().getWorld()->getStore().get() + .find("fVanityDelay")->getFloat(); if (mTimeIdle >= 0.f) mTimeIdle += dt; - if (mTimeIdle > 30.f) { + if (mTimeIdle > vanityDelay) { MWBase::Environment::get().getWorld()->toggleVanityMode(true); mTimeIdle = -1.f; } From 590c8cb4a0c6bc1dc55f88c20a29c8f9453f757b Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 01:03:44 +0100 Subject: [PATCH 13/18] Implement Disintegrate effects. When an armor/weapon breaks, unequip it and do not allow equipping it again. --- apps/openmw/mwclass/armor.cpp | 3 ++ apps/openmw/mwclass/npc.cpp | 10 +++++ apps/openmw/mwclass/weapon.cpp | 3 ++ apps/openmw/mwmechanics/actors.cpp | 65 ++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+) diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index c7c80ec2e..0fae1b05c 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -291,6 +291,9 @@ namespace MWClass { MWWorld::InventoryStore& invStore = MWWorld::Class::get(npc).getInventoryStore(npc); + if (ptr.getCellRef().mCharge == 0) + return std::make_pair(0, "#{sInventoryMessage1}"); + // slots that this item can be equipped in std::pair, bool> slots_ = MWWorld::Class::get(ptr).getEquipmentSlots(ptr); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4187aa8c1..2ba0566dd 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -493,6 +493,11 @@ namespace MWClass if (!MWBase::Environment::get().getWorld()->getGodModeState()) weapon.getCellRef().mCharge -= std::min(std::max(1, (int)(damage * gmst.find("fWeaponDamageMult")->getFloat())), weapon.getCellRef().mCharge); + + // Weapon broken? unequip it + if (weapon.getCellRef().mCharge == 0) + weapon = *inv.unequipItem(weapon, ptr); + } healthdmg = true; } @@ -644,6 +649,11 @@ namespace MWClass armorref.mCharge = armor.get()->mBase->mData.mHealth; armorref.mCharge -= std::min(std::max(1, (int)damagediff), armorref.mCharge); + + // Armor broken? unequip it + if (armorref.mCharge == 0) + inv.unequipItem(armor, ptr); + switch(get(armor).getEquipmentSkill(armor)) { case ESM::Skill::LightArmor: diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 7b0a5eb47..5e93e0d81 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -388,6 +388,9 @@ namespace MWClass std::pair Weapon::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const { + if (ptr.getCellRef().mCharge == 0) + return std::make_pair(0, "#{sInventoryMessage1}"); + std::pair, bool> slots_ = MWWorld::Class::get(ptr).getEquipmentSlots(ptr); if (slots_.first.empty()) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 62242ee8c..13b5da11a 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -46,6 +46,44 @@ void adjustBoundItem (const std::string& item, bool bound, const MWWorld::Ptr& a } } +bool disintegrateSlot (MWWorld::Ptr ptr, int slot, float disintegrate) +{ + // TODO: remove this check once creatures support inventory store + if (ptr.getTypeName() == typeid(ESM::NPC).name()) + { + MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr); + MWWorld::ContainerStoreIterator item = + inv.getSlot(slot); + if (item != inv.end()) + { + if (!item->getClass().hasItemHealth(*item)) + return false; + if (item->getCellRef().mCharge == -1) + item->getCellRef().mCharge = item->getClass().getItemMaxHealth(*item); + + if (item->getCellRef().mCharge == 0) + return false; + + item->getCellRef().mCharge -= + std::min(disintegrate, + static_cast(item->getCellRef().mCharge)); + + if (item->getCellRef().mCharge == 0) + { + // Will unequip the broken item and try to find a replacement + if (ptr.getRefData().getHandle() != "player") + inv.autoEquip(ptr); + else + inv.unequipItem(*item, ptr); + } + + return true; + } + } + return true; +} + + } namespace MWMechanics @@ -241,6 +279,33 @@ namespace MWMechanics creatureStats.setDynamic(i, stat); } + // Apply disintegration (reduces item health) + float disintegrateWeapon = effects.get(EffectKey(ESM::MagicEffect::DisintegrateWeapon)).mMagnitude; + if (disintegrateWeapon > 0) + disintegrateSlot(ptr, MWWorld::InventoryStore::Slot_CarriedRight, disintegrateWeapon*duration); + float disintegrateArmor = effects.get(EffectKey(ESM::MagicEffect::DisintegrateArmor)).mMagnitude; + if (disintegrateArmor > 0) + { + // According to UESP + int priorities[] = { + MWWorld::InventoryStore::Slot_CarriedLeft, + MWWorld::InventoryStore::Slot_Cuirass, + MWWorld::InventoryStore::Slot_LeftPauldron, + MWWorld::InventoryStore::Slot_RightPauldron, + MWWorld::InventoryStore::Slot_LeftGauntlet, + MWWorld::InventoryStore::Slot_RightGauntlet, + MWWorld::InventoryStore::Slot_Helmet, + MWWorld::InventoryStore::Slot_Greaves, + MWWorld::InventoryStore::Slot_Boots + }; + + for (unsigned int i=0; i Date: Thu, 2 Jan 2014 01:31:06 +0100 Subject: [PATCH 14/18] Some checks to prevent bound item abuse --- apps/openmw/mwgui/inventorywindow.cpp | 8 ++++++++ apps/openmw/mwgui/pickpocketitemmodel.cpp | 8 ++++++++ apps/openmw/mwgui/tradeitemmodel.cpp | 7 +++++++ apps/openmw/mwmechanics/actors.cpp | 5 +++++ components/esm/cellref.hpp | 4 ++-- 5 files changed, 30 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index ffd81e98b..c14971f6e 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -163,6 +163,14 @@ namespace MWGui MWWorld::Ptr object = item.mBase; int count = item.mCount; + // Bound items may not be moved + if (item.mBase.getCellRef().mRefID.size() > 6 + && item.mBase.getCellRef().mRefID.substr(0,6) == "bound_") + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sBarterDialog12}"); + return; + } + if (item.mType == ItemStack::Type_Equipped) { MWWorld::InventoryStore& invStore = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); diff --git a/apps/openmw/mwgui/pickpocketitemmodel.cpp b/apps/openmw/mwgui/pickpocketitemmodel.cpp index 16be5f6cc..13ee4396d 100644 --- a/apps/openmw/mwgui/pickpocketitemmodel.cpp +++ b/apps/openmw/mwgui/pickpocketitemmodel.cpp @@ -40,6 +40,14 @@ namespace MWGui for (size_t i = 0; igetItemCount(); ++i) { const ItemStack& item = mSourceModel->getItem(i); + + // Bound items may not be stolen + if (item.mBase.getCellRef().mRefID.size() > 6 + && item.mBase.getCellRef().mRefID.substr(0,6) == "bound_") + { + continue; + } + if (std::find(mHiddenItems.begin(), mHiddenItems.end(), item) == mHiddenItems.end() && item.mType != ItemStack::Type_Equipped) mItems.push_back(item); diff --git a/apps/openmw/mwgui/tradeitemmodel.cpp b/apps/openmw/mwgui/tradeitemmodel.cpp index e836355d3..5c12843da 100644 --- a/apps/openmw/mwgui/tradeitemmodel.cpp +++ b/apps/openmw/mwgui/tradeitemmodel.cpp @@ -154,6 +154,13 @@ namespace MWGui if(!MWWorld::Class::get(base).canSell(base, services)) continue; + // Bound items may not be bought + if (item.mBase.getCellRef().mRefID.size() > 6 + && item.mBase.getCellRef().mRefID.substr(0,6) == "bound_") + { + continue; + } + // don't show equipped items if(mMerchant.getTypeName() == typeid(ESM::NPC).name()) { diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 13b5da11a..840167e3a 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -695,6 +695,11 @@ namespace MWMechanics iter->second->kill(); + // Reset magic effects and recalculate derived effects + // One case where we need this is to make sure bound items are removed upon death + stats.setMagicEffects(MWMechanics::MagicEffects()); + calculateCreatureStatModifiers(iter->first, 0); + ++mDeathCount[cls.getId(iter->first)]; if(cls.isEssential(iter->first)) diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 47cb0b99e..5f1066cf8 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -20,7 +20,7 @@ namespace ESM public: int mRefnum; // Reference number - std::string mRefID; // ID of object being referenced + std::string mRefID; // ID of object being referenced (must be lowercase) float mScale; // Scale applied to mesh @@ -89,4 +89,4 @@ namespace ESM }; } -#endif \ No newline at end of file +#endif From 5b0a4c94245cb632801e3798effc0d3998ff8202 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 01:42:30 +0100 Subject: [PATCH 15/18] Get rid of unused file and some other cruft. --- CMakeLists.txt | 2 - libs/openengine/bullet/CMotionState.cpp | 46 ---------------------- libs/openengine/bullet/CMotionState.h | 52 ------------------------- libs/openengine/bullet/physic.cpp | 10 +---- libs/openengine/bullet/physic.hpp | 17 -------- 5 files changed, 2 insertions(+), 125 deletions(-) delete mode 100644 libs/openengine/bullet/CMotionState.cpp delete mode 100644 libs/openengine/bullet/CMotionState.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ee4a0246b..64f8121c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,8 +99,6 @@ set(OENGINE_BULLET ${LIBDIR}/openengine/bullet/BtOgreExtras.h ${LIBDIR}/openengine/bullet/BtOgreGP.h ${LIBDIR}/openengine/bullet/BtOgrePG.h - ${LIBDIR}/openengine/bullet/CMotionState.cpp - ${LIBDIR}/openengine/bullet/CMotionState.h ${LIBDIR}/openengine/bullet/physic.cpp ${LIBDIR}/openengine/bullet/physic.hpp ${LIBDIR}/openengine/bullet/BulletShapeLoader.cpp diff --git a/libs/openengine/bullet/CMotionState.cpp b/libs/openengine/bullet/CMotionState.cpp deleted file mode 100644 index c20415884..000000000 --- a/libs/openengine/bullet/CMotionState.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "CMotionState.h" -#include "physic.hpp" - -#include -#include -#include - -namespace OEngine { -namespace Physic -{ - - CMotionState::CMotionState(PhysicEngine* eng,std::string name) - : isPC(false) - , isNPC(true) - { - pEng = eng; - tr.setIdentity(); - pName = name; - } - - void CMotionState::getWorldTransform(btTransform &worldTrans) const - { - worldTrans = tr; - } - - void CMotionState::setWorldTransform(const btTransform &worldTrans) - { - tr = worldTrans; - - PhysicEvent evt; - evt.isNPC = isNPC; - evt.isPC = isPC; - evt.newTransform = tr; - evt.RigidBodyName = pName; - - if(isPC) - { - pEng->PEventList.push_back(evt); - } - else - { - pEng->NPEventList.push_back(evt); - } - } - -}} diff --git a/libs/openengine/bullet/CMotionState.h b/libs/openengine/bullet/CMotionState.h deleted file mode 100644 index 3508ab4ef..000000000 --- a/libs/openengine/bullet/CMotionState.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef OENGINE_CMOTIONSTATE_H -#define OENGINE_CMOTIONSTATE_H - -#include -#include - -namespace OEngine { -namespace Physic -{ - class PhysicEngine; - - /** - * A CMotionState is associated with a single RigidBody. - * When the RigidBody is moved by bullet, bullet will call the function setWorldTransform. - * for more info, see the bullet Wiki at btMotionState. - */ - class CMotionState:public btMotionState - { - public: - - CMotionState(PhysicEngine* eng,std::string name); - - /** - * Return the position of the RigidBody. - */ - virtual void getWorldTransform(btTransform &worldTrans) const; - - /** - * Function called by bullet when the RigidBody is moved. - * It add an event to the EventList of the PhysicEngine class. - */ - virtual void setWorldTransform(const btTransform &worldTrans); - - protected: - PhysicEngine* pEng; - btTransform tr; - bool isNPC; - bool isPC; - - std::string pName; - }; - - struct PhysicEvent - { - bool isNPC; - bool isPC; - btTransform newTransform; - std::string RigidBodyName; - }; - -}} -#endif diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index e33edda18..4e80088bf 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -3,7 +3,6 @@ #include #include #include -#include "CMotionState.h" #include "OgreRoot.h" #include "btKinematicCharacterController.h" #include "BtOgrePG.h" @@ -318,9 +317,7 @@ namespace Physic btVector3 scl(triSize, triSize, 1); hfShape->setLocalScaling(scl); - CMotionState* newMotionState = new CMotionState(this,name); - - btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo(0,newMotionState,hfShape); + btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo(0,0,hfShape); RigidBody* body = new RigidBody(CI,name); body->getWorldTransform().setOrigin(btVector3( (x+0.5)*triSize*(sqrtVerts-1), (y+0.5)*triSize*(sqrtVerts-1), (maxh+minh)/2.f)); @@ -401,12 +398,9 @@ namespace Physic else shape->mRaycastingShape->setLocalScaling( btVector3(scale,scale,scale)); - //create the motionState - CMotionState* newMotionState = new CMotionState(this,name); - //create the real body btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo - (0,newMotionState, raycasting ? shape->mRaycastingShape : shape->mCollisionShape); + (0,0, raycasting ? shape->mRaycastingShape : shape->mCollisionShape); RigidBody* body = new RigidBody(CI,name); body->mPlaceable = placeable; diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index f28f95ccb..6cd7244b8 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -38,7 +38,6 @@ namespace MWWorld namespace OEngine { namespace Physic { - class CMotionState; struct PhysicEvent; class PhysicEngine; class RigidBody; @@ -157,17 +156,7 @@ namespace Physic private: void disableCollisionBody(); void enableCollisionBody(); -public: -//HACK: in Visual Studio 2010 and presumably above, this structures alignment -// must be 16, but the built in operator new & delete don't properly -// perform this alignment. -#if _MSC_VER >= 1600 - void * operator new (size_t Size) { return _aligned_malloc (Size, 16); } - void operator delete (void * Data) { _aligned_free (Data); } -#endif - - private: OEngine::Physic::RigidBody* mBody; OEngine::Physic::RigidBody* mRaycastingBody; @@ -329,12 +318,6 @@ public: const btVector3 &origin, btCollisionObject *object); - //event list of non player object - std::list NPEventList; - - //event list affecting the player - std::list PEventList; - //Bullet Stuff btOverlappingPairCache* pairCache; btBroadphaseInterface* broadphase; From 0f2b2fabdb35125e85ec648ad334b6bae40a81fa Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 02:03:11 +0100 Subject: [PATCH 16/18] Implement water walking --- apps/openmw/mwworld/physicssystem.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 451e093ae..2a7f5948e 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -577,10 +577,28 @@ namespace MWWorld float oldHeight = iter->first.getRefData().getPosition().pos[2]; + bool waterCollision = false; + if (iter->first.getClass().getCreatureStats(iter->first).getMagicEffects() + .get(ESM::MagicEffect::WaterWalking).mMagnitude + && cell->hasWater() + && !world->isUnderwater(iter->first.getCell(), + Ogre::Vector3(iter->first.getRefData().getPosition().pos))) + waterCollision = true; + + btStaticPlaneShape planeShape(btVector3(0,0,1), waterlevel); + btCollisionObject object; + object.setCollisionShape(&planeShape); + + if (waterCollision) + mEngine->dynamicsWorld->addCollisionObject(&object); + Ogre::Vector3 newpos = MovementSolver::move(iter->first, iter->second, mTimeAccum, world->isFlying(iter->first), waterlevel, mEngine); + if (waterCollision) + mEngine->dynamicsWorld->removeCollisionObject(&object); + float heightDiff = newpos.z - oldHeight; if (heightDiff < 0) From 993edf03844cbf5b614371b031daf55db57b9550 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 02:27:48 +0100 Subject: [PATCH 17/18] Bug #1063: Safety check in moveObject. Required when items in containers execute script commands like setAtStart etc. --- apps/openmw/mwworld/worldimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 715270780..d02e1022e 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -911,7 +911,7 @@ namespace MWWorld ptr.getRefData().setCount(0); } } - if (haveToMove) + if (haveToMove && ptr.getRefData().getBaseNode()) { mRendering->moveObject(ptr, vec); mPhysics->moveObject (ptr); From c558e12212ace15ec20097b1e365a9bdfcc052aa Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 03:06:48 +0100 Subject: [PATCH 18/18] Don't try to move objects that are not in a cell --- .../mwscript/transformationextensions.cpp | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 47a632ae9..e44180977 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -207,6 +207,10 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); + + if (!ptr.isInCell()) + return; + if (ptr.getRefData().getHandle() == "player") { MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); @@ -275,6 +279,10 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); + + if (!ptr.isInCell()) + return; + if (ptr.getRefData().getHandle() == "player") { MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); @@ -336,6 +344,10 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); + + if (!ptr.isInCell()) + return; + if (ptr.getRefData().getHandle() == "player") { MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); @@ -605,6 +617,10 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); + + if (!ptr.isInCell()) + return; + ptr.getRefData().getLocalRotation().rot[0] = 0; ptr.getRefData().getLocalRotation().rot[1] = 0; ptr.getRefData().getLocalRotation().rot[2] = 0; @@ -624,6 +640,9 @@ namespace MWScript { const MWWorld::Ptr& ptr = R()(runtime); + if (!ptr.isInCell()) + return; + std::string axis = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Float movement = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration()); @@ -659,6 +678,9 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); + if (!ptr.isInCell()) + return; + std::string axis = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Float movement = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration());