mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-19 20:23:54 +00:00
Merge pull request #2648 from Assumeru/start-scripts
Allow targeting non-unique actors with StartScript. Fixes #2311
This commit is contained in:
commit
62290182eb
20 changed files with 309 additions and 118 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -5,83 +5,171 @@
|
|||
#include <components/esm/esmwriter.hpp>
|
||||
#include <components/esm/globalscript.hpp>
|
||||
|
||||
#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>
|
||||
{
|
||||
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
|
||||
{
|
||||
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<std::string, GlobalScriptDesc>::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<ESM::Script>().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<GlobalScriptDesc>();
|
||||
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<std::string, GlobalScriptDesc>::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<std::string, GlobalScriptDesc>::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<std::string, GlobalScriptDesc>::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<std::string, GlobalScriptDesc>::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<std::string, GlobalScriptDesc>::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<GlobalScriptDesc>();
|
||||
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<std::string, GlobalScriptDesc>::iterator iter = mScripts.find (name2);
|
||||
auto iter = mScripts.find (name2);
|
||||
|
||||
if (iter==mScripts.end())
|
||||
{
|
||||
const ESM::Script *script = mStore.get<ESM::Script>().find (name);
|
||||
|
||||
GlobalScriptDesc desc;
|
||||
desc.mLocals.configure (*script);
|
||||
auto desc = std::make_shared<GlobalScriptDesc>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,24 @@
|
|||
#ifndef GAME_SCRIPT_GLOBALSCRIPTS_H
|
||||
#define GAME_SCRIPT_GLOBALSCRIPTS_H
|
||||
|
||||
#include <boost/variant/variant.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#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<MWWorld::Ptr, std::pair<ESM::RefNum, std::string> > 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<std::string, GlobalScriptDesc> mScripts;
|
||||
std::map<std::string, std::shared_ptr<GlobalScriptDesc> > 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.
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#include "interpretercontext.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
#include <components/compiler/locals.hpp>
|
||||
|
@ -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<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,
|
||||
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> 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)
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
#ifndef GAME_SCRIPT_INTERPRETERCONTEXT_H
|
||||
#define GAME_SCRIPT_INTERPRETERCONTEXT_H
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <components/interpreter/context.hpp>
|
||||
|
||||
#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<GlobalScriptDesc> 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> 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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Interpreter::Type_Code> 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<int, int> ScriptManager::compileAll()
|
||||
|
|
|
@ -55,7 +55,7 @@ namespace MWScript
|
|||
Compiler::Context& compilerContext, int warningsMode,
|
||||
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)
|
||||
|
||||
virtual bool compile (const std::string& name);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <components/esm/esmwriter.hpp>
|
||||
#include <components/esm/defs.hpp>
|
||||
#include <components/esm/cellstate.hpp>
|
||||
#include <components/esm/cellref.hpp>
|
||||
#include <components/loadinglistener/loadinglistener.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
|
@ -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<MWWorld::Ptr> &out)
|
||||
{
|
||||
const MWWorld::Store<ESM::Cell> &cells = mStore.get<ESM::Cell>();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <components/esm/cellstate.hpp>
|
||||
#include <components/esm/cellid.hpp>
|
||||
#include <components/esm/cellref.hpp>
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm/esmwriter.hpp>
|
||||
#include <components/esm/objectstate.hpp>
|
||||
|
@ -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())
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm/esmwriter.hpp>
|
||||
#include <components/esm/cellid.hpp>
|
||||
#include <components/esm/cellref.hpp>
|
||||
|
||||
#include <components/misc/constants.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue