Allow targeting non-unique actors with StartScript (bug #2311)

pull/2648/head
unknown 5 years ago
parent 7a6ba8bf7a
commit 3b4782959e

@ -2,6 +2,7 @@
------ ------
Bug #1952: Incorrect particle lighting 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 #3676: NiParticleColorModifier isn't applied properly
Bug #5358: ForceGreeting always resets the dialogue window completely Bug #5358: ForceGreeting always resets the dialogue window completely
Bug #5363: Enchantment autocalc not always 0/1 Bug #5363: Enchantment autocalc not always 0/1

@ -35,7 +35,7 @@ namespace MWBase
virtual ~ScriptManager() {} 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) ///< Run the script with the given name (compile first, if not compiled yet)
virtual bool compile (const std::string& name) = 0; virtual bool compile (const std::string& name) = 0;

@ -189,6 +189,8 @@ namespace MWBase
virtual MWWorld::Ptr searchPtrViaActorId (int actorId) = 0; virtual MWWorld::Ptr searchPtrViaActorId (int actorId) = 0;
///< Search is limited to the active cells. ///< 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; virtual MWWorld::Ptr findContainer (const MWWorld::ConstPtr& ptr) = 0;
///< Return a pointer to a liveCellRef which contains \a ptr. ///< Return a pointer to a liveCellRef which contains \a ptr.
/// \note Search is limited to the active cells. /// \note Search is limited to the active cells.

@ -5,83 +5,171 @@
#include <components/esm/esmwriter.hpp> #include <components/esm/esmwriter.hpp>
#include <components/esm/globalscript.hpp> #include <components/esm/globalscript.hpp>
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/scriptmanager.hpp" #include "../mwbase/scriptmanager.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "interpretercontext.hpp" #include "interpretercontext.hpp"
namespace
{
struct ScriptCreatingVisitor : public boost::static_visitor<ESM::GlobalScript>
{
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<ESM::RefNum, std::string> &pair) const
{
ESM::GlobalScript script;
script.mTargetId = pair.second;
script.mTargetRef = pair.first;
return script;
}
};
struct PtrGettingVisitor : public boost::static_visitor<const MWWorld::Ptr*>
{
const MWWorld::Ptr* operator()(const MWWorld::Ptr &ptr) const
{
return &ptr;
}
const MWWorld::Ptr* operator()(const std::pair<ESM::RefNum, std::string> &pair) const
{
return nullptr;
}
};
struct PtrResolvingVisitor : public boost::static_visitor<MWWorld::Ptr>
{
MWWorld::Ptr operator()(const MWWorld::Ptr &ptr) const
{
return ptr;
}
MWWorld::Ptr operator()(const std::pair<ESM::RefNum, std::string> &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<bool>
{
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<ESM::RefNum, std::string> &pair) const
{
return false;
}
};
}
namespace MWScript namespace MWScript
{ {
GlobalScriptDesc::GlobalScriptDesc() : mRunning (false) {} 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) GlobalScripts::GlobalScripts (const MWWorld::ESMStore& store)
: mStore (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<std::string, GlobalScriptDesc>::iterator iter = const auto iter = mScripts.find (::Misc::StringUtils::lowerCase (name));
mScripts.find (::Misc::StringUtils::lowerCase (name));
if (iter==mScripts.end()) if (iter==mScripts.end())
{ {
if (const ESM::Script *script = mStore.get<ESM::Script>().search(name)) if (const ESM::Script *script = mStore.get<ESM::Script>().search(name))
{ {
GlobalScriptDesc desc; auto desc = std::make_shared<GlobalScriptDesc>();
desc.mRunning = true; MWWorld::Ptr ptr = target;
desc.mLocals.configure (*script); desc->mTarget = ptr;
desc.mId = targetId; desc->mRunning = true;
desc->mLocals.configure (*script);
mScripts.insert (std::make_pair (name, desc)); mScripts.insert (std::make_pair(name, desc));
} }
else else
{ {
Log(Debug::Error) << "Failed to add global script " << name << ": script record not found"; 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->mRunning = true;
iter->second.mId = targetId; MWWorld::Ptr ptr = target;
iter->second->mTarget = ptr;
} }
} }
void GlobalScripts::removeScript (const std::string& name) void GlobalScripts::removeScript (const std::string& name)
{ {
std::map<std::string, GlobalScriptDesc>::iterator iter = const auto iter = mScripts.find (::Misc::StringUtils::lowerCase (name));
mScripts.find (::Misc::StringUtils::lowerCase (name));
if (iter!=mScripts.end()) if (iter!=mScripts.end())
iter->second.mRunning = false; iter->second->mRunning = false;
} }
bool GlobalScripts::isRunning (const std::string& name) const bool GlobalScripts::isRunning (const std::string& name) const
{ {
std::map<std::string, GlobalScriptDesc>::const_iterator iter = const auto iter = mScripts.find (::Misc::StringUtils::lowerCase (name));
mScripts.find (::Misc::StringUtils::lowerCase (name));
if (iter==mScripts.end()) if (iter==mScripts.end())
return false; return false;
return iter->second.mRunning; return iter->second->mRunning;
} }
void GlobalScripts::run() void GlobalScripts::run()
{ {
for (std::map<std::string, GlobalScriptDesc>::iterator iter (mScripts.begin()); for (const auto& script : mScripts)
iter!=mScripts.end(); ++iter)
{ {
if (iter->second.mRunning) if (script.second->mRunning)
{ {
MWWorld::Ptr ptr; MWScript::InterpreterContext context(script.second);
if (!MWBase::Environment::get().getScriptManager()->run(script.first, context))
MWScript::InterpreterContext interpreterContext ( script.second->mRunning = false;
&iter->second.mLocals, MWWorld::Ptr(), iter->second.mId);
MWBase::Environment::get().getScriptManager()->run (iter->first, interpreterContext);
} }
} }
} }
@ -129,18 +217,15 @@ namespace MWScript
void GlobalScripts::write (ESM::ESMWriter& writer, Loading::Listener& progress) const void GlobalScripts::write (ESM::ESMWriter& writer, Loading::Listener& progress) const
{ {
for (std::map<std::string, GlobalScriptDesc>::const_iterator iter (mScripts.begin()); for (const auto& iter : mScripts)
iter!=mScripts.end(); ++iter)
{ {
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.mRunning = iter.second->mRunning ? 1 : 0;
script.mTargetId = iter->second.mId;
writer.startRecord (ESM::REC_GSCR); writer.startRecord (ESM::REC_GSCR);
script.save (writer); script.save (writer);
@ -155,8 +240,7 @@ namespace MWScript
ESM::GlobalScript script; ESM::GlobalScript script;
script.load (reader); script.load (reader);
std::map<std::string, GlobalScriptDesc>::iterator iter = auto iter = mScripts.find (script.mId);
mScripts.find (script.mId);
if (iter==mScripts.end()) if (iter==mScripts.end())
{ {
@ -164,8 +248,12 @@ namespace MWScript
{ {
try try
{ {
GlobalScriptDesc desc; auto desc = std::make_shared<GlobalScriptDesc>();
desc.mLocals.configure (*scriptRecord); 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; iter = mScripts.insert (std::make_pair (script.mId, desc)).first;
} }
@ -182,9 +270,8 @@ namespace MWScript
return true; return true;
} }
iter->second.mRunning = script.mRunning!=0; iter->second->mRunning = script.mRunning!=0;
iter->second.mLocals.read (script.mLocals, script.mId); iter->second->mLocals.read (script.mLocals, script.mId);
iter->second.mId = script.mTargetId;
return true; return true;
} }
@ -195,18 +282,28 @@ namespace MWScript
Locals& GlobalScripts::getLocals (const std::string& name) Locals& GlobalScripts::getLocals (const std::string& name)
{ {
std::string name2 = ::Misc::StringUtils::lowerCase (name); std::string name2 = ::Misc::StringUtils::lowerCase (name);
std::map<std::string, GlobalScriptDesc>::iterator iter = mScripts.find (name2); auto iter = mScripts.find (name2);
if (iter==mScripts.end()) if (iter==mScripts.end())
{ {
const ESM::Script *script = mStore.get<ESM::Script>().find (name); const ESM::Script *script = mStore.get<ESM::Script>().find (name);
GlobalScriptDesc desc; auto desc = std::make_shared<GlobalScriptDesc>();
desc.mLocals.configure (*script); desc->mLocals.configure (*script);
iter = mScripts.insert (std::make_pair (name2, desc)).first; 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;
}
} }
} }

@ -1,17 +1,24 @@
#ifndef GAME_SCRIPT_GLOBALSCRIPTS_H #ifndef GAME_SCRIPT_GLOBALSCRIPTS_H
#define GAME_SCRIPT_GLOBALSCRIPTS_H #define GAME_SCRIPT_GLOBALSCRIPTS_H
#include <boost/variant/variant.hpp>
#include <string> #include <string>
#include <map> #include <map>
#include <memory>
#include <utility>
#include <stdint.h> #include <stdint.h>
#include "locals.hpp" #include "locals.hpp"
#include "../mwworld/ptr.hpp"
namespace ESM namespace ESM
{ {
class ESMWriter; class ESMWriter;
class ESMReader; class ESMReader;
struct RefNum;
} }
namespace Loading namespace Loading
@ -30,21 +37,25 @@ namespace MWScript
{ {
bool mRunning; bool mRunning;
Locals mLocals; Locals mLocals;
std::string mId; // ID used to start targeted script (empty if not a targeted script) boost::variant<MWWorld::Ptr, std::pair<ESM::RefNum, std::string> > mTarget; // Used to start targeted script
GlobalScriptDesc(); 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 class GlobalScripts
{ {
const MWWorld::ESMStore& mStore; const MWWorld::ESMStore& mStore;
std::map<std::string, GlobalScriptDesc> mScripts; std::map<std::string, std::shared_ptr<GlobalScriptDesc> > mScripts;
public: public:
GlobalScripts (const MWWorld::ESMStore& store); 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); void removeScript (const std::string& name);
@ -70,6 +81,9 @@ namespace MWScript
Locals& getLocals (const std::string& name); Locals& getLocals (const std::string& name);
///< If the script \a name has not been added as a global script yet, it is added ///< 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. /// 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.
}; };
} }

@ -1,7 +1,6 @@
#include "interpretercontext.hpp" #include "interpretercontext.hpp"
#include <cmath> #include <cmath>
#include <stdexcept>
#include <sstream> #include <sstream>
#include <components/compiler/locals.hpp> #include <components/compiler/locals.hpp>
@ -28,26 +27,6 @@
namespace MWScript 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 MWWorld::Ptr InterpreterContext::getReferenceImp (
const std::string& id, bool activeOnly, bool doThrow) const const std::string& id, bool activeOnly, bool doThrow) const
{ {
@ -57,12 +36,11 @@ namespace MWScript
} }
else else
{ {
if (mReference.isEmpty() && !mTargetId.empty()) if (mReference.isEmpty() && mGlobalScriptDesc)
mReference = mReference = mGlobalScriptDesc->getPtr();
MWBase::Environment::get().getWorld()->searchPtr (mTargetId, false);
if (mReference.isEmpty() && doThrow) if (mReference.isEmpty() && doThrow)
throw std::runtime_error ("no implicit reference"); throw MissingImplicitRefError();
return mReference; return mReference;
} }
@ -80,7 +58,7 @@ namespace MWScript
{ {
const MWWorld::Ptr ptr = getReferenceImp (id, false); const MWWorld::Ptr ptr = getReferenceImp (id, false);
id = ptr.getClass().getScript (ptr); id = ptr.getClass().getScript (ptr);
ptr.getRefData().setLocals ( ptr.getRefData().setLocals (
*MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().find (id)); *MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().find (id));
@ -109,6 +87,8 @@ namespace MWScript
} }
} }
MissingImplicitRefError::MissingImplicitRefError() : std::runtime_error("no implicit reference") {}
int InterpreterContext::findLocalVariableIndex (const std::string& scriptId, int InterpreterContext::findLocalVariableIndex (const std::string& scriptId,
const std::string& name, char type) const const std::string& name, char type) const
{ {
@ -134,16 +114,21 @@ namespace MWScript
throw std::runtime_error (stream.str().c_str()); throw std::runtime_error (stream.str().c_str());
} }
InterpreterContext::InterpreterContext (MWScript::Locals *locals, const MWWorld::Ptr& reference)
: mLocals (locals), mReference (reference)
{}
InterpreterContext::InterpreterContext ( InterpreterContext::InterpreterContext (std::shared_ptr<GlobalScriptDesc> globalScriptDesc)
MWScript::Locals *locals, const MWWorld::Ptr& reference, const std::string& targetId) : mLocals (&(globalScriptDesc->mLocals))
: mLocals (locals), mReference (reference), mTargetId (targetId)
{ {
// If we run on a reference (local script, dialogue script or console with object const MWWorld::Ptr* ptr = globalScriptDesc->getPtrIfPresent();
// selected), store the ID of that reference store it so it can be inherited by // A nullptr here signifies that the script's target has not yet been resolved after loading the game.
// targeted scripts started from this one. // Script targets are lazily resolved to MWWorld::Ptrs (which can, upon resolution, be empty)
if (targetId.empty() && !reference.isEmpty()) // because scripts started through dialogue often don't use their implicit target.
mTargetId = reference.getCellRef().getRefId(); if (ptr)
mReference = *ptr;
else
mGlobalScriptDesc = globalScriptDesc;
} }
int InterpreterContext::getLocalShort (int index) const int InterpreterContext::getLocalShort (int index) const
@ -437,7 +422,12 @@ namespace MWScript
void InterpreterContext::startScript (const std::string& name, const std::string& targetId) 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) void InterpreterContext::stopScript (const std::string& name)
@ -449,12 +439,7 @@ namespace MWScript
{ {
// NOTE: id may be empty, indicating an implicit reference // NOTE: id may be empty, indicating an implicit reference
MWWorld::Ptr ref2; MWWorld::Ptr ref2 = getReferenceImp(id);
if (id.empty())
ref2 = getReferenceImp();
else
ref2 = MWBase::Environment::get().getWorld()->getPtr(id, false);
if (ref2.getContainerStore()) // is the object contained? if (ref2.getContainerStore()) // is the object contained?
{ {
@ -578,11 +563,6 @@ namespace MWScript
return getReferenceImp ("", true, required); return getReferenceImp ("", true, required);
} }
std::string InterpreterContext::getTargetId() const
{
return mTargetId;
}
void InterpreterContext::updatePtr(const MWWorld::Ptr& base, const MWWorld::Ptr& updated) void InterpreterContext::updatePtr(const MWWorld::Ptr& base, const MWWorld::Ptr& updated)
{ {
if (!mReference.isEmpty() && base == mReference) if (!mReference.isEmpty() && base == mReference)

@ -1,8 +1,13 @@
#ifndef GAME_SCRIPT_INTERPRETERCONTEXT_H #ifndef GAME_SCRIPT_INTERPRETERCONTEXT_H
#define GAME_SCRIPT_INTERPRETERCONTEXT_H #define GAME_SCRIPT_INTERPRETERCONTEXT_H
#include <memory>
#include <stdexcept>
#include <components/interpreter/context.hpp> #include <components/interpreter/context.hpp>
#include "globalscripts.hpp"
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
namespace MWSound namespace MWSound
@ -19,17 +24,17 @@ namespace MWScript
{ {
class Locals; class Locals;
class MissingImplicitRefError : public std::runtime_error
{
public:
MissingImplicitRefError();
};
class InterpreterContext : public Interpreter::Context class InterpreterContext : public Interpreter::Context
{ {
Locals *mLocals; Locals *mLocals;
mutable MWWorld::Ptr mReference; mutable MWWorld::Ptr mReference;
std::shared_ptr<GlobalScriptDesc> mGlobalScriptDesc;
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);
/// If \a id is empty, a reference the script is run from is returned or in case /// 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. /// of a non-local script the reference derived from the target ID.
@ -47,9 +52,9 @@ namespace MWScript
char type) const; char type) const;
public: public:
InterpreterContext (std::shared_ptr<GlobalScriptDesc> globalScriptDesc);
InterpreterContext (MWScript::Locals *locals, const MWWorld::Ptr& reference, InterpreterContext (MWScript::Locals *locals, const MWWorld::Ptr& reference);
const std::string& targetId = "");
///< The ownership of \a locals is not transferred. 0-pointer allowed. ///< The ownership of \a locals is not transferred. 0-pointer allowed.
virtual int getLocalShort (int index) const; virtual int getLocalShort (int index) const;
@ -153,8 +158,6 @@ namespace MWScript
void updatePtr(const MWWorld::Ptr& base, const MWWorld::Ptr& updated); 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. ///< 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;
}; };
} }

@ -19,6 +19,7 @@
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "extensions.hpp" #include "extensions.hpp"
#include "interpretercontext.hpp"
namespace MWScript namespace MWScript
{ {
@ -88,7 +89,7 @@ namespace MWScript
return false; return false;
} }
void ScriptManager::run (const std::string& name, Interpreter::Context& interpreterContext) bool ScriptManager::run (const std::string& name, Interpreter::Context& interpreterContext)
{ {
// compile script // compile script
ScriptCollection::iterator iter = mScripts.find (name); ScriptCollection::iterator iter = mScripts.find (name);
@ -100,7 +101,7 @@ namespace MWScript
// failed -> ignore script from now on. // failed -> ignore script from now on.
std::vector<Interpreter::Type_Code> empty; std::vector<Interpreter::Type_Code> empty;
mScripts.insert (std::make_pair (name, std::make_pair (empty, Compiler::Locals()))); mScripts.insert (std::make_pair (name, std::make_pair (empty, Compiler::Locals())));
return; return false;
} }
iter = mScripts.find (name); iter = mScripts.find (name);
@ -118,14 +119,19 @@ namespace MWScript
} }
mInterpreter.run (&iter->second.first[0], iter->second.first.size(), interpreterContext); 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) catch (const std::exception& e)
{ {
Log(Debug::Error) << "Execution of script " << name << " failed:"; Log(Debug::Error) << "Execution of script " << name << " failed: " << e.what();
Log(Debug::Error) << e.what();
iter->second.first.clear(); // don't execute again. iter->second.first.clear(); // don't execute again.
} }
return false;
} }
std::pair<int, int> ScriptManager::compileAll() std::pair<int, int> ScriptManager::compileAll()

@ -55,7 +55,7 @@ namespace MWScript
Compiler::Context& compilerContext, int warningsMode, Compiler::Context& compilerContext, int warningsMode,
const std::vector<std::string>& scriptBlacklist); const std::vector<std::string>& 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) ///< Run the script with the given name (compile first, if not compiled yet)
virtual bool compile (const std::string& name); virtual bool compile (const std::string& name);

@ -5,6 +5,7 @@
#include <components/esm/esmwriter.hpp> #include <components/esm/esmwriter.hpp>
#include <components/esm/defs.hpp> #include <components/esm/defs.hpp>
#include <components/esm/cellstate.hpp> #include <components/esm/cellstate.hpp>
#include <components/esm/cellref.hpp>
#include <components/loadinglistener/loadinglistener.hpp> #include <components/loadinglistener/loadinglistener.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
@ -270,6 +271,37 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name)
return Ptr(); 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<MWWorld::Ptr> &out) void MWWorld::Cells::getExteriorPtrs(const std::string &name, std::vector<MWWorld::Ptr> &out)
{ {
const MWWorld::Store<ESM::Cell> &cells = mStore.get<ESM::Cell>(); const MWWorld::Store<ESM::Cell> &cells = mStore.get<ESM::Cell>();

@ -13,6 +13,7 @@ namespace ESM
class ESMWriter; class ESMWriter;
struct CellId; struct CellId;
struct Cell; struct Cell;
struct RefNum;
} }
namespace Loading namespace Loading
@ -41,6 +42,8 @@ namespace MWWorld
Ptr getPtrAndCache (const std::string& name, CellStore& cellStore); 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; void writeCell (ESM::ESMWriter& writer, CellStore& cell) const;
public: public:
@ -62,6 +65,8 @@ namespace MWWorld
/// @note name must be lower case /// @note name must be lower case
Ptr getPtr (const std::string& name); Ptr getPtr (const std::string& name);
Ptr getPtr(const std::string& id, const ESM::RefNum& refNum);
void rest (double hours); void rest (double hours);
void recharge (float duration); void recharge (float duration);

@ -6,6 +6,7 @@
#include <components/esm/cellstate.hpp> #include <components/esm/cellstate.hpp>
#include <components/esm/cellid.hpp> #include <components/esm/cellid.hpp>
#include <components/esm/cellref.hpp>
#include <components/esm/esmreader.hpp> #include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp> #include <components/esm/esmwriter.hpp>
#include <components/esm/objectstate.hpp> #include <components/esm/objectstate.hpp>
@ -435,6 +436,32 @@ namespace MWWorld
return Ptr(); 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 float CellStore::getWaterLevel() const
{ {
if (isExterior()) if (isExterior())

@ -41,6 +41,7 @@ namespace ESM
struct CellState; struct CellState;
struct FogState; struct FogState;
struct CellId; struct CellId;
struct RefNum;
} }
namespace MWWorld namespace MWWorld
@ -242,6 +243,11 @@ namespace MWWorld
Ptr searchViaActorId (int id); Ptr searchViaActorId (int id);
///< Will return an empty Ptr if cell is not loaded. ///< 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; float getWaterLevel() const;
bool movedHere(const MWWorld::Ptr& ptr) const; bool movedHere(const MWWorld::Ptr& ptr) const;

@ -11,6 +11,7 @@
#include <components/esm/esmreader.hpp> #include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp> #include <components/esm/esmwriter.hpp>
#include <components/esm/cellid.hpp> #include <components/esm/cellid.hpp>
#include <components/esm/cellref.hpp>
#include <components/misc/constants.hpp> #include <components/misc/constants.hpp>
#include <components/misc/resourcehelpers.hpp> #include <components/misc/resourcehelpers.hpp>
@ -752,6 +753,11 @@ namespace MWWorld
return mWorldScene->searchPtrViaActorId (actorId); return mWorldScene->searchPtrViaActorId (actorId);
} }
Ptr World::searchPtrViaRefNum (const std::string& id, const ESM::RefNum& refNum)
{
return mCells.getPtr (id, refNum);
}
struct FindContainerVisitor struct FindContainerVisitor
{ {
ConstPtr mContainedPtr; ConstPtr mContainedPtr;
@ -1290,6 +1296,7 @@ namespace MWWorld
mRendering->updatePtr(ptr, newPtr); mRendering->updatePtr(ptr, newPtr);
MWBase::Environment::get().getSoundManager()->updatePtr (ptr, newPtr); MWBase::Environment::get().getSoundManager()->updatePtr (ptr, newPtr);
mPhysics->updatePtr(ptr, newPtr); mPhysics->updatePtr(ptr, newPtr);
MWBase::Environment::get().getScriptManager()->getGlobalScripts().updatePtrs(ptr, newPtr);
MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager(); MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager();
mechMgr->updateCell(ptr, newPtr); mechMgr->updateCell(ptr, newPtr);

@ -297,6 +297,8 @@ namespace MWWorld
Ptr searchPtrViaActorId (int actorId) override; Ptr searchPtrViaActorId (int actorId) override;
///< Search is limited to the active cells. ///< 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; MWWorld::Ptr findContainer (const MWWorld::ConstPtr& ptr) override;
///< Return a pointer to a liveCellRef which contains \a ptr. ///< Return a pointer to a liveCellRef which contains \a ptr.
/// \note Search is limited to the active cells. /// \note Search is limited to the active cells.

@ -12,7 +12,11 @@ void ESM::GlobalScript::load (ESMReader &esm)
mRunning = 0; mRunning = 0;
esm.getHNOT (mRunning, "RUN_"); 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 void ESM::GlobalScript::save (ESMWriter &esm) const
@ -24,5 +28,10 @@ void ESM::GlobalScript::save (ESMWriter &esm) const
if (mRunning) if (mRunning)
esm.writeHNT ("RUN_", mRunning); esm.writeHNT ("RUN_", mRunning);
esm.writeHNOString ("TARG", mTargetId); if (!mTargetId.empty())
{
esm.writeHNOString ("TARG", mTargetId);
if (mTargetRef.hasContentFile())
mTargetRef.save (esm, true, "FRMR");
}
} }

@ -2,6 +2,7 @@
#define OPENMW_ESM_GLOBALSCRIPT_H #define OPENMW_ESM_GLOBALSCRIPT_H
#include "locals.hpp" #include "locals.hpp"
#include "cellref.hpp"
namespace ESM namespace ESM
{ {
@ -16,6 +17,7 @@ namespace ESM
Locals mLocals; Locals mLocals;
int mRunning; int mRunning;
std::string mTargetId; // for targeted scripts std::string mTargetId; // for targeted scripts
RefNum mTargetRef;
void load (ESMReader &esm); void load (ESMReader &esm);
void save (ESMWriter &esm) const; void save (ESMWriter &esm) const;

@ -5,7 +5,7 @@
#include "defs.hpp" #include "defs.hpp"
unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE;
int ESM::SavedGame::sCurrentFormat = 5; int ESM::SavedGame::sCurrentFormat = 6;
void ESM::SavedGame::load (ESMReader &esm) void ESM::SavedGame::load (ESMReader &esm)
{ {

@ -108,8 +108,6 @@ namespace Interpreter
virtual void setMemberFloat (const std::string& id, const std::string& name, float value, bool global) virtual void setMemberFloat (const std::string& id, const std::string& name, float value, bool global)
= 0; = 0;
virtual std::string getTargetId() const = 0;
}; };
} }

@ -26,7 +26,7 @@ namespace Interpreter
{ {
std::string name = runtime.getStringLiteral (runtime[0].mInteger); std::string name = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop(); runtime.pop();
runtime.getContext().startScript (name, runtime.getContext().getTargetId()); runtime.getContext().startScript (name);
} }
}; };

Loading…
Cancel
Save