diff --git a/CHANGELOG.md b/CHANGELOG.md index 56e778c0f8..611a7b625e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ------ Bug #1952: Incorrect particle lighting + Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs Bug #3676: NiParticleColorModifier isn't applied properly Bug #5358: ForceGreeting always resets the dialogue window completely Bug #5363: Enchantment autocalc not always 0/1 diff --git a/apps/openmw/mwbase/scriptmanager.hpp b/apps/openmw/mwbase/scriptmanager.hpp index 7bdeba132c..da3454d348 100644 --- a/apps/openmw/mwbase/scriptmanager.hpp +++ b/apps/openmw/mwbase/scriptmanager.hpp @@ -35,7 +35,7 @@ namespace MWBase virtual ~ScriptManager() {} - virtual void run (const std::string& name, Interpreter::Context& interpreterContext) = 0; + virtual bool run (const std::string& name, Interpreter::Context& interpreterContext) = 0; ///< Run the script with the given name (compile first, if not compiled yet) virtual bool compile (const std::string& name) = 0; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 31d4a1b3c1..84f9b49844 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -189,6 +189,8 @@ namespace MWBase virtual MWWorld::Ptr searchPtrViaActorId (int actorId) = 0; ///< Search is limited to the active cells. + virtual MWWorld::Ptr searchPtrViaRefNum (const std::string& id, const ESM::RefNum& refNum) = 0; + virtual MWWorld::Ptr findContainer (const MWWorld::ConstPtr& ptr) = 0; ///< Return a pointer to a liveCellRef which contains \a ptr. /// \note Search is limited to the active cells. diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 786cce072b..5dad9d6ece 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -5,83 +5,171 @@ #include #include +#include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwbase/scriptmanager.hpp" +#include "../mwmechanics/creaturestats.hpp" + #include "interpretercontext.hpp" +namespace +{ + struct ScriptCreatingVisitor : public boost::static_visitor + { + ESM::GlobalScript operator()(const MWWorld::Ptr &ptr) const + { + ESM::GlobalScript script; + script.mTargetRef.unset(); + if (!ptr.isEmpty()) + { + if (ptr.getCellRef().hasContentFile()) + { + script.mTargetId = ptr.getCellRef().getRefId(); + script.mTargetRef = ptr.getCellRef().getRefNum(); + } + else if (MWBase::Environment::get().getWorld()->getPlayerPtr() == ptr) + script.mTargetId = ptr.getCellRef().getRefId(); + } + return script; + } + + ESM::GlobalScript operator()(const std::pair &pair) const + { + ESM::GlobalScript script; + script.mTargetId = pair.second; + script.mTargetRef = pair.first; + return script; + } + }; + + struct PtrGettingVisitor : public boost::static_visitor + { + const MWWorld::Ptr* operator()(const MWWorld::Ptr &ptr) const + { + return &ptr; + } + + const MWWorld::Ptr* operator()(const std::pair &pair) const + { + return nullptr; + } + }; + + struct PtrResolvingVisitor : public boost::static_visitor + { + MWWorld::Ptr operator()(const MWWorld::Ptr &ptr) const + { + return ptr; + } + + MWWorld::Ptr operator()(const std::pair &pair) const + { + if (pair.second.empty()) + return MWWorld::Ptr(); + else if(pair.first.hasContentFile()) + return MWBase::Environment::get().getWorld()->searchPtrViaRefNum(pair.second, pair.first); + return MWBase::Environment::get().getWorld()->searchPtr(pair.second, false); + } + }; + + class MatchPtrVisitor : public boost::static_visitor + { + const MWWorld::Ptr& mPtr; + public: + MatchPtrVisitor(const MWWorld::Ptr& ptr) : mPtr(ptr) {} + + bool operator()(const MWWorld::Ptr &ptr) const + { + return ptr == mPtr; + } + + bool operator()(const std::pair &pair) const + { + return false; + } + }; +} + namespace MWScript { GlobalScriptDesc::GlobalScriptDesc() : mRunning (false) {} + const MWWorld::Ptr* GlobalScriptDesc::getPtrIfPresent() const + { + return boost::apply_visitor(PtrGettingVisitor(), mTarget); + } + + MWWorld::Ptr GlobalScriptDesc::getPtr() + { + MWWorld::Ptr ptr = boost::apply_visitor(PtrResolvingVisitor(), mTarget); + mTarget = ptr; + return ptr; + } + GlobalScripts::GlobalScripts (const MWWorld::ESMStore& store) : mStore (store) {} - void GlobalScripts::addScript (const std::string& name, const std::string& targetId) + void GlobalScripts::addScript (const std::string& name, const MWWorld::Ptr& target) { - std::map::iterator iter = - mScripts.find (::Misc::StringUtils::lowerCase (name)); + const auto iter = mScripts.find (::Misc::StringUtils::lowerCase (name)); if (iter==mScripts.end()) { if (const ESM::Script *script = mStore.get().search(name)) { - GlobalScriptDesc desc; - desc.mRunning = true; - desc.mLocals.configure (*script); - desc.mId = targetId; - - mScripts.insert (std::make_pair (name, desc)); + auto desc = std::make_shared(); + MWWorld::Ptr ptr = target; + desc->mTarget = ptr; + desc->mRunning = true; + desc->mLocals.configure (*script); + mScripts.insert (std::make_pair(name, desc)); } else { Log(Debug::Error) << "Failed to add global script " << name << ": script record not found"; } } - else if (!iter->second.mRunning) + else if (!iter->second->mRunning) { - iter->second.mRunning = true; - iter->second.mId = targetId; + iter->second->mRunning = true; + MWWorld::Ptr ptr = target; + iter->second->mTarget = ptr; } } void GlobalScripts::removeScript (const std::string& name) { - std::map::iterator iter = - mScripts.find (::Misc::StringUtils::lowerCase (name)); + const auto iter = mScripts.find (::Misc::StringUtils::lowerCase (name)); if (iter!=mScripts.end()) - iter->second.mRunning = false; + iter->second->mRunning = false; } bool GlobalScripts::isRunning (const std::string& name) const { - std::map::const_iterator iter = - mScripts.find (::Misc::StringUtils::lowerCase (name)); + const auto iter = mScripts.find (::Misc::StringUtils::lowerCase (name)); if (iter==mScripts.end()) return false; - return iter->second.mRunning; + return iter->second->mRunning; } void GlobalScripts::run() { - for (std::map::iterator iter (mScripts.begin()); - iter!=mScripts.end(); ++iter) + for (const auto& script : mScripts) { - if (iter->second.mRunning) + if (script.second->mRunning) { - MWWorld::Ptr ptr; - - MWScript::InterpreterContext interpreterContext ( - &iter->second.mLocals, MWWorld::Ptr(), iter->second.mId); - - MWBase::Environment::get().getScriptManager()->run (iter->first, interpreterContext); + MWScript::InterpreterContext context(script.second); + if (!MWBase::Environment::get().getScriptManager()->run(script.first, context)) + script.second->mRunning = false; } } } @@ -129,18 +217,15 @@ namespace MWScript void GlobalScripts::write (ESM::ESMWriter& writer, Loading::Listener& progress) const { - for (std::map::const_iterator iter (mScripts.begin()); - iter!=mScripts.end(); ++iter) + for (const auto& iter : mScripts) { - ESM::GlobalScript script; + ESM::GlobalScript script = boost::apply_visitor (ScriptCreatingVisitor(), iter.second->mTarget); - script.mId = iter->first; + script.mId = iter.first; - iter->second.mLocals.write (script.mLocals, iter->first); + iter.second->mLocals.write (script.mLocals, iter.first); - script.mRunning = iter->second.mRunning ? 1 : 0; - - script.mTargetId = iter->second.mId; + script.mRunning = iter.second->mRunning ? 1 : 0; writer.startRecord (ESM::REC_GSCR); script.save (writer); @@ -155,8 +240,7 @@ namespace MWScript ESM::GlobalScript script; script.load (reader); - std::map::iterator iter = - mScripts.find (script.mId); + auto iter = mScripts.find (script.mId); if (iter==mScripts.end()) { @@ -164,8 +248,12 @@ namespace MWScript { try { - GlobalScriptDesc desc; - desc.mLocals.configure (*scriptRecord); + auto desc = std::make_shared(); + if (!script.mTargetId.empty()) + { + desc->mTarget = std::make_pair(script.mTargetRef, script.mTargetId); + } + desc->mLocals.configure (*scriptRecord); iter = mScripts.insert (std::make_pair (script.mId, desc)).first; } @@ -182,9 +270,8 @@ namespace MWScript return true; } - iter->second.mRunning = script.mRunning!=0; - iter->second.mLocals.read (script.mLocals, script.mId); - iter->second.mId = script.mTargetId; + iter->second->mRunning = script.mRunning!=0; + iter->second->mLocals.read (script.mLocals, script.mId); return true; } @@ -195,18 +282,28 @@ namespace MWScript Locals& GlobalScripts::getLocals (const std::string& name) { std::string name2 = ::Misc::StringUtils::lowerCase (name); - std::map::iterator iter = mScripts.find (name2); + auto iter = mScripts.find (name2); if (iter==mScripts.end()) { const ESM::Script *script = mStore.get().find (name); - GlobalScriptDesc desc; - desc.mLocals.configure (*script); + auto desc = std::make_shared(); + desc->mLocals.configure (*script); iter = mScripts.insert (std::make_pair (name2, desc)).first; } - return iter->second.mLocals; + return iter->second->mLocals; + } + + void GlobalScripts::updatePtrs(const MWWorld::Ptr& base, const MWWorld::Ptr& updated) + { + MatchPtrVisitor visitor(base); + for (const auto& script : mScripts) + { + if (boost::apply_visitor (visitor, script.second->mTarget)) + script.second->mTarget = updated; + } } } diff --git a/apps/openmw/mwscript/globalscripts.hpp b/apps/openmw/mwscript/globalscripts.hpp index 9b7aa0514e..36d89a7eb7 100644 --- a/apps/openmw/mwscript/globalscripts.hpp +++ b/apps/openmw/mwscript/globalscripts.hpp @@ -1,17 +1,24 @@ #ifndef GAME_SCRIPT_GLOBALSCRIPTS_H #define GAME_SCRIPT_GLOBALSCRIPTS_H +#include + #include #include +#include +#include #include #include "locals.hpp" +#include "../mwworld/ptr.hpp" + namespace ESM { class ESMWriter; class ESMReader; + struct RefNum; } namespace Loading @@ -30,21 +37,25 @@ namespace MWScript { bool mRunning; Locals mLocals; - std::string mId; // ID used to start targeted script (empty if not a targeted script) + boost::variant > mTarget; // Used to start targeted script GlobalScriptDesc(); + + const MWWorld::Ptr* getPtrIfPresent() const; // Returns a Ptr if one has been resolved + + MWWorld::Ptr getPtr(); // Resolves mTarget to a Ptr and caches the (potentially empty) result }; class GlobalScripts { const MWWorld::ESMStore& mStore; - std::map mScripts; + std::map > mScripts; public: GlobalScripts (const MWWorld::ESMStore& store); - void addScript (const std::string& name, const std::string& targetId = ""); + void addScript (const std::string& name, const MWWorld::Ptr& target = MWWorld::Ptr()); void removeScript (const std::string& name); @@ -70,6 +81,9 @@ namespace MWScript Locals& getLocals (const std::string& name); ///< If the script \a name has not been added as a global script yet, it is added /// automatically, but is not set to running state. + + void updatePtrs(const MWWorld::Ptr& base, const MWWorld::Ptr& updated); + ///< Update the Ptrs stored in mTarget. Should be called after the reference has been moved to a new cell. }; } diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 30c56406ef..58596a2f42 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -1,7 +1,6 @@ #include "interpretercontext.hpp" #include -#include #include #include @@ -28,26 +27,6 @@ namespace MWScript { - MWWorld::Ptr InterpreterContext::getReferenceImp ( - const std::string& id, bool activeOnly, bool doThrow) - { - if (!id.empty()) - { - return MWBase::Environment::get().getWorld()->getPtr (id, activeOnly); - } - else - { - if (mReference.isEmpty() && !mTargetId.empty()) - mReference = - MWBase::Environment::get().getWorld()->searchPtr (mTargetId, false); - - if (mReference.isEmpty() && doThrow) - throw std::runtime_error ("no implicit reference"); - - return mReference; - } - } - const MWWorld::Ptr InterpreterContext::getReferenceImp ( const std::string& id, bool activeOnly, bool doThrow) const { @@ -57,12 +36,11 @@ namespace MWScript } else { - if (mReference.isEmpty() && !mTargetId.empty()) - mReference = - MWBase::Environment::get().getWorld()->searchPtr (mTargetId, false); + if (mReference.isEmpty() && mGlobalScriptDesc) + mReference = mGlobalScriptDesc->getPtr(); if (mReference.isEmpty() && doThrow) - throw std::runtime_error ("no implicit reference"); + throw MissingImplicitRefError(); return mReference; } @@ -80,7 +58,7 @@ namespace MWScript { const MWWorld::Ptr ptr = getReferenceImp (id, false); - id = ptr.getClass().getScript (ptr); + id = ptr.getClass().getScript (ptr); ptr.getRefData().setLocals ( *MWBase::Environment::get().getWorld()->getStore().get().find (id)); @@ -109,6 +87,8 @@ namespace MWScript } } + MissingImplicitRefError::MissingImplicitRefError() : std::runtime_error("no implicit reference") {} + int InterpreterContext::findLocalVariableIndex (const std::string& scriptId, const std::string& name, char type) const { @@ -134,16 +114,21 @@ namespace MWScript throw std::runtime_error (stream.str().c_str()); } + InterpreterContext::InterpreterContext (MWScript::Locals *locals, const MWWorld::Ptr& reference) + : mLocals (locals), mReference (reference) + {} - InterpreterContext::InterpreterContext ( - MWScript::Locals *locals, const MWWorld::Ptr& reference, const std::string& targetId) - : mLocals (locals), mReference (reference), mTargetId (targetId) + InterpreterContext::InterpreterContext (std::shared_ptr globalScriptDesc) + : mLocals (&(globalScriptDesc->mLocals)) { - // If we run on a reference (local script, dialogue script or console with object - // selected), store the ID of that reference store it so it can be inherited by - // targeted scripts started from this one. - if (targetId.empty() && !reference.isEmpty()) - mTargetId = reference.getCellRef().getRefId(); + const MWWorld::Ptr* ptr = globalScriptDesc->getPtrIfPresent(); + // A nullptr here signifies that the script's target has not yet been resolved after loading the game. + // Script targets are lazily resolved to MWWorld::Ptrs (which can, upon resolution, be empty) + // because scripts started through dialogue often don't use their implicit target. + if (ptr) + mReference = *ptr; + else + mGlobalScriptDesc = globalScriptDesc; } int InterpreterContext::getLocalShort (int index) const @@ -437,7 +422,12 @@ namespace MWScript void InterpreterContext::startScript (const std::string& name, const std::string& targetId) { - MWBase::Environment::get().getScriptManager()->getGlobalScripts().addScript (name, targetId); + MWWorld::Ptr target; + if (targetId.empty()) + target = getReference(false); + else + target = getReferenceImp(targetId); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().addScript (name, target); } void InterpreterContext::stopScript (const std::string& name) @@ -449,12 +439,7 @@ namespace MWScript { // NOTE: id may be empty, indicating an implicit reference - MWWorld::Ptr ref2; - - if (id.empty()) - ref2 = getReferenceImp(); - else - ref2 = MWBase::Environment::get().getWorld()->getPtr(id, false); + MWWorld::Ptr ref2 = getReferenceImp(id); if (ref2.getContainerStore()) // is the object contained? { @@ -578,11 +563,6 @@ namespace MWScript return getReferenceImp ("", true, required); } - std::string InterpreterContext::getTargetId() const - { - return mTargetId; - } - void InterpreterContext::updatePtr(const MWWorld::Ptr& base, const MWWorld::Ptr& updated) { if (!mReference.isEmpty() && base == mReference) diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index 0e001d6923..1c465ee3e1 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -1,8 +1,13 @@ #ifndef GAME_SCRIPT_INTERPRETERCONTEXT_H #define GAME_SCRIPT_INTERPRETERCONTEXT_H +#include +#include + #include +#include "globalscripts.hpp" + #include "../mwworld/ptr.hpp" namespace MWSound @@ -19,17 +24,17 @@ namespace MWScript { class Locals; + class MissingImplicitRefError : public std::runtime_error + { + public: + MissingImplicitRefError(); + }; + class InterpreterContext : public Interpreter::Context { Locals *mLocals; mutable MWWorld::Ptr mReference; - - std::string mTargetId; - - /// If \a id is empty, a reference the script is run from is returned or in case - /// of a non-local script the reference derived from the target ID. - MWWorld::Ptr getReferenceImp (const std::string& id = "", bool activeOnly = false, - bool doThrow=true); + std::shared_ptr mGlobalScriptDesc; /// If \a id is empty, a reference the script is run from is returned or in case /// of a non-local script the reference derived from the target ID. @@ -47,9 +52,9 @@ namespace MWScript char type) const; public: + InterpreterContext (std::shared_ptr globalScriptDesc); - InterpreterContext (MWScript::Locals *locals, const MWWorld::Ptr& reference, - const std::string& targetId = ""); + InterpreterContext (MWScript::Locals *locals, const MWWorld::Ptr& reference); ///< The ownership of \a locals is not transferred. 0-pointer allowed. virtual int getLocalShort (int index) const; @@ -153,8 +158,6 @@ namespace MWScript void updatePtr(const MWWorld::Ptr& base, const MWWorld::Ptr& updated); ///< Update the Ptr stored in mReference, if there is one stored there. Should be called after the reference has been moved to a new cell. - - virtual std::string getTargetId() const; }; } diff --git a/apps/openmw/mwscript/scriptmanagerimp.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp index 267ff7da68..b69eb7f606 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -19,6 +19,7 @@ #include "../mwworld/esmstore.hpp" #include "extensions.hpp" +#include "interpretercontext.hpp" namespace MWScript { @@ -88,7 +89,7 @@ namespace MWScript return false; } - void ScriptManager::run (const std::string& name, Interpreter::Context& interpreterContext) + bool ScriptManager::run (const std::string& name, Interpreter::Context& interpreterContext) { // compile script ScriptCollection::iterator iter = mScripts.find (name); @@ -100,7 +101,7 @@ namespace MWScript // failed -> ignore script from now on. std::vector empty; mScripts.insert (std::make_pair (name, std::make_pair (empty, Compiler::Locals()))); - return; + return false; } iter = mScripts.find (name); @@ -118,14 +119,19 @@ namespace MWScript } mInterpreter.run (&iter->second.first[0], iter->second.first.size(), interpreterContext); + return true; + } + catch (const MissingImplicitRefError& e) + { + Log(Debug::Error) << "Execution of script " << name << " failed: " << e.what(); } catch (const std::exception& e) { - Log(Debug::Error) << "Execution of script " << name << " failed:"; - Log(Debug::Error) << e.what(); + Log(Debug::Error) << "Execution of script " << name << " failed: " << e.what(); iter->second.first.clear(); // don't execute again. } + return false; } std::pair ScriptManager::compileAll() diff --git a/apps/openmw/mwscript/scriptmanagerimp.hpp b/apps/openmw/mwscript/scriptmanagerimp.hpp index c22a5da81f..27567a1911 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.hpp +++ b/apps/openmw/mwscript/scriptmanagerimp.hpp @@ -55,7 +55,7 @@ namespace MWScript Compiler::Context& compilerContext, int warningsMode, const std::vector& scriptBlacklist); - virtual void run (const std::string& name, Interpreter::Context& interpreterContext); + virtual bool run (const std::string& name, Interpreter::Context& interpreterContext); ///< Run the script with the given name (compile first, if not compiled yet) virtual bool compile (const std::string& name); diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index a724c9bdb0..bf107b422f 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -270,6 +271,37 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name) return Ptr(); } +MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& id, const ESM::RefNum& refNum) +{ + for (auto& pair : mInteriors) + { + Ptr ptr = getPtr(pair.second, id, refNum); + if (!ptr.isEmpty()) + return ptr; + } + for (auto& pair : mExteriors) + { + Ptr ptr = getPtr(pair.second, id, refNum); + if (!ptr.isEmpty()) + return ptr; + } + return Ptr(); +} + +MWWorld::Ptr MWWorld::Cells::getPtr(CellStore& cellStore, const std::string& id, const ESM::RefNum& refNum) +{ + if (cellStore.getState() == CellStore::State_Unloaded) + cellStore.preload(); + if (cellStore.getState() == CellStore::State_Preloaded) + { + if (cellStore.hasId(id)) + cellStore.load(); + else + return Ptr(); + } + return cellStore.searchViaRefNum(refNum); +} + void MWWorld::Cells::getExteriorPtrs(const std::string &name, std::vector &out) { const MWWorld::Store &cells = mStore.get(); diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index 4a6d7abf6a..3e64ad9753 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -13,6 +13,7 @@ namespace ESM class ESMWriter; struct CellId; struct Cell; + struct RefNum; } namespace Loading @@ -41,6 +42,8 @@ namespace MWWorld Ptr getPtrAndCache (const std::string& name, CellStore& cellStore); + Ptr getPtr(CellStore& cellStore, const std::string& id, const ESM::RefNum& refNum); + void writeCell (ESM::ESMWriter& writer, CellStore& cell) const; public: @@ -62,6 +65,8 @@ namespace MWWorld /// @note name must be lower case Ptr getPtr (const std::string& name); + Ptr getPtr(const std::string& id, const ESM::RefNum& refNum); + void rest (double hours); void recharge (float duration); diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 607551f7de..599f345b8a 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -435,6 +436,32 @@ namespace MWWorld return Ptr(); } + class RefNumSearchVisitor + { + const ESM::RefNum& mRefNum; + public: + RefNumSearchVisitor(const ESM::RefNum& refNum) : mRefNum(refNum) {} + + Ptr mFound; + + bool operator()(const Ptr& ptr) + { + if (ptr.getCellRef().getRefNum() == mRefNum) + { + mFound = ptr; + return false; + } + return true; + } + }; + + Ptr CellStore::searchViaRefNum (const ESM::RefNum& refNum) + { + RefNumSearchVisitor searchVisitor(refNum); + forEach(searchVisitor); + return searchVisitor.mFound; + } + float CellStore::getWaterLevel() const { if (isExterior()) diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 371867631b..edd8577ae0 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -41,6 +41,7 @@ namespace ESM struct CellState; struct FogState; struct CellId; + struct RefNum; } namespace MWWorld @@ -242,6 +243,11 @@ namespace MWWorld Ptr searchViaActorId (int id); ///< Will return an empty Ptr if cell is not loaded. + Ptr searchViaRefNum (const ESM::RefNum& refNum); + ///< Will return an empty Ptr if cell is not loaded. Does not check references in + /// containers. + /// @note Triggers CellStore hasState flag. + float getWaterLevel() const; bool movedHere(const MWWorld::Ptr& ptr) const; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index a623a5d52f..8caec9876b 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -752,6 +753,11 @@ namespace MWWorld return mWorldScene->searchPtrViaActorId (actorId); } + Ptr World::searchPtrViaRefNum (const std::string& id, const ESM::RefNum& refNum) + { + return mCells.getPtr (id, refNum); + } + struct FindContainerVisitor { ConstPtr mContainedPtr; @@ -1290,6 +1296,7 @@ namespace MWWorld mRendering->updatePtr(ptr, newPtr); MWBase::Environment::get().getSoundManager()->updatePtr (ptr, newPtr); mPhysics->updatePtr(ptr, newPtr); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().updatePtrs(ptr, newPtr); MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager(); mechMgr->updateCell(ptr, newPtr); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index fd36e5ba21..7b6d2afdcd 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -297,6 +297,8 @@ namespace MWWorld Ptr searchPtrViaActorId (int actorId) override; ///< Search is limited to the active cells. + Ptr searchPtrViaRefNum (const std::string& id, const ESM::RefNum& refNum) override; + MWWorld::Ptr findContainer (const MWWorld::ConstPtr& ptr) override; ///< Return a pointer to a liveCellRef which contains \a ptr. /// \note Search is limited to the active cells. diff --git a/components/esm/globalscript.cpp b/components/esm/globalscript.cpp index a42cdc2309..239d162f29 100644 --- a/components/esm/globalscript.cpp +++ b/components/esm/globalscript.cpp @@ -12,7 +12,11 @@ void ESM::GlobalScript::load (ESMReader &esm) mRunning = 0; esm.getHNOT (mRunning, "RUN_"); - mTargetId = esm.getHNOString ("TARG"); + mTargetRef.unset(); + if (esm.peekNextSub("TARG")) + mTargetId = esm.getHNString ("TARG"); + if (esm.peekNextSub("FRMR")) + mTargetRef.load(esm, true, "FRMR"); } void ESM::GlobalScript::save (ESMWriter &esm) const @@ -24,5 +28,10 @@ void ESM::GlobalScript::save (ESMWriter &esm) const if (mRunning) esm.writeHNT ("RUN_", mRunning); - esm.writeHNOString ("TARG", mTargetId); + if (!mTargetId.empty()) + { + esm.writeHNOString ("TARG", mTargetId); + if (mTargetRef.hasContentFile()) + mTargetRef.save (esm, true, "FRMR"); + } } diff --git a/components/esm/globalscript.hpp b/components/esm/globalscript.hpp index 8b7e627953..1a1a6cf4eb 100644 --- a/components/esm/globalscript.hpp +++ b/components/esm/globalscript.hpp @@ -2,6 +2,7 @@ #define OPENMW_ESM_GLOBALSCRIPT_H #include "locals.hpp" +#include "cellref.hpp" namespace ESM { @@ -16,6 +17,7 @@ namespace ESM Locals mLocals; int mRunning; std::string mTargetId; // for targeted scripts + RefNum mTargetRef; void load (ESMReader &esm); void save (ESMWriter &esm) const; diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index ea9fef4fbf..633f7c9fd0 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -5,7 +5,7 @@ #include "defs.hpp" unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; -int ESM::SavedGame::sCurrentFormat = 5; +int ESM::SavedGame::sCurrentFormat = 6; void ESM::SavedGame::load (ESMReader &esm) { diff --git a/components/interpreter/context.hpp b/components/interpreter/context.hpp index 4c320879e6..88fdae1280 100644 --- a/components/interpreter/context.hpp +++ b/components/interpreter/context.hpp @@ -108,8 +108,6 @@ namespace Interpreter virtual void setMemberFloat (const std::string& id, const std::string& name, float value, bool global) = 0; - - virtual std::string getTargetId() const = 0; }; } diff --git a/components/interpreter/scriptopcodes.hpp b/components/interpreter/scriptopcodes.hpp index 976390eb59..c98bcd23e2 100644 --- a/components/interpreter/scriptopcodes.hpp +++ b/components/interpreter/scriptopcodes.hpp @@ -26,7 +26,7 @@ namespace Interpreter { std::string name = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - runtime.getContext().startScript (name, runtime.getContext().getTargetId()); + runtime.getContext().startScript (name); } };