From ea3b88951a79e5bb8593ca98ec53a8a6c1fb0930 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 02:22:11 +0100 Subject: [PATCH] Implement divine/almsivi intervention magic effects --- apps/openmw/mwbase/world.hpp | 6 +++++ apps/openmw/mwmechanics/spellcasting.cpp | 19 +++++++++++--- apps/openmw/mwworld/cells.cpp | 16 +++++++++--- apps/openmw/mwworld/cells.hpp | 7 +++++ apps/openmw/mwworld/worldimp.cpp | 33 ++++++++++++++++++++++-- apps/openmw/mwworld/worldimp.hpp | 6 +++++ 6 files changed, 78 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 44b45badff..d9640f4d22 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -432,6 +432,12 @@ namespace MWBase virtual bool isDark() const = 0; virtual bool findInteriorPositionInWorldSpace(MWWorld::CellStore* cell, Ogre::Vector3& result) = 0; + + /// Teleports \a ptr to the reference of \a id (e.g. DivineMarker, PrisonMarker, TempleMarker) + /// closest to \a worldPos. + /// @note id must be lower case + virtual void teleportToClosestMarker (const MWWorld::Ptr& ptr, + const std::string& id, Ogre::Vector3 worldPos) = 0; }; } diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 025aa6b3a9..ee907284d9 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -7,6 +7,7 @@ #include "../mwworld/containerstore.hpp" +#include "../mwworld/player.hpp" #include "../mwrender/animation.hpp" @@ -238,14 +239,24 @@ namespace MWMechanics else if (effectId == ESM::MagicEffect::RemoveCurse) target.getClass().getCreatureStats(target).getSpells().purgeCurses(); - else if (effectId == ESM::MagicEffect::DivineIntervention) + if (target.getRefData().getHandle() != "player") + return; + if (!MWBase::Environment::get().getWorld()->isTeleportingEnabled()) + return; + + Ogre::Vector3 worldPos; + if (!MWBase::Environment::get().getWorld()->findInteriorPositionInWorldSpace(target.getCell(), worldPos)) + worldPos = MWBase::Environment::get().getWorld()->getPlayer().getLastKnownExteriorPosition(); + + if (effectId == ESM::MagicEffect::DivineIntervention) { - // We need to be able to get the world location of an interior cell before implementing this - // or alternatively, the last known exterior location of the player, which is how vanilla does it. + MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "divinemarker", + worldPos); } else if (effectId == ESM::MagicEffect::AlmsiviIntervention) { - // Same as above + MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "templemarker", + worldPos); } else if (effectId == ESM::MagicEffect::Mark) diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 37c4b6a3f4..865c0d01fc 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -129,9 +129,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, Ptr::CellStore& ce if (cell.mState==Ptr::CellStore::State_Preloaded) { - std::string lowerCase = Misc::StringUtils::lowerCase(name); - - if (std::binary_search (cell.mIds.begin(), cell.mIds.end(), lowerCase)) + if (std::binary_search (cell.mIds.begin(), cell.mIds.end(), name)) { cell.load (mStore, mReader); } @@ -261,3 +259,15 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name) // giving up return Ptr(); } + +void MWWorld::Cells::getExteriorPtrs(const std::string &name, std::vector &out) +{ + for (std::map, Ptr::CellStore>::iterator iter = mExteriors.begin(); + iter!=mExteriors.end(); ++iter) + { + Ptr ptr = getPtrAndCache (name, iter->second); + if (!ptr.isEmpty()) + out.push_back(ptr); + } + +} diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index 0c51cf4520..31de2f60e8 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -47,8 +47,15 @@ namespace MWWorld Ptr getPtr (const std::string& name, CellStore& cellStore, bool searchInContainers = false); ///< \param searchInContainers Only affect loaded cells. + /// @note name must be lower case + /// @note name must be lower case Ptr getPtr (const std::string& name); + + /// Get all Ptrs referencing \a name in exterior cells + /// @note Due to the current implementation of getPtr this only supports one Ptr per cell. + /// @note name must be lower case + void getExteriorPtrs (const std::string& name, std::vector& out); }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 670c0387f1..fe2d2e64e6 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -39,6 +39,7 @@ #include "cellfunctors.hpp" #include "containerstore.hpp" #include "inventorystore.hpp" +#include "actionteleport.hpp" #include "contentloader.hpp" #include "esmloader.hpp" @@ -498,12 +499,14 @@ namespace MWWorld if (!ptr.isEmpty()) return ptr; + std::string lowerCaseName = Misc::StringUtils::lowerCase(name); + // active cells for (Scene::CellStoreCollection::const_iterator iter (mWorldScene->getActiveCells().begin()); iter!=mWorldScene->getActiveCells().end(); ++iter) { Ptr::CellStore* cellstore = *iter; - Ptr ptr = mCells.getPtr (name, *cellstore, true); + Ptr ptr = mCells.getPtr (lowerCaseName, *cellstore, true); if (!ptr.isEmpty()) return ptr; @@ -511,7 +514,7 @@ namespace MWWorld if (!activeOnly) { - Ptr ptr = mCells.getPtr (name); + Ptr ptr = mCells.getPtr (lowerCaseName); if (!ptr.isEmpty()) return ptr; @@ -2317,4 +2320,30 @@ namespace MWWorld // No luck :( return false; } + + void World::teleportToClosestMarker (const MWWorld::Ptr& ptr, + const std::string& id, Ogre::Vector3 worldPos) + { + MWWorld::Ptr closestMarker; + float closestDistance = FLT_MAX; + + std::vector markers; + mCells.getExteriorPtrs(id, markers); + + 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]); + float distance = worldPos.squaredDistance(markerPos); + if (distance < closestDistance) + { + closestDistance = distance; + closestMarker = *it; + } + + } + + MWWorld::ActionTeleport action("", closestMarker.getRefData().getPosition()); + action.execute(ptr); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index fad683d18c..2ec5e42f0d 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -520,6 +520,12 @@ namespace MWWorld virtual bool isDark() const; virtual bool findInteriorPositionInWorldSpace(MWWorld::CellStore* cell, Ogre::Vector3& result); + + /// Teleports \a ptr to the reference of \a id (e.g. DivineMarker, PrisonMarker, TempleMarker) + /// closest to \a worldPos. + /// @note id must be lower case + virtual void teleportToClosestMarker (const MWWorld::Ptr& ptr, + const std::string& id, Ogre::Vector3 worldPos); }; }