Merge branch 'scripts'

deque
Marc Zinnschlag 11 years ago
commit c9cd7fb6b7

@ -9,7 +9,7 @@ opencs_units (model/doc
) )
opencs_units_noqt (model/doc opencs_units_noqt (model/doc
stage savingstate savingstages stage savingstate savingstages blacklist
) )
opencs_hdrs_noqt (model/doc opencs_hdrs_noqt (model/doc

@ -86,7 +86,11 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
("encoding", boost::program_options::value<std::string>()->default_value("win1252")) ("encoding", boost::program_options::value<std::string>()->default_value("win1252"))
("resources", boost::program_options::value<std::string>()->default_value("resources")) ("resources", boost::program_options::value<std::string>()->default_value("resources"))
("fallback-archive", boost::program_options::value<std::vector<std::string> >()-> ("fallback-archive", boost::program_options::value<std::vector<std::string> >()->
default_value(std::vector<std::string>(), "fallback-archive")->multitoken()); default_value(std::vector<std::string>(), "fallback-archive")->multitoken())
("script-blacklist", boost::program_options::value<std::vector<std::string> >()->default_value(std::vector<std::string>(), "")
->multitoken(), "exclude specified script from the verifier (if the use of the blacklist is enabled)")
("script-blacklist-use", boost::program_options::value<bool>()->implicit_value(true)
->default_value(true), "enable script blacklisting");
boost::program_options::notify(variables); boost::program_options::notify(variables);
@ -97,6 +101,10 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
mDocumentManager.setResourceDir (mResources = variables["resources"].as<std::string>()); mDocumentManager.setResourceDir (mResources = variables["resources"].as<std::string>());
if (variables["script-blacklist-use"].as<bool>())
mDocumentManager.setBlacklistedScripts (
variables["script-blacklist"].as<std::vector<std::string> >());
mFsStrict = variables["fs-strict"].as<bool>(); mFsStrict = variables["fs-strict"].as<bool>();
Files::PathContainer dataDirs, dataLocal; Files::PathContainer dataDirs, dataLocal;

@ -0,0 +1,31 @@
#include "blacklist.hpp"
#include <algorithm>
#include <components/misc/stringops.hpp>
bool CSMDoc::Blacklist::isBlacklisted (const CSMWorld::UniversalId& id) const
{
std::map<CSMWorld::UniversalId::Type, std::vector<std::string> >::const_iterator iter =
mIds.find (id.getType());
if (iter==mIds.end())
return false;
return std::binary_search (iter->second.begin(), iter->second.end(),
Misc::StringUtils::lowerCase (id.getId()));
}
void CSMDoc::Blacklist::add (CSMWorld::UniversalId::Type type,
const std::vector<std::string>& ids)
{
std::vector<std::string>& list = mIds[type];
int size = list.size();
list.resize (size+ids.size());
std::transform (ids.begin(), ids.end(), list.begin()+size, Misc::StringUtils::lowerCase);
std::sort (list.begin(), list.end());
}

@ -0,0 +1,25 @@
#ifndef CSM_DOC_BLACKLIST_H
#define CSM_DOC_BLACKLIST_H
#include <map>
#include <vector>
#include <string>
#include "../world/universalid.hpp"
namespace CSMDoc
{
/// \brief ID blacklist sorted by UniversalId type
class Blacklist
{
std::map<CSMWorld::UniversalId::Type, std::vector<std::string> > mIds;
public:
bool isBlacklisted (const CSMWorld::UniversalId& id) const;
void add (CSMWorld::UniversalId::Type type, const std::vector<std::string>& ids);
};
}
#endif

@ -2205,9 +2205,10 @@ void CSMDoc::Document::createBase()
CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, CSMDoc::Document::Document (const Files::ConfigurationManager& configuration,
const std::vector< boost::filesystem::path >& files, bool new_, const std::vector< boost::filesystem::path >& files, bool new_,
const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir,
ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager) ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager,
const std::vector<std::string>& blacklistedScripts)
: mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, resourcesManager), : mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, resourcesManager),
mTools (mData), mResDir(resDir), mTools (*this), mResDir(resDir),
mProjectPath ((configuration.getUserDataPath() / "projects") / mProjectPath ((configuration.getUserDataPath() / "projects") /
(savePath.filename().string() + ".project")), (savePath.filename().string() + ".project")),
mSaving (*this, mProjectPath, encoding) mSaving (*this, mProjectPath, encoding)
@ -2239,6 +2240,8 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration,
createBase(); createBase();
} }
mBlacklist.add (CSMWorld::UniversalId::Type_Script, blacklistedScripts);
addOptionalGmsts(); addOptionalGmsts();
addOptionalGlobals(); addOptionalGlobals();
@ -2358,6 +2361,13 @@ CSMTools::ReportModel *CSMDoc::Document::getReport (const CSMWorld::UniversalId&
return mTools.getReport (id); return mTools.getReport (id);
} }
bool CSMDoc::Document::isBlacklisted (const CSMWorld::UniversalId& id)
const
{
return mBlacklist.isBlacklisted (id);
}
void CSMDoc::Document::progress (int current, int max, int type) void CSMDoc::Document::progress (int current, int max, int type)
{ {
emit progress (current, max, type, 1, this); emit progress (current, max, type, 1, this);

@ -17,6 +17,7 @@
#include "state.hpp" #include "state.hpp"
#include "saving.hpp" #include "saving.hpp"
#include "blacklist.hpp"
class QAbstractItemModel; class QAbstractItemModel;
@ -52,6 +53,7 @@ namespace CSMDoc
boost::filesystem::path mProjectPath; boost::filesystem::path mProjectPath;
Saving mSaving; Saving mSaving;
boost::filesystem::path mResDir; boost::filesystem::path mResDir;
Blacklist mBlacklist;
// It is important that the undo stack is declared last, because on desctruction it fires a signal, that is connected to a slot, that is // It is important that the undo stack is declared last, because on desctruction it fires a signal, that is connected to a slot, that is
// using other member variables. Unfortunately this connection is cut only in the QObject destructor, which is way too late. // using other member variables. Unfortunately this connection is cut only in the QObject destructor, which is way too late.
@ -78,7 +80,8 @@ namespace CSMDoc
Document (const Files::ConfigurationManager& configuration, Document (const Files::ConfigurationManager& configuration,
const std::vector< boost::filesystem::path >& files, bool new_, const std::vector< boost::filesystem::path >& files, bool new_,
const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir,
ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager); ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager,
const std::vector<std::string>& blacklistedScripts);
~Document(); ~Document();
@ -110,6 +113,8 @@ namespace CSMDoc
CSMTools::ReportModel *getReport (const CSMWorld::UniversalId& id); CSMTools::ReportModel *getReport (const CSMWorld::UniversalId& id);
///< The ownership of the returned report is not transferred. ///< The ownership of the returned report is not transferred.
bool isBlacklisted (const CSMWorld::UniversalId& id) const;
signals: signals:
void stateChanged (int state, CSMDoc::Document *document); void stateChanged (int state, CSMDoc::Document *document);

@ -52,7 +52,7 @@ CSMDoc::DocumentManager::~DocumentManager()
void CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files, const boost::filesystem::path& savePath, void CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files, const boost::filesystem::path& savePath,
bool new_) bool new_)
{ {
Document *document = new Document (mConfiguration, files, new_, savePath, mResDir, mEncoding, mResourcesManager); Document *document = new Document (mConfiguration, files, new_, savePath, mResDir, mEncoding, mResourcesManager, mBlacklistedScripts);
mDocuments.push_back (document); mDocuments.push_back (document);
@ -85,6 +85,11 @@ void CSMDoc::DocumentManager::setEncoding (ToUTF8::FromType encoding)
mEncoding = encoding; mEncoding = encoding;
} }
void CSMDoc::DocumentManager::setBlacklistedScripts (const std::vector<std::string>& scriptIds)
{
mBlacklistedScripts = scriptIds;
}
void CSMDoc::DocumentManager::listResources() void CSMDoc::DocumentManager::listResources()
{ {
mResourcesManager.listResources(); mResourcesManager.listResources();

@ -34,6 +34,7 @@ namespace CSMDoc
Loader mLoader; Loader mLoader;
ToUTF8::FromType mEncoding; ToUTF8::FromType mEncoding;
CSMWorld::ResourcesManager mResourcesManager; CSMWorld::ResourcesManager mResourcesManager;
std::vector<std::string> mBlacklistedScripts;
DocumentManager (const DocumentManager&); DocumentManager (const DocumentManager&);
DocumentManager& operator= (const DocumentManager&); DocumentManager& operator= (const DocumentManager&);
@ -53,6 +54,8 @@ namespace CSMDoc
void setEncoding (ToUTF8::FromType encoding); void setEncoding (ToUTF8::FromType encoding);
void setBlacklistedScripts (const std::vector<std::string>& scriptIds);
/// Ask OGRE for a list of available resources. /// Ask OGRE for a list of available resources.
void listResources(); void listResources();

@ -7,6 +7,8 @@
#include <components/compiler/exception.hpp> #include <components/compiler/exception.hpp>
#include <components/compiler/extensions0.hpp> #include <components/compiler/extensions0.hpp>
#include "../doc/document.hpp"
#include "../world/data.hpp" #include "../world/data.hpp"
void CSMTools::ScriptCheckStage::report (const std::string& message, const Compiler::TokenLoc& loc, void CSMTools::ScriptCheckStage::report (const std::string& message, const Compiler::TokenLoc& loc,
@ -37,8 +39,8 @@ void CSMTools::ScriptCheckStage::report (const std::string& message, Type type)
(type==ErrorMessage ? "error: " : "warning: ") + message)); (type==ErrorMessage ? "error: " : "warning: ") + message));
} }
CSMTools::ScriptCheckStage::ScriptCheckStage (const CSMWorld::Data& data) CSMTools::ScriptCheckStage::ScriptCheckStage (const CSMDoc::Document& document)
: mData (data), mContext (data), mMessages (0) : mDocument (document), mContext (document.getData()), mMessages (0)
{ {
/// \todo add an option to configure warning mode /// \todo add an option to configure warning mode
setWarningsMode (0); setWarningsMode (0);
@ -53,18 +55,25 @@ int CSMTools::ScriptCheckStage::setup()
mMessages = 0; mMessages = 0;
mId.clear(); mId.clear();
return mData.getScripts().getSize(); return mDocument.getData().getScripts().getSize();
} }
void CSMTools::ScriptCheckStage::perform (int stage, Messages& messages) void CSMTools::ScriptCheckStage::perform (int stage, Messages& messages)
{ {
mId = mDocument.getData().getScripts().getId (stage);
if (mDocument.isBlacklisted (
CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Script, mId)))
return;
mMessages = &messages; mMessages = &messages;
mId = mData.getScripts().getId (stage);
try try
{ {
mFile = mData.getScripts().getRecord (stage).get().mId; const CSMWorld::Data& data = mDocument.getData();
std::istringstream input (mData.getScripts().getRecord (stage).get().mScriptText);
mFile = data.getScripts().getRecord (stage).get().mId;
std::istringstream input (data.getScripts().getRecord (stage).get().mScriptText);
Compiler::Scanner scanner (*this, input, mContext.getExtensions()); Compiler::Scanner scanner (*this, input, mContext.getExtensions());

@ -8,12 +8,17 @@
#include "../world/scriptcontext.hpp" #include "../world/scriptcontext.hpp"
namespace CSMDoc
{
class Document;
}
namespace CSMTools namespace CSMTools
{ {
/// \brief VerifyStage: make sure that scripts compile /// \brief VerifyStage: make sure that scripts compile
class ScriptCheckStage : public CSMDoc::Stage, private Compiler::ErrorHandler class ScriptCheckStage : public CSMDoc::Stage, private Compiler::ErrorHandler
{ {
const CSMWorld::Data& mData; const CSMDoc::Document& mDocument;
Compiler::Extensions mExtensions; Compiler::Extensions mExtensions;
CSMWorld::ScriptContext mContext; CSMWorld::ScriptContext mContext;
std::string mId; std::string mId;
@ -28,7 +33,7 @@ namespace CSMTools
public: public:
ScriptCheckStage (const CSMWorld::Data& data); ScriptCheckStage (const CSMDoc::Document& document);
virtual int setup(); virtual int setup();
///< \return number of steps ///< \return number of steps

@ -5,6 +5,7 @@
#include "../doc/state.hpp" #include "../doc/state.hpp"
#include "../doc/operation.hpp" #include "../doc/operation.hpp"
#include "../doc/document.hpp"
#include "../world/data.hpp" #include "../world/data.hpp"
#include "../world/universalid.hpp" #include "../world/universalid.hpp"
@ -80,13 +81,14 @@ CSMDoc::Operation *CSMTools::Tools::getVerifier()
mVerifier->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions())); mVerifier->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions()));
mVerifier->appendStage (new ScriptCheckStage (mData)); mVerifier->appendStage (new ScriptCheckStage (mDocument));
} }
return mVerifier; return mVerifier;
} }
CSMTools::Tools::Tools (CSMWorld::Data& data) : mData (data), mVerifier (0), mNextReportNumber (0) CSMTools::Tools::Tools (CSMDoc::Document& document)
: mDocument (document), mData (document.getData()), mVerifier (0), mNextReportNumber (0)
{ {
// index 0: load error log // index 0: load error log
mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel)); mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel));

@ -14,6 +14,7 @@ namespace CSMWorld
namespace CSMDoc namespace CSMDoc
{ {
class Operation; class Operation;
class Document;
} }
namespace CSMTools namespace CSMTools
@ -24,6 +25,7 @@ namespace CSMTools
{ {
Q_OBJECT Q_OBJECT
CSMDoc::Document& mDocument;
CSMWorld::Data& mData; CSMWorld::Data& mData;
CSMDoc::Operation *mVerifier; CSMDoc::Operation *mVerifier;
std::map<int, ReportModel *> mReports; std::map<int, ReportModel *> mReports;
@ -44,7 +46,7 @@ namespace CSMTools
public: public:
Tools (CSMWorld::Data& data); Tools (CSMDoc::Document& document);
virtual ~Tools(); virtual ~Tools();

@ -180,6 +180,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
, mEncoder(NULL) , mEncoder(NULL)
, mActivationDistanceOverride(-1) , mActivationDistanceOverride(-1)
, mGrab(true) , mGrab(true)
, mScriptBlacklistUse (true)
{ {
std::srand ( std::time(NULL) ); std::srand ( std::time(NULL) );
@ -406,7 +407,8 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
mScriptContext->setExtensions (&mExtensions); mScriptContext->setExtensions (&mExtensions);
mEnvironment.setScriptManager (new MWScript::ScriptManager (MWBase::Environment::get().getWorld()->getStore(), mEnvironment.setScriptManager (new MWScript::ScriptManager (MWBase::Environment::get().getWorld()->getStore(),
mVerboseScripts, *mScriptContext, mWarningsMode)); mVerboseScripts, *mScriptContext, mWarningsMode,
mScriptBlacklistUse ? mScriptBlacklist : std::vector<std::string>()));
// Create game mechanics system // Create game mechanics system
MWMechanics::MechanicsManager* mechanics = new MWMechanics::MechanicsManager; MWMechanics::MechanicsManager* mechanics = new MWMechanics::MechanicsManager;
@ -565,3 +567,13 @@ void OMW::Engine::setWarningsMode (int mode)
{ {
mWarningsMode = mode; mWarningsMode = mode;
} }
void OMW::Engine::setScriptBlacklist (const std::vector<std::string>& list)
{
mScriptBlacklist = list;
}
void OMW::Engine::setScriptBlacklistUse (bool use)
{
mScriptBlacklistUse = use;
}

@ -89,6 +89,8 @@ namespace OMW
Files::Collections mFileCollections; Files::Collections mFileCollections;
bool mFSStrict; bool mFSStrict;
Translation::Storage mTranslationDataStorage; Translation::Storage mTranslationDataStorage;
std::vector<std::string> mScriptBlacklist;
bool mScriptBlacklistUse;
// not implemented // not implemented
Engine (const Engine&); Engine (const Engine&);
@ -181,6 +183,10 @@ namespace OMW
void setWarningsMode (int mode); void setWarningsMode (int mode);
void setScriptBlacklist (const std::vector<std::string>& list);
void setScriptBlacklistUse (bool use);
private: private:
Files::ConfigurationManager& mCfgMgr; Files::ConfigurationManager& mCfgMgr;
}; };

@ -144,6 +144,12 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
"\t1 - show warning but consider script as correctly compiled anyway\n" "\t1 - show warning but consider script as correctly compiled anyway\n"
"\t2 - treat warnings as errors") "\t2 - treat warnings as errors")
("script-blacklist", bpo::value<StringsVector>()->default_value(StringsVector(), "")
->multitoken(), "ignore the specified script (if the use of the blacklist is enabled)")
("script-blacklist-use", bpo::value<bool>()->implicit_value(true)
->default_value(true), "enable script blacklisting")
("skip-menu", bpo::value<bool>()->implicit_value(true) ("skip-menu", bpo::value<bool>()->implicit_value(true)
->default_value(false), "skip main menu on game startup") ->default_value(false), "skip main menu on game startup")
@ -241,15 +247,19 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
engine.setCell(variables["start"].as<std::string>()); engine.setCell(variables["start"].as<std::string>());
engine.setSkipMenu (variables["skip-menu"].as<bool>()); engine.setSkipMenu (variables["skip-menu"].as<bool>());
// other settings // scripts
engine.setSoundUsage(!variables["no-sound"].as<bool>());
engine.setScriptsVerbosity(variables["script-verbose"].as<bool>());
engine.setCompileAll(variables["script-all"].as<bool>()); engine.setCompileAll(variables["script-all"].as<bool>());
engine.setFallbackValues(variables["fallback"].as<FallbackMap>().mMap); engine.setScriptsVerbosity(variables["script-verbose"].as<bool>());
engine.setScriptConsoleMode (variables["script-console"].as<bool>()); engine.setScriptConsoleMode (variables["script-console"].as<bool>());
engine.setStartupScript (variables["script-run"].as<std::string>()); engine.setStartupScript (variables["script-run"].as<std::string>());
engine.setActivationDistanceOverride (variables["activate-dist"].as<int>());
engine.setWarningsMode (variables["script-warn"].as<int>()); engine.setWarningsMode (variables["script-warn"].as<int>());
engine.setScriptBlacklist (variables["script-blacklist"].as<StringsVector>());
engine.setScriptBlacklistUse (variables["script-blacklist-use"].as<bool>());
// other settings
engine.setSoundUsage(!variables["no-sound"].as<bool>());
engine.setFallbackValues(variables["fallback"].as<FallbackMap>().mMap);
engine.setActivationDistanceOverride (variables["activate-dist"].as<int>());
return true; return true;
} }

@ -46,17 +46,11 @@ namespace MWBase
///< Compile all scripts ///< Compile all scripts
/// \return count, success /// \return count, success
virtual Compiler::Locals& getLocals (const std::string& name) = 0; virtual const Compiler::Locals& getLocals (const std::string& name) = 0;
///< Return locals for script \a name. ///< Return locals for script \a name.
virtual MWScript::GlobalScripts& getGlobalScripts() = 0; virtual MWScript::GlobalScripts& getGlobalScripts() = 0;
};
virtual int getLocalIndex (const std::string& scriptId, const std::string& variable,
char type) = 0;
///< Return index of the variable of the given name and type in the given script. Will
/// throw an exception, if there is no such script or variable or the type does not match.
};
} }
#endif #endif

@ -25,6 +25,11 @@
namespace MWClass namespace MWClass
{ {
std::string Activator::getId (const MWWorld::Ptr& ptr) const
{
return ptr.get<ESM::Activator>()->mBase->mId;
}
void Activator::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const void Activator::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{ {
const std::string model = getModel(ptr); const std::string model = getModel(ptr);

@ -13,6 +13,9 @@ namespace MWClass
public: public:
/// Return ID of \a ptr
virtual std::string getId (const MWWorld::Ptr& ptr) const;
virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const;
///< Add reference into a cell for rendering ///< Add reference into a cell for rendering

@ -21,6 +21,11 @@
namespace MWClass namespace MWClass
{ {
std::string Apparatus::getId (const MWWorld::Ptr& ptr) const
{
return ptr.get<ESM::Apparatus>()->mBase->mId;
}
void Apparatus::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const void Apparatus::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{ {
const std::string model = getModel(ptr); const std::string model = getModel(ptr);

@ -13,6 +13,9 @@ namespace MWClass
public: public:
/// Return ID of \a ptr
virtual std::string getId (const MWWorld::Ptr& ptr) const;
virtual float getWeight (const MWWorld::Ptr& ptr) const; virtual float getWeight (const MWWorld::Ptr& ptr) const;
virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const;

@ -25,6 +25,11 @@
namespace MWClass namespace MWClass
{ {
std::string Armor::getId (const MWWorld::Ptr& ptr) const
{
return ptr.get<ESM::Armor>()->mBase->mId;
}
void Armor::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const void Armor::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{ {
const std::string model = getModel(ptr); const std::string model = getModel(ptr);

@ -12,6 +12,9 @@ namespace MWClass
public: public:
/// Return ID of \a ptr
virtual std::string getId (const MWWorld::Ptr& ptr) const;
virtual float getWeight (const MWWorld::Ptr& ptr) const; virtual float getWeight (const MWWorld::Ptr& ptr) const;
virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const;

@ -22,6 +22,11 @@
namespace MWClass namespace MWClass
{ {
std::string Book::getId (const MWWorld::Ptr& ptr) const
{
return ptr.get<ESM::Book>()->mBase->mId;
}
void Book::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const void Book::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{ {
const std::string model = getModel(ptr); const std::string model = getModel(ptr);

@ -12,6 +12,9 @@ namespace MWClass
public: public:
/// Return ID of \a ptr
virtual std::string getId (const MWWorld::Ptr& ptr) const;
virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const;
///< Add reference into a cell for rendering ///< Add reference into a cell for rendering

@ -22,6 +22,11 @@
namespace MWClass namespace MWClass
{ {
std::string Clothing::getId (const MWWorld::Ptr& ptr) const
{
return ptr.get<ESM::Clothing>()->mBase->mId;
}
void Clothing::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const void Clothing::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{ {
const std::string model = getModel(ptr); const std::string model = getModel(ptr);

@ -12,6 +12,9 @@ namespace MWClass
public: public:
/// Return ID of \a ptr
virtual std::string getId (const MWWorld::Ptr& ptr) const;
virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const;
///< Add reference into a cell for rendering ///< Add reference into a cell for rendering

@ -43,6 +43,11 @@ namespace
namespace MWClass namespace MWClass
{ {
std::string Container::getId (const MWWorld::Ptr& ptr) const
{
return ptr.get<ESM::Container>()->mBase->mId;
}
void Container::ensureCustomData (const MWWorld::Ptr& ptr) const void Container::ensureCustomData (const MWWorld::Ptr& ptr) const
{ {
if (!ptr.getRefData().getCustomData()) if (!ptr.getRefData().getCustomData())

@ -15,6 +15,9 @@ namespace MWClass
public: public:
/// Return ID of \a ptr
virtual std::string getId (const MWWorld::Ptr& ptr) const;
virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const;
///< Add reference into a cell for rendering ///< Add reference into a cell for rendering

@ -27,6 +27,11 @@ namespace
namespace MWClass namespace MWClass
{ {
std::string CreatureLevList::getId (const MWWorld::Ptr& ptr) const
{
return ptr.get<ESM::CreatureLevList>()->mBase->mId;
}
std::string CreatureLevList::getName (const MWWorld::Ptr& ptr) const std::string CreatureLevList::getName (const MWWorld::Ptr& ptr) const
{ {
return ""; return "";

@ -11,6 +11,9 @@ namespace MWClass
public: public:
/// Return ID of \a ptr
virtual std::string getId (const MWWorld::Ptr& ptr) const;
virtual std::string getName (const MWWorld::Ptr& ptr) const; virtual std::string getName (const MWWorld::Ptr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name (the one that is to be presented to the user; not the internal one);
/// can return an empty string. /// can return an empty string.

@ -42,6 +42,11 @@ namespace
namespace MWClass namespace MWClass
{ {
std::string Door::getId (const MWWorld::Ptr& ptr) const
{
return ptr.get<ESM::Door>()->mBase->mId;
}
void Door::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const void Door::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{ {
const std::string model = getModel(ptr); const std::string model = getModel(ptr);

@ -16,6 +16,9 @@ namespace MWClass
public: public:
/// Return ID of \a ptr
virtual std::string getId (const MWWorld::Ptr& ptr) const;
virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const;
///< Add reference into a cell for rendering ///< Add reference into a cell for rendering

@ -5,6 +5,11 @@
namespace MWClass namespace MWClass
{ {
std::string ItemLevList::getId (const MWWorld::Ptr& ptr) const
{
return ptr.get<ESM::ItemLevList>()->mBase->mId;
}
std::string ItemLevList::getName (const MWWorld::Ptr& ptr) const std::string ItemLevList::getName (const MWWorld::Ptr& ptr) const
{ {
return ""; return "";

@ -9,6 +9,9 @@ namespace MWClass
{ {
public: public:
/// Return ID of \a ptr
virtual std::string getId (const MWWorld::Ptr& ptr) const;
virtual std::string getName (const MWWorld::Ptr& ptr) const; virtual std::string getName (const MWWorld::Ptr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name (the one that is to be presented to the user; not the internal one);
/// can return an empty string. /// can return an empty string.

@ -47,6 +47,11 @@ namespace
namespace MWClass namespace MWClass
{ {
std::string Light::getId (const MWWorld::Ptr& ptr) const
{
return ptr.get<ESM::Light>()->mBase->mId;
}
void Light::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const void Light::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{ {
const std::string model = getModel(ptr); const std::string model = getModel(ptr);

@ -14,6 +14,9 @@ namespace MWClass
public: public:
/// Return ID of \a ptr
virtual std::string getId (const MWWorld::Ptr& ptr) const;
virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const;
///< Add reference into a cell for rendering ///< Add reference into a cell for rendering

@ -22,6 +22,11 @@
namespace MWClass namespace MWClass
{ {
std::string Lockpick::getId (const MWWorld::Ptr& ptr) const
{
return ptr.get<ESM::Lockpick>()->mBase->mId;
}
void Lockpick::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const void Lockpick::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{ {
const std::string model = getModel(ptr); const std::string model = getModel(ptr);

@ -12,6 +12,9 @@ namespace MWClass
public: public:
/// Return ID of \a ptr
virtual std::string getId (const MWWorld::Ptr& ptr) const;
virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const;
///< Add reference into a cell for rendering ///< Add reference into a cell for rendering

@ -38,6 +38,11 @@ bool isGold (const MWWorld::Ptr& ptr)
namespace MWClass namespace MWClass
{ {
std::string Miscellaneous::getId (const MWWorld::Ptr& ptr) const
{
return ptr.get<ESM::Miscellaneous>()->mBase->mId;
}
void Miscellaneous::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const void Miscellaneous::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{ {
const std::string model = getModel(ptr); const std::string model = getModel(ptr);

@ -12,6 +12,9 @@ namespace MWClass
public: public:
/// Return ID of \a ptr
virtual std::string getId (const MWWorld::Ptr& ptr) const;
virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const;
///< Add reference into a cell for rendering ///< Add reference into a cell for rendering

@ -24,6 +24,11 @@
namespace MWClass namespace MWClass
{ {
std::string Potion::getId (const MWWorld::Ptr& ptr) const
{
return ptr.get<ESM::Potion>()->mBase->mId;
}
void Potion::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const void Potion::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{ {
const std::string model = getModel(ptr); const std::string model = getModel(ptr);

@ -12,6 +12,9 @@ namespace MWClass
public: public:
/// Return ID of \a ptr
virtual std::string getId (const MWWorld::Ptr& ptr) const;
virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const;
///< Add reference into a cell for rendering ///< Add reference into a cell for rendering

@ -22,6 +22,11 @@
namespace MWClass namespace MWClass
{ {
std::string Probe::getId (const MWWorld::Ptr& ptr) const
{
return ptr.get<ESM::Probe>()->mBase->mId;
}
void Probe::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const void Probe::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{ {
const std::string model = getModel(ptr); const std::string model = getModel(ptr);

@ -12,6 +12,9 @@ namespace MWClass
public: public:
/// Return ID of \a ptr
virtual std::string getId (const MWWorld::Ptr& ptr) const;
virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const;
///< Add reference into a cell for rendering ///< Add reference into a cell for rendering

@ -21,6 +21,11 @@
namespace MWClass namespace MWClass
{ {
std::string Repair::getId (const MWWorld::Ptr& ptr) const
{
return ptr.get<ESM::Repair>()->mBase->mId;
}
void Repair::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const void Repair::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{ {
const std::string model = getModel(ptr); const std::string model = getModel(ptr);

@ -12,6 +12,9 @@ namespace MWClass
public: public:
/// Return ID of \a ptr
virtual std::string getId (const MWWorld::Ptr& ptr) const;
virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const;
///< Add reference into a cell for rendering ///< Add reference into a cell for rendering

@ -12,6 +12,11 @@
namespace MWClass namespace MWClass
{ {
std::string Static::getId (const MWWorld::Ptr& ptr) const
{
return ptr.get<ESM::Static>()->mBase->mId;
}
void Static::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const void Static::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{ {
MWWorld::LiveCellRef<ESM::Static> *ref = MWWorld::LiveCellRef<ESM::Static> *ref =

@ -12,6 +12,9 @@ namespace MWClass
public: public:
/// Return ID of \a ptr
virtual std::string getId (const MWWorld::Ptr& ptr) const;
virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const;
///< Add reference into a cell for rendering ///< Add reference into a cell for rendering

@ -1,11 +1,14 @@
#include "filter.hpp" #include "filter.hpp"
#include <components/compiler/locals.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/journal.hpp" #include "../mwbase/journal.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/dialoguemanager.hpp" #include "../mwbase/dialoguemanager.hpp"
#include "../mwbase/scriptmanager.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
@ -197,33 +200,28 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c
if (scriptName.empty()) if (scriptName.empty())
return false; // no script return false; // no script
const ESM::Script *script = std::string name = Misc::StringUtils::lowerCase (select.getName());
MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().find (scriptName);
std::string name = select.getName(); const Compiler::Locals& localDefs =
MWBase::Environment::get().getScriptManager()->getLocals (scriptName);
int i = 0; char type = localDefs.getType (name);
for (; i<static_cast<int> (script->mVarNames.size()); ++i) if (type==' ')
if (Misc::StringUtils::ciEqual(script->mVarNames[i], name)) return false; // script does not have a variable of this name.
break;
if (i>=static_cast<int> (script->mVarNames.size())) int index = localDefs.getIndex (name);
return false; // script does not have a variable of this name
const MWScript::Locals& locals = mActor.getRefData().getLocals(); const MWScript::Locals& locals = mActor.getRefData().getLocals();
if (i<script->mData.mNumShorts) switch (type)
return select.selectCompare (static_cast<int> (locals.mShorts[i])); {
case 's': return select.selectCompare (static_cast<int> (locals.mShorts[index]));
i -= script->mData.mNumShorts; case 'l': return select.selectCompare (locals.mLongs[index]);
case 'f': return select.selectCompare (locals.mFloats[index]);
if (i<script->mData.mNumLongs) }
return select.selectCompare (locals.mLongs[i]);
i -= script->mData.mNumLongs;
return select.selectCompare (locals.mFloats.at (i)); throw std::logic_error ("unknown local variable type in dialogue filter");
} }
case SelectWrapper::Function_PcHealthPercent: case SelectWrapper::Function_PcHealthPercent:
@ -463,20 +461,10 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
// This actor has no attached script, so there is no local variable // This actor has no attached script, so there is no local variable
return true; return true;
const ESM::Script *script = const Compiler::Locals& localDefs =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().find (scriptName); MWBase::Environment::get().getScriptManager()->getLocals (scriptName);
std::string name = select.getName();
int i = 0;
for (; i < static_cast<int> (script->mVarNames.size()); ++i)
if (Misc::StringUtils::ciEqual(script->mVarNames[i], name))
break;
if (i >= static_cast<int> (script->mVarNames.size()))
return true; // script does not have a variable of this name
return false; return localDefs.getIndex (Misc::StringUtils::lowerCase (select.getName()))==-1;
} }
case SelectWrapper::Function_SameGender: case SelectWrapper::Function_SameGender:

@ -15,61 +15,69 @@
namespace MWScript namespace MWScript
{ {
GlobalScriptDesc::GlobalScriptDesc() : mRunning (false) {}
GlobalScripts::GlobalScripts (const MWWorld::ESMStore& store) GlobalScripts::GlobalScripts (const MWWorld::ESMStore& store)
: mStore (store) : mStore (store)
{ {}
addStartup();
}
void GlobalScripts::addScript (const std::string& name) void GlobalScripts::addScript (const std::string& name, const std::string& targetId)
{ {
std::map<std::string, std::pair<bool, Locals> >::iterator iter = std::map<std::string, GlobalScriptDesc>::iterator 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>().find (name)) if (const ESM::Script *script = mStore.get<ESM::Script>().find (name))
{ {
Locals locals; GlobalScriptDesc desc;
desc.mRunning = true;
locals.configure (*script); desc.mLocals.configure (*script);
desc.mId = targetId;
mScripts.insert (std::make_pair (name, std::make_pair (true, locals))); mScripts.insert (std::make_pair (name, desc));
} }
} }
else else if (!iter->second.mRunning)
iter->second.first = true; {
iter->second.mRunning = true;
iter->second.mId = targetId;
}
} }
void GlobalScripts::removeScript (const std::string& name) void GlobalScripts::removeScript (const std::string& name)
{ {
std::map<std::string, std::pair<bool, Locals> >::iterator iter = std::map<std::string, GlobalScriptDesc>::iterator iter =
mScripts.find (::Misc::StringUtils::lowerCase (name)); mScripts.find (::Misc::StringUtils::lowerCase (name));
if (iter!=mScripts.end()) if (iter!=mScripts.end())
iter->second.first = 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, std::pair<bool, Locals> >::const_iterator iter = std::map<std::string, GlobalScriptDesc>::const_iterator 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.first; return iter->second.mRunning;
} }
void GlobalScripts::run() void GlobalScripts::run()
{ {
for (std::map<std::string, std::pair<bool, Locals> >::iterator iter (mScripts.begin()); for (std::map<std::string, GlobalScriptDesc>::iterator iter (mScripts.begin());
iter!=mScripts.end(); ++iter) iter!=mScripts.end(); ++iter)
{ {
if (iter->second.first) if (iter->second.mRunning)
{ {
MWWorld::Ptr ptr;
MWScript::InterpreterContext interpreterContext ( MWScript::InterpreterContext interpreterContext (
&iter->second.second, MWWorld::Ptr()); &iter->second.mLocals, MWWorld::Ptr(), iter->second.mId);
MWBase::Environment::get().getScriptManager()->run (iter->first, interpreterContext); MWBase::Environment::get().getScriptManager()->run (iter->first, interpreterContext);
} }
} }
@ -99,16 +107,18 @@ 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, std::pair<bool, Locals> >::const_iterator iter (mScripts.begin()); for (std::map<std::string, GlobalScriptDesc>::const_iterator iter (mScripts.begin());
iter!=mScripts.end(); ++iter) iter!=mScripts.end(); ++iter)
{ {
ESM::GlobalScript script; ESM::GlobalScript script;
script.mId = iter->first; script.mId = iter->first;
iter->second.second.write (script.mLocals, iter->first); iter->second.mLocals.write (script.mLocals, iter->first);
script.mRunning = iter->second.first ? 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);
@ -124,25 +134,25 @@ namespace MWScript
ESM::GlobalScript script; ESM::GlobalScript script;
script.load (reader); script.load (reader);
std::map<std::string, std::pair<bool, Locals> >::iterator iter = std::map<std::string, GlobalScriptDesc>::iterator iter =
mScripts.find (script.mId); mScripts.find (script.mId);
if (iter==mScripts.end()) if (iter==mScripts.end())
{ {
if (const ESM::Script *scriptRecord = mStore.get<ESM::Script>().search (script.mId)) if (const ESM::Script *scriptRecord = mStore.get<ESM::Script>().search (script.mId))
{ {
std::pair<bool, Locals> data (false, Locals()); GlobalScriptDesc desc;
desc.mLocals.configure (*scriptRecord);
data.second.configure (*scriptRecord);
iter = mScripts.insert (std::make_pair (script.mId, data)).first; iter = mScripts.insert (std::make_pair (script.mId, desc)).first;
} }
else // script does not exist anymore else // script does not exist anymore
return true; return true;
} }
iter->second.first = script.mRunning!=0; iter->second.mRunning = script.mRunning!=0;
iter->second.second.read (script.mLocals, script.mId); iter->second.mLocals.read (script.mLocals, script.mId);
iter->second.mId = script.mTargetId;
return true; return true;
} }
@ -153,21 +163,19 @@ 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, std::pair<bool, Locals> >::iterator iter = std::map<std::string, GlobalScriptDesc>::iterator iter = mScripts.find (name2);
mScripts.find (name2);
if (iter==mScripts.end()) if (iter==mScripts.end())
{ {
if (const ESM::Script *script = mStore.get<ESM::Script>().find (name)) if (const ESM::Script *script = mStore.get<ESM::Script>().find (name))
{ {
Locals locals; GlobalScriptDesc desc;
desc.mLocals.configure (*script);
locals.configure (*script);
iter = mScripts.insert (std::make_pair (name, std::make_pair (false, locals))).first; iter = mScripts.insert (std::make_pair (name, desc)).first;
} }
} }
return iter->second.second; return iter->second.mLocals;
} }
} }

@ -26,16 +26,25 @@ namespace MWWorld
namespace MWScript namespace MWScript
{ {
struct GlobalScriptDesc
{
bool mRunning;
Locals mLocals;
std::string mId; // ID used to start targeted script (empty if not a targeted script)
GlobalScriptDesc();
};
class GlobalScripts class GlobalScripts
{ {
const MWWorld::ESMStore& mStore; const MWWorld::ESMStore& mStore;
std::map<std::string, std::pair<bool, Locals> > mScripts; // running, local variables std::map<std::string, GlobalScriptDesc> mScripts;
public: public:
GlobalScripts (const MWWorld::ESMStore& store); GlobalScripts (const MWWorld::ESMStore& store);
void addScript (const std::string& name); void addScript (const std::string& name, const std::string& targetId = "");
void removeScript (const std::string& name); void removeScript (const std::string& name);

@ -3,8 +3,12 @@
#include <cmath> #include <cmath>
#include <stdexcept> #include <stdexcept>
#include <sstream>
#include <components/interpreter/types.hpp> #include <components/interpreter/types.hpp>
#include <components/compiler/locals.hpp>
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -22,7 +26,7 @@
namespace MWScript namespace MWScript
{ {
MWWorld::Ptr InterpreterContext::getReference ( MWWorld::Ptr InterpreterContext::getReferenceImp (
const std::string& id, bool activeOnly, bool doThrow) const std::string& id, bool activeOnly, bool doThrow)
{ {
if (!id.empty()) if (!id.empty())
@ -31,6 +35,10 @@ namespace MWScript
} }
else else
{ {
if (mReference.isEmpty() && !mTargetId.empty())
mReference =
MWBase::Environment::get().getWorld()->searchPtr (mTargetId, false);
if (mReference.isEmpty() && doThrow) if (mReference.isEmpty() && doThrow)
throw std::runtime_error ("no implicit reference"); throw std::runtime_error ("no implicit reference");
@ -38,7 +46,7 @@ namespace MWScript
} }
} }
const MWWorld::Ptr InterpreterContext::getReference ( const MWWorld::Ptr InterpreterContext::getReferenceImp (
const std::string& id, bool activeOnly, bool doThrow) const const std::string& id, bool activeOnly, bool doThrow) const
{ {
if (!id.empty()) if (!id.empty())
@ -47,6 +55,10 @@ namespace MWScript
} }
else else
{ {
if (mReference.isEmpty() && !mTargetId.empty())
mReference =
MWBase::Environment::get().getWorld()->searchPtr (mTargetId, false);
if (mReference.isEmpty() && doThrow) if (mReference.isEmpty() && doThrow)
throw std::runtime_error ("no implicit reference"); throw std::runtime_error ("no implicit reference");
@ -64,7 +76,7 @@ namespace MWScript
} }
else else
{ {
const MWWorld::Ptr ptr = getReference (id, false); const MWWorld::Ptr ptr = getReferenceImp (id, false);
id = ptr.getClass().getScript (ptr); id = ptr.getClass().getScript (ptr);
@ -84,7 +96,7 @@ namespace MWScript
} }
else else
{ {
const MWWorld::Ptr ptr = getReference (id, false); const MWWorld::Ptr ptr = getReferenceImp (id, false);
id = ptr.getClass().getScript (ptr); id = ptr.getClass().getScript (ptr);
@ -95,11 +107,43 @@ namespace MWScript
} }
} }
int InterpreterContext::findLocalVariableIndex (const std::string& scriptId,
const std::string& name, char type) const
{
int index = MWBase::Environment::get().getScriptManager()->getLocals (scriptId).
search (type, name);
if (index!=-1)
return index;
std::ostringstream stream;
stream << "Failed to access ";
switch (type)
{
case 's': stream << "short"; break;
case 'l': stream << "long"; break;
case 'f': stream << "float"; break;
}
stream << " member variable " << name << " in script " << scriptId;
throw std::runtime_error (stream.str().c_str());
}
InterpreterContext::InterpreterContext ( InterpreterContext::InterpreterContext (
MWScript::Locals *locals, MWWorld::Ptr reference) MWScript::Locals *locals, MWWorld::Ptr reference, const std::string& targetId)
: mLocals (locals), mReference (reference), : mLocals (locals), mReference (reference),
mActivationHandled (false) mActivationHandled (false), mTargetId (targetId)
{} {
// 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.getClass().getId (reference);
}
int InterpreterContext::getLocalShort (int index) const int InterpreterContext::getLocalShort (int index) const
{ {
@ -236,34 +280,34 @@ namespace MWScript
std::string InterpreterContext::getNPCName() const std::string InterpreterContext::getNPCName() const
{ {
ESM::NPC npc = *mReference.get<ESM::NPC>()->mBase; ESM::NPC npc = *getReferenceImp().get<ESM::NPC>()->mBase;
return npc.mName; return npc.mName;
} }
std::string InterpreterContext::getNPCRace() const std::string InterpreterContext::getNPCRace() const
{ {
ESM::NPC npc = *mReference.get<ESM::NPC>()->mBase; ESM::NPC npc = *getReferenceImp().get<ESM::NPC>()->mBase;
const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(npc.mRace); const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(npc.mRace);
return race->mName; return race->mName;
} }
std::string InterpreterContext::getNPCClass() const std::string InterpreterContext::getNPCClass() const
{ {
ESM::NPC npc = *mReference.get<ESM::NPC>()->mBase; ESM::NPC npc = *getReferenceImp().get<ESM::NPC>()->mBase;
const ESM::Class* class_ = MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().find(npc.mClass); const ESM::Class* class_ = MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().find(npc.mClass);
return class_->mName; return class_->mName;
} }
std::string InterpreterContext::getNPCFaction() const std::string InterpreterContext::getNPCFaction() const
{ {
ESM::NPC npc = *mReference.get<ESM::NPC>()->mBase; ESM::NPC npc = *getReferenceImp().get<ESM::NPC>()->mBase;
const ESM::Faction* faction = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(npc.mFaction); const ESM::Faction* faction = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(npc.mFaction);
return faction->mName; return faction->mName;
} }
std::string InterpreterContext::getNPCRank() const std::string InterpreterContext::getNPCRank() const
{ {
std::map<std::string, int> ranks = mReference.getClass().getNpcStats (mReference).getFactionRanks(); std::map<std::string, int> ranks = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks();
std::map<std::string, int>::const_iterator it = ranks.begin(); std::map<std::string, int>::const_iterator it = ranks.begin();
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
@ -299,7 +343,7 @@ namespace MWScript
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
MWWorld::Ptr player = world->getPlayerPtr(); MWWorld::Ptr player = world->getPlayerPtr();
std::string factionId = mReference.getClass().getNpcStats (mReference).getFactionRanks().begin()->first; std::string factionId = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks().begin()->first;
std::map<std::string, int> ranks = player.getClass().getNpcStats (player).getFactionRanks(); std::map<std::string, int> ranks = player.getClass().getNpcStats (player).getFactionRanks();
std::map<std::string, int>::const_iterator it = ranks.find(factionId); std::map<std::string, int>::const_iterator it = ranks.find(factionId);
@ -326,7 +370,7 @@ namespace MWScript
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
MWWorld::Ptr player = world->getPlayerPtr(); MWWorld::Ptr player = world->getPlayerPtr();
std::string factionId = mReference.getClass().getNpcStats (mReference).getFactionRanks().begin()->first; std::string factionId = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks().begin()->first;
std::map<std::string, int> ranks = player.getClass().getNpcStats (player).getFactionRanks(); std::map<std::string, int> ranks = player.getClass().getNpcStats (player).getFactionRanks();
std::map<std::string, int>::const_iterator it = ranks.find(factionId); std::map<std::string, int>::const_iterator it = ranks.find(factionId);
@ -366,9 +410,9 @@ namespace MWScript
return MWBase::Environment::get().getScriptManager()->getGlobalScripts().isRunning (name); return MWBase::Environment::get().getScriptManager()->getGlobalScripts().isRunning (name);
} }
void InterpreterContext::startScript (const std::string& name) void InterpreterContext::startScript (const std::string& name, const std::string& targetId)
{ {
MWBase::Environment::get().getScriptManager()->getGlobalScripts().addScript (name); MWBase::Environment::get().getScriptManager()->getGlobalScripts().addScript (name, targetId);
} }
void InterpreterContext::stopScript (const std::string& name) void InterpreterContext::stopScript (const std::string& name)
@ -383,7 +427,7 @@ namespace MWScript
MWWorld::Ptr ref2; MWWorld::Ptr ref2;
if (id.empty()) if (id.empty())
ref2 = getReference("", true, true); ref2 = getReferenceImp();
else else
ref2 = MWBase::Environment::get().getWorld()->searchPtr(id, true); ref2 = MWBase::Environment::get().getWorld()->searchPtr(id, true);
@ -448,19 +492,19 @@ namespace MWScript
bool InterpreterContext::isDisabled (const std::string& id) const bool InterpreterContext::isDisabled (const std::string& id) const
{ {
const MWWorld::Ptr ref = getReference (id, false); const MWWorld::Ptr ref = getReferenceImp (id, false);
return !ref.getRefData().isEnabled(); return !ref.getRefData().isEnabled();
} }
void InterpreterContext::enable (const std::string& id) void InterpreterContext::enable (const std::string& id)
{ {
MWWorld::Ptr ref = getReference (id, false); MWWorld::Ptr ref = getReferenceImp (id, false);
MWBase::Environment::get().getWorld()->enable (ref); MWBase::Environment::get().getWorld()->enable (ref);
} }
void InterpreterContext::disable (const std::string& id) void InterpreterContext::disable (const std::string& id)
{ {
MWWorld::Ptr ref = getReference (id, false); MWWorld::Ptr ref = getReferenceImp (id, false);
MWBase::Environment::get().getWorld()->disable (ref); MWBase::Environment::get().getWorld()->disable (ref);
} }
@ -471,10 +515,7 @@ namespace MWScript
const Locals& locals = getMemberLocals (scriptId, global); const Locals& locals = getMemberLocals (scriptId, global);
int index = MWBase::Environment::get().getScriptManager()->getLocalIndex ( return locals.mShorts[findLocalVariableIndex (scriptId, name, 's')];
scriptId, name, 's');
return locals.mShorts[index];
} }
int InterpreterContext::getMemberLong (const std::string& id, const std::string& name, int InterpreterContext::getMemberLong (const std::string& id, const std::string& name,
@ -484,10 +525,7 @@ namespace MWScript
const Locals& locals = getMemberLocals (scriptId, global); const Locals& locals = getMemberLocals (scriptId, global);
int index = MWBase::Environment::get().getScriptManager()->getLocalIndex ( return locals.mLongs[findLocalVariableIndex (scriptId, name, 'l')];
scriptId, name, 'l');
return locals.mLongs[index];
} }
float InterpreterContext::getMemberFloat (const std::string& id, const std::string& name, float InterpreterContext::getMemberFloat (const std::string& id, const std::string& name,
@ -497,10 +535,7 @@ namespace MWScript
const Locals& locals = getMemberLocals (scriptId, global); const Locals& locals = getMemberLocals (scriptId, global);
int index = MWBase::Environment::get().getScriptManager()->getLocalIndex ( return locals.mFloats[findLocalVariableIndex (scriptId, name, 'f')];
scriptId, name, 'f');
return locals.mFloats[index];
} }
void InterpreterContext::setMemberShort (const std::string& id, const std::string& name, void InterpreterContext::setMemberShort (const std::string& id, const std::string& name,
@ -510,10 +545,7 @@ namespace MWScript
Locals& locals = getMemberLocals (scriptId, global); Locals& locals = getMemberLocals (scriptId, global);
int index = locals.mShorts[findLocalVariableIndex (scriptId, name, 's')] = value;
MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 's');
locals.mShorts[index] = value;
} }
void InterpreterContext::setMemberLong (const std::string& id, const std::string& name, int value, bool global) void InterpreterContext::setMemberLong (const std::string& id, const std::string& name, int value, bool global)
@ -522,10 +554,7 @@ namespace MWScript
Locals& locals = getMemberLocals (scriptId, global); Locals& locals = getMemberLocals (scriptId, global);
int index = locals.mLongs[findLocalVariableIndex (scriptId, name, 'l')] = value;
MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 'l');
locals.mLongs[index] = value;
} }
void InterpreterContext::setMemberFloat (const std::string& id, const std::string& name, float value, bool global) void InterpreterContext::setMemberFloat (const std::string& id, const std::string& name, float value, bool global)
@ -534,14 +563,16 @@ namespace MWScript
Locals& locals = getMemberLocals (scriptId, global); Locals& locals = getMemberLocals (scriptId, global);
int index = locals.mFloats[findLocalVariableIndex (scriptId, name, 'f')] = value;
MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 'f');
locals.mFloats[index] = value;
} }
MWWorld::Ptr InterpreterContext::getReference(bool required) MWWorld::Ptr InterpreterContext::getReference(bool required)
{ {
return getReference ("", true, required); return getReferenceImp ("", true, required);
}
std::string InterpreterContext::getTargetId() const
{
return mTargetId;
} }
} }

@ -27,14 +27,22 @@ namespace MWScript
class InterpreterContext : public Interpreter::Context class InterpreterContext : public Interpreter::Context
{ {
Locals *mLocals; Locals *mLocals;
MWWorld::Ptr mReference; mutable MWWorld::Ptr mReference;
MWWorld::Ptr mActivated; MWWorld::Ptr mActivated;
bool mActivationHandled; bool mActivationHandled;
MWWorld::Ptr getReference (const std::string& id, bool activeOnly, bool doThrow=true); std::string mTargetId;
const MWWorld::Ptr getReference (const std::string& id, bool activeOnly, bool doThrow=true) const; /// 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
/// of a non-local script the reference derived from the target ID.
const MWWorld::Ptr getReferenceImp (const std::string& id = "",
bool activeOnly = false, bool doThrow=true) const;
const Locals& getMemberLocals (std::string& id, bool global) const; const Locals& getMemberLocals (std::string& id, bool global) const;
///< \a id is changed to the respective script ID, if \a id wasn't a script ID before ///< \a id is changed to the respective script ID, if \a id wasn't a script ID before
@ -42,9 +50,14 @@ namespace MWScript
Locals& getMemberLocals (std::string& id, bool global); Locals& getMemberLocals (std::string& id, bool global);
///< \a id is changed to the respective script ID, if \a id wasn't a script ID before ///< \a id is changed to the respective script ID, if \a id wasn't a script ID before
/// Throws an exception if local variable can't be found.
int findLocalVariableIndex (const std::string& scriptId, const std::string& name,
char type) const;
public: public:
InterpreterContext (MWScript::Locals *locals, MWWorld::Ptr reference); InterpreterContext (MWScript::Locals *locals, 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;
@ -113,7 +126,7 @@ namespace MWScript
virtual bool isScriptRunning (const std::string& name) const; virtual bool isScriptRunning (const std::string& name) const;
virtual void startScript (const std::string& name); virtual void startScript (const std::string& name, const std::string& targetId = "");
virtual void stopScript (const std::string& name); virtual void stopScript (const std::string& name);
@ -158,6 +171,8 @@ namespace MWScript
MWWorld::Ptr getReference(bool required=true); MWWorld::Ptr getReference(bool required=true);
///< Reference, that the script is running from (can be empty) ///< Reference, that the script is running from (can be empty)
virtual std::string getTargetId() const;
}; };
} }

@ -14,12 +14,15 @@ namespace MWScript
{ {
void Locals::configure (const ESM::Script& script) void Locals::configure (const ESM::Script& script)
{ {
const Compiler::Locals& locals =
MWBase::Environment::get().getScriptManager()->getLocals (script.mId);
mShorts.clear(); mShorts.clear();
mShorts.resize (script.mData.mNumShorts, 0); mShorts.resize (locals.get ('s').size(), 0);
mLongs.clear(); mLongs.clear();
mLongs.resize (script.mData.mNumLongs, 0); mLongs.resize (locals.get ('l').size(), 0);
mFloats.clear(); mFloats.clear();
mFloats.resize (script.mData.mNumFloats, 0); mFloats.resize (locals.get ('f').size(), 0);
} }
bool Locals::isEmpty() const bool Locals::isEmpty() const
@ -29,7 +32,7 @@ namespace MWScript
int Locals::getIntVar(const std::string &script, const std::string &var) int Locals::getIntVar(const std::string &script, const std::string &var)
{ {
Compiler::Locals locals = MWBase::Environment::get().getScriptManager()->getLocals(script); const Compiler::Locals& locals = MWBase::Environment::get().getScriptManager()->getLocals(script);
int index = locals.getIndex(var); int index = locals.getIndex(var);
char type = locals.getType(var); char type = locals.getType(var);
if(index != -1) if(index != -1)
@ -53,7 +56,7 @@ namespace MWScript
bool Locals::setVarByInt(const std::string& script, const std::string& var, int val) bool Locals::setVarByInt(const std::string& script, const std::string& var, int val)
{ {
Compiler::Locals locals = MWBase::Environment::get().getScriptManager()->getLocals(script); const Compiler::Locals& locals = MWBase::Environment::get().getScriptManager()->getLocals(script);
int index = locals.getIndex(var); int index = locals.getIndex(var);
char type = locals.getType(var); char type = locals.getType(var);
if(index != -1) if(index != -1)

@ -5,6 +5,7 @@
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <exception> #include <exception>
#include <algorithm>
#include <components/esm/loadscpt.hpp> #include <components/esm/loadscpt.hpp>
@ -22,12 +23,19 @@
namespace MWScript namespace MWScript
{ {
ScriptManager::ScriptManager (const MWWorld::ESMStore& store, bool verbose, ScriptManager::ScriptManager (const MWWorld::ESMStore& store, bool verbose,
Compiler::Context& compilerContext, int warningsMode) Compiler::Context& compilerContext, int warningsMode,
const std::vector<std::string>& scriptBlacklist)
: mErrorHandler (std::cerr), mStore (store), mVerbose (verbose), : mErrorHandler (std::cerr), mStore (store), mVerbose (verbose),
mCompilerContext (compilerContext), mParser (mErrorHandler, mCompilerContext), mCompilerContext (compilerContext), mParser (mErrorHandler, mCompilerContext),
mOpcodesInstalled (false), mGlobalScripts (store) mOpcodesInstalled (false), mGlobalScripts (store)
{ {
mErrorHandler.setWarningsMode (warningsMode); mErrorHandler.setWarningsMode (warningsMode);
mScriptBlacklist.resize (scriptBlacklist.size());
std::transform (scriptBlacklist.begin(), scriptBlacklist.end(),
mScriptBlacklist.begin(), Misc::StringUtils::lowerCase);
std::sort (mScriptBlacklist.begin(), mScriptBlacklist.end());
} }
bool ScriptManager::compile (const std::string& name) bool ScriptManager::compile (const std::string& name)
@ -133,16 +141,22 @@ namespace MWScript
int success = 0; int success = 0;
const MWWorld::Store<ESM::Script>& scripts = mStore.get<ESM::Script>(); const MWWorld::Store<ESM::Script>& scripts = mStore.get<ESM::Script>();
MWWorld::Store<ESM::Script>::iterator it = scripts.begin();
for (; it != scripts.end(); ++it, ++count) for (MWWorld::Store<ESM::Script>::iterator iter = scripts.begin();
if (compile (it->mId)) iter != scripts.end(); ++iter)
++success; if (!std::binary_search (mScriptBlacklist.begin(), mScriptBlacklist.end(),
Misc::StringUtils::lowerCase (iter->mId)))
{
++count;
if (compile (iter->mId))
++success;
}
return std::make_pair (count, success); return std::make_pair (count, success);
} }
Compiler::Locals& ScriptManager::getLocals (const std::string& name) const Compiler::Locals& ScriptManager::getLocals (const std::string& name)
{ {
std::string name2 = Misc::StringUtils::lowerCase (name); std::string name2 = Misc::StringUtils::lowerCase (name);
@ -182,46 +196,4 @@ namespace MWScript
{ {
return mGlobalScripts; return mGlobalScripts;
} }
int ScriptManager::getLocalIndex (const std::string& scriptId, const std::string& variable,
char type)
{
const ESM::Script *script = mStore.get<ESM::Script>().find (scriptId);
int offset = 0;
int size = 0;
switch (type)
{
case 's':
offset = 0;
size = script->mData.mNumShorts;
break;
case 'l':
offset = script->mData.mNumShorts;
size = script->mData.mNumLongs;
break;
case 'f':
offset = script->mData.mNumShorts+script->mData.mNumLongs;
size = script->mData.mNumFloats;
break;
default:
throw std::runtime_error ("invalid variable type");
}
std::string variable2 = Misc::StringUtils::lowerCase (variable);
for (int i=0; i<size; ++i)
if (Misc::StringUtils::lowerCase (script->mVarNames.at (i+offset))==variable2)
return i;
throw std::runtime_error ("unable to access local variable " + variable + " of " + scriptId);
}
} }

@ -48,11 +48,13 @@ namespace MWScript
ScriptCollection mScripts; ScriptCollection mScripts;
GlobalScripts mGlobalScripts; GlobalScripts mGlobalScripts;
std::map<std::string, Compiler::Locals> mOtherLocals; std::map<std::string, Compiler::Locals> mOtherLocals;
std::vector<std::string> mScriptBlacklist;
public: public:
ScriptManager (const MWWorld::ESMStore& store, bool verbose, ScriptManager (const MWWorld::ESMStore& store, bool verbose,
Compiler::Context& compilerContext, int warningsMode); Compiler::Context& compilerContext, int warningsMode,
const std::vector<std::string>& scriptBlacklist);
virtual void run (const std::string& name, Interpreter::Context& interpreterContext); virtual void 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)
@ -65,15 +67,10 @@ namespace MWScript
///< Compile all scripts ///< Compile all scripts
/// \return count, success /// \return count, success
virtual Compiler::Locals& getLocals (const std::string& name); virtual const Compiler::Locals& getLocals (const std::string& name);
///< Return locals for script \a name. ///< Return locals for script \a name.
virtual GlobalScripts& getGlobalScripts(); virtual GlobalScripts& getGlobalScripts();
virtual int getLocalIndex (const std::string& scriptId, const std::string& variable,
char type);
///< Return index of the variable of the given name and type in the given script. Will
/// throw an exception, if there is no such script or variable or the type does not match.
}; };
} }

@ -132,12 +132,12 @@ void MWState::StateManager::newGame (bool bypass)
{ {
cleanup(); cleanup();
MWBase::Environment::get().getWorld()->startNewGame (bypass);
if (!bypass) if (!bypass)
MWBase::Environment::get().getWindowManager()->setNewGame (true); MWBase::Environment::get().getWindowManager()->setNewGame (true);
else
MWBase::Environment::get().getWorld()->setGlobalInt ("chargenstate", -1); MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup();
MWBase::Environment::get().getWorld()->startNewGame (bypass);
mState = State_Running; mState = State_Running;
} }
@ -401,6 +401,8 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl
// Use detectWorldSpaceChange=false, otherwise some of the data we just loaded would be cleared again // Use detectWorldSpaceChange=false, otherwise some of the data we just loaded would be cleared again
MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition(), false); MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition(), false);
MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup();
// Do not trigger erroneous cellChanged events // Do not trigger erroneous cellChanged events
MWBase::Environment::get().getWorld()->markCellAsUnchanged(); MWBase::Environment::get().getWorld()->markCellAsUnchanged();
} }

@ -211,9 +211,9 @@ namespace MWWorld
// set new game mark // set new game mark
mGlobalVariables["chargenstate"].setInteger (1); mGlobalVariables["chargenstate"].setInteger (1);
mGlobalVariables["pcrace"].setInteger (3); mGlobalVariables["pcrace"].setInteger (3);
MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup();
} }
else
mGlobalVariables["chargenstate"].setInteger (-1);
if (bypass && !mStartCell.empty()) if (bypass && !mStartCell.empty())
{ {

@ -58,7 +58,7 @@ add_component_dir (compiler
context controlparser errorhandler exception exprparser extensions fileparser generator context controlparser errorhandler exception exprparser extensions fileparser generator
lineparser literals locals output parser scanner scriptparser skipparser streamerrorhandler lineparser literals locals output parser scanner scriptparser skipparser streamerrorhandler
stringparser tokenloc nullerrorhandler opcodes extensions0 declarationparser stringparser tokenloc nullerrorhandler opcodes extensions0 declarationparser
quickfileparser quickfileparser discardparser
) )
add_component_dir (interpreter add_component_dir (interpreter

@ -0,0 +1,70 @@
#include "discardparser.hpp"
#include "scanner.hpp"
namespace Compiler
{
DiscardParser::DiscardParser (ErrorHandler& errorHandler, const Context& context)
: Parser (errorHandler, context), mState (StartState)
{
}
bool DiscardParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner)
{
if (mState==StartState || mState==CommaState || mState==MinusState)
{
start();
return false;
}
return Parser::parseInt (value, loc, scanner);
}
bool DiscardParser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner)
{
if (mState==StartState || mState==CommaState || mState==MinusState)
{
start();
return false;
}
return Parser::parseFloat (value, loc, scanner);
}
bool DiscardParser::parseName (const std::string& name, const TokenLoc& loc,
Scanner& scanner)
{
if (mState==StartState || mState==CommaState)
{
start();
return false;
}
return Parser::parseName (name, loc, scanner);
}
bool DiscardParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
{
if (code==Scanner::S_comma && mState==StartState)
{
mState = CommaState;
return true;
}
if (code==Scanner::S_minus && (mState==StartState || mState==CommaState))
{
mState = MinusState;
return true;
}
return Parser::parseSpecial (code, loc, scanner);
}
void DiscardParser::reset()
{
mState = StartState;
Parser::reset();
}
}

@ -0,0 +1,45 @@
#ifndef COMPILER_DISCARDPARSER_H_INCLUDED
#define COMPILER_DISCARDPARSER_H_INCLUDED
#include "parser.hpp"
namespace Compiler
{
/// \brief Parse a single optional numeric value or string and discard it
class DiscardParser : public Parser
{
enum State
{
StartState, CommaState, MinusState
};
State mState;
public:
DiscardParser (ErrorHandler& errorHandler, const Context& context);
virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner);
///< Handle an int token.
/// \return fetch another token?
virtual bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner);
///< Handle a float token.
/// \return fetch another token?
virtual bool parseName (const std::string& name, const TokenLoc& loc,
Scanner& scanner);
///< Handle a name token.
/// \return fetch another token?
virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner);
///< Handle a special character token.
/// \return fetch another token?
virtual void reset();
///< Reset parser to clean state.
};
}
#endif

@ -3,11 +3,8 @@
namespace Compiler namespace Compiler
{ {
// constructor ErrorHandler::ErrorHandler()
: mWarnings (0), mErrors (0), mWarningsMode (1), mDowngradeErrors (false) {}
ErrorHandler::ErrorHandler() : mWarnings (0), mErrors (0), mWarningsMode (1) {}
// destructor
ErrorHandler::~ErrorHandler() {} ErrorHandler::~ErrorHandler() {}
@ -49,6 +46,12 @@ namespace Compiler
void ErrorHandler::error (const std::string& message, const TokenLoc& loc) void ErrorHandler::error (const std::string& message, const TokenLoc& loc)
{ {
if (mDowngradeErrors)
{
warning (message, loc);
return;
}
++mErrors; ++mErrors;
report (message, loc, ErrorMessage); report (message, loc, ErrorMessage);
} }
@ -72,4 +75,21 @@ namespace Compiler
{ {
mWarningsMode = mode; mWarningsMode = mode;
} }
void ErrorHandler::downgradeErrors (bool downgrade)
{
mDowngradeErrors = downgrade;
}
ErrorDowngrade::ErrorDowngrade (ErrorHandler& handler) : mHandler (handler)
{
mHandler.downgradeErrors (true);
}
ErrorDowngrade::~ErrorDowngrade()
{
mHandler.downgradeErrors (false);
}
} }

@ -17,6 +17,7 @@ namespace Compiler
int mWarnings; int mWarnings;
int mErrors; int mErrors;
int mWarningsMode; int mWarningsMode;
bool mDowngradeErrors;
protected: protected:
@ -66,6 +67,26 @@ namespace Compiler
void setWarningsMode (int mode); void setWarningsMode (int mode);
///< // 0 ignore, 1 rate as warning, 2 rate as error ///< // 0 ignore, 1 rate as warning, 2 rate as error
/// Treat errors as warnings.
void downgradeErrors (bool downgrade);
};
class ErrorDowngrade
{
ErrorHandler& mHandler;
/// not implemented
ErrorDowngrade (const ErrorDowngrade&);
/// not implemented
ErrorDowngrade& operator= (const ErrorDowngrade&);
public:
ErrorDowngrade (ErrorHandler& handler);
~ErrorDowngrade();
}; };
} }

@ -16,6 +16,7 @@
#include "stringparser.hpp" #include "stringparser.hpp"
#include "extensions.hpp" #include "extensions.hpp"
#include "context.hpp" #include "context.hpp"
#include "discardparser.hpp"
namespace Compiler namespace Compiler
{ {
@ -386,6 +387,9 @@ namespace Compiler
mExplicit.clear(); mExplicit.clear();
mRefOp = false; mRefOp = false;
std::vector<Interpreter::Type_Code> ignore;
parseArguments ("x", scanner, ignore);
mNextOperand = false; mNextOperand = false;
return true; return true;
} }
@ -404,6 +408,21 @@ namespace Compiler
mNextOperand = false; mNextOperand = false;
return true; return true;
} }
else if (keyword==Scanner::K_scriptrunning)
{
start();
mTokenLoc = loc;
parseArguments ("c", scanner);
Generator::scriptRunning (mCode);
mOperands.push_back ('l');
mExplicit.clear();
mRefOp = false;
mNextOperand = false;
return true;
}
// check for custom extensions // check for custom extensions
if (const Extensions *extensions = getContext().getExtensions()) if (const Extensions *extensions = getContext().getExtensions())
@ -527,6 +546,9 @@ namespace Compiler
Generator::getDisabled (mCode, mLiterals, ""); Generator::getDisabled (mCode, mLiterals, "");
mOperands.push_back ('l'); mOperands.push_back ('l');
std::vector<Interpreter::Type_Code> ignore;
parseArguments ("x", scanner, ignore);
mNextOperand = false; mNextOperand = false;
return true; return true;
} }
@ -737,6 +759,7 @@ namespace Compiler
ExprParser parser (getErrorHandler(), getContext(), mLocals, mLiterals, true); ExprParser parser (getErrorHandler(), getContext(), mLocals, mLiterals, true);
StringParser stringParser (getErrorHandler(), getContext(), mLiterals); StringParser stringParser (getErrorHandler(), getContext(), mLiterals);
DiscardParser discardParser (getErrorHandler(), getContext());
std::stack<std::vector<Interpreter::Type_Code> > stack; std::stack<std::vector<Interpreter::Type_Code> > stack;
@ -771,11 +794,32 @@ namespace Compiler
++optionalCount; ++optionalCount;
} }
} }
else if (*iter=='X')
{
parser.reset();
parser.setOptional (true);
scanner.scan (parser);
if (parser.isEmpty())
break;
}
else if (*iter=='z')
{
discardParser.reset();
discardParser.setOptional (true);
scanner.scan (discardParser);
if (discardParser.isEmpty())
break;
}
else else
{ {
parser.reset(); parser.reset();
if (optional || *iter == 'X') if (optional)
parser.setOptional (true); parser.setOptional (true);
scanner.scan (parser); scanner.scan (parser);
@ -783,20 +827,17 @@ namespace Compiler
if (optional && parser.isEmpty()) if (optional && parser.isEmpty())
break; break;
if (*iter != 'X') std::vector<Interpreter::Type_Code> tmp;
{
std::vector<Interpreter::Type_Code> tmp;
char type = parser.append (tmp); char type = parser.append (tmp);
if (type!=*iter) if (type!=*iter)
Generator::convert (tmp, type, *iter); Generator::convert (tmp, type, *iter);
stack.push (tmp); stack.push (tmp);
if (optional) if (optional)
++optionalCount; ++optionalCount;
}
} }
} }

@ -21,7 +21,8 @@ namespace Compiler
s - Short <BR> s - Short <BR>
S - String, case preserved <BR> S - String, case preserved <BR>
x - Optional, ignored string argument x - Optional, ignored string argument
X - Optional, ignored integer argument X - Optional, ignored numeric expression
z - Optional, ignored string or numeric argument
**/ **/
typedef std::string ScriptArgs; typedef std::string ScriptArgs;

@ -118,7 +118,7 @@ namespace Compiler
opcodeGetItemCountExplicit); opcodeGetItemCountExplicit);
extensions.registerInstruction ("removeitem", "cl", opcodeRemoveItem, extensions.registerInstruction ("removeitem", "cl", opcodeRemoveItem,
opcodeRemoveItemExplicit); opcodeRemoveItemExplicit);
extensions.registerInstruction ("equip", "c", opcodeEquip, opcodeEquipExplicit); extensions.registerInstruction ("equip", "cX", opcodeEquip, opcodeEquipExplicit);
extensions.registerFunction ("getarmortype", 'l', "l", opcodeGetArmorType, opcodeGetArmorTypeExplicit); extensions.registerFunction ("getarmortype", 'l', "l", opcodeGetArmorType, opcodeGetArmorTypeExplicit);
extensions.registerFunction ("hasitemequipped", 'l', "c", opcodeHasItemEquipped, opcodeHasItemEquippedExplicit); extensions.registerFunction ("hasitemequipped", 'l', "c", opcodeHasItemEquipped, opcodeHasItemEquippedExplicit);
extensions.registerFunction ("hassoulgem", 'l', "c", opcodeHasSoulGem, opcodeHasSoulGemExplicit); extensions.registerFunction ("hassoulgem", 'l', "c", opcodeHasSoulGem, opcodeHasSoulGemExplicit);
@ -406,7 +406,7 @@ namespace Compiler
extensions.registerInstruction ("setpccrimelevel", "f", opcodeSetPCCrimeLevel); extensions.registerInstruction ("setpccrimelevel", "f", opcodeSetPCCrimeLevel);
extensions.registerInstruction ("modpccrimelevel", "f", opcodeModPCCrimeLevel); extensions.registerInstruction ("modpccrimelevel", "f", opcodeModPCCrimeLevel);
extensions.registerInstruction ("addspell", "cxX", opcodeAddSpell, opcodeAddSpellExplicit); extensions.registerInstruction ("addspell", "cz", opcodeAddSpell, opcodeAddSpellExplicit);
extensions.registerInstruction ("removespell", "c", opcodeRemoveSpell, extensions.registerInstruction ("removespell", "c", opcodeRemoveSpell,
opcodeRemoveSpellExplicit); opcodeRemoveSpellExplicit);
extensions.registerInstruction ("removespelleffects", "c", opcodeRemoveSpellEffects, extensions.registerInstruction ("removespelleffects", "c", opcodeRemoveSpellEffects,

@ -51,6 +51,12 @@ namespace Compiler
/// \todo allow this workaround to be disabled for newer scripts /// \todo allow this workaround to be disabled for newer scripts
} }
if (mState==BeginCompleteState)
{
reportWarning ("Stray string (" + name + ") after begin statement", loc);
return true;
}
return Parser::parseName (name, loc, scanner); return Parser::parseName (name, loc, scanner);
} }

@ -300,9 +300,9 @@ namespace
code.push_back (Compiler::Generator::segment5 (46)); code.push_back (Compiler::Generator::segment5 (46));
} }
void opStartScript (Compiler::Generator::CodeContainer& code) void opStartScript (Compiler::Generator::CodeContainer& code, bool targeted)
{ {
code.push_back (Compiler::Generator::segment5 (47)); code.push_back (Compiler::Generator::segment5 (targeted ? 71 : 47));
} }
void opStopScript (Compiler::Generator::CodeContainer& code) void opStopScript (Compiler::Generator::CodeContainer& code)
@ -830,9 +830,16 @@ namespace Compiler
opScriptRunning (code); opScriptRunning (code);
} }
void startScript (CodeContainer& code) void startScript (CodeContainer& code, Literals& literals, const std::string& id)
{ {
opStartScript (code); if (id.empty())
opStartScript (code, false);
else
{
int index = literals.addString (id);
opPushInt (code, index);
opStartScript (code, true);
}
} }
void stopScript (CodeContainer& code) void stopScript (CodeContainer& code)

@ -113,7 +113,7 @@ namespace Compiler
void scriptRunning (CodeContainer& code); void scriptRunning (CodeContainer& code);
void startScript (CodeContainer& code); void startScript (CodeContainer& code, Literals& literals, const std::string& id);
void stopScript (CodeContainer& code); void stopScript (CodeContainer& code);

@ -11,6 +11,7 @@
#include "generator.hpp" #include "generator.hpp"
#include "extensions.hpp" #include "extensions.hpp"
#include "declarationparser.hpp" #include "declarationparser.hpp"
#include "exception.hpp"
namespace Compiler namespace Compiler
{ {
@ -262,6 +263,20 @@ namespace Compiler
Generator::disable (mCode, mLiterals, mExplicit); Generator::disable (mCode, mLiterals, mExplicit);
mState = PotentialEndState; mState = PotentialEndState;
return true; return true;
case Scanner::K_startscript:
mExprParser.parseArguments ("c", scanner, mCode);
Generator::startScript (mCode, mLiterals, mExplicit);
mState = EndState;
return true;
case Scanner::K_stopscript:
mExprParser.parseArguments ("c", scanner, mCode);
Generator::stopScript (mCode);
mState = EndState;
return true;
} }
// check for custom extensions // check for custom extensions
@ -278,9 +293,31 @@ namespace Compiler
mExplicit.clear(); mExplicit.clear();
} }
int optionals = mExprParser.parseArguments (argumentType, scanner, mCode); int optionals = 0;
try
{
ErrorDowngrade errorDowngrade (getErrorHandler());
std::vector<Interpreter::Type_Code> code;
optionals = mExprParser.parseArguments (argumentType, scanner, code);
mCode.insert (mCode.begin(), code.begin(), code.end());
extensions->generateInstructionCode (keyword, mCode, mLiterals,
mExplicit, optionals);
}
catch (const SourceException& exception)
{
// Ignore argument exceptions for positioncell.
/// \todo add option to disable this
if (Misc::StringUtils::lowerCase (loc.mLiteral)=="positioncell")
{
SkipParser skip (getErrorHandler(), getContext());
scanner.scan (skip);
return false;
}
throw;
}
extensions->generateInstructionCode (keyword, mCode, mLiterals, mExplicit, optionals);
mState = EndState; mState = EndState;
return true; return true;
} }
@ -349,7 +386,7 @@ namespace Compiler
if (declaration.parseKeyword (keyword, loc, scanner)) if (declaration.parseKeyword (keyword, loc, scanner))
scanner.scan (declaration); scanner.scan (declaration);
return true; return false;
} }
case Scanner::K_set: mState = SetState; return true; case Scanner::K_set: mState = SetState; return true;
@ -361,13 +398,6 @@ namespace Compiler
mState = EndState; mState = EndState;
return true; return true;
case Scanner::K_startscript:
mExprParser.parseArguments ("c", scanner, mCode);
Generator::startScript (mCode);
mState = EndState;
return true;
case Scanner::K_stopscript: case Scanner::K_stopscript:
mExprParser.parseArguments ("c", scanner, mCode); mExprParser.parseArguments ("c", scanner, mCode);

@ -17,8 +17,6 @@ namespace Compiler
int searchIndex (char type, const std::string& name) const; int searchIndex (char type, const std::string& name) const;
bool search (char type, const std::string& name) const;
std::vector<std::string>& get (char type); std::vector<std::string>& get (char type);
public: public:
@ -29,6 +27,10 @@ namespace Compiler
int getIndex (const std::string& name) const; int getIndex (const std::string& name) const;
///< return index for local variable \a name (-1: does not exist). ///< return index for local variable \a name (-1: does not exist).
/// Return index for local variable \a name of type \a type (-1: variable does not
/// exit).
bool search (char type, const std::string& name) const;
const std::vector<std::string>& get (char type) const; const std::vector<std::string>& get (char type) const;
void write (std::ostream& localFile) const; void write (std::ostream& localFile) const;

@ -343,17 +343,13 @@ namespace Compiler
} }
else if (!(c=='"' && name.empty())) else if (!(c=='"' && name.empty()))
{ {
if (!(std::isalpha (c) || std::isdigit (c) || c=='_' || c=='`' || if (!isStringCharacter (c))
/// \todo add an option to disable the following hack. Also, find out who is
/// responsible for allowing it in the first place and meet up with that person in
/// a dark alley.
(c=='-' && !name.empty() && std::isalpha (mStream.peek()))))
{ {
putback (c); putback (c);
break; break;
} }
if (first && std::isdigit (c)) if (first && (std::isdigit (c) || c=='`' || c=='-'))
error = true; error = true;
} }
@ -499,6 +495,17 @@ namespace Compiler
return true; return true;
} }
bool Scanner::isStringCharacter (char c, bool lookAhead)
{
return std::isalpha (c) || std::isdigit (c) || c=='_' ||
/// \todo disable this when doing more stricter compiling
c=='`' ||
/// \todo disable this when doing more stricter compiling. Also, find out who is
/// responsible for allowing it in the first place and meet up with that person in
/// a dark alley.
(c=='-' && (!lookAhead || isStringCharacter (mStream.peek(), false)));
}
bool Scanner::isWhitespace (char c) bool Scanner::isWhitespace (char c)
{ {
return c==' ' || c=='\t'; return c==' ' || c=='\t';

@ -92,6 +92,8 @@ namespace Compiler
bool scanSpecial (char c, Parser& parser, bool& cont); bool scanSpecial (char c, Parser& parser, bool& cont);
bool isStringCharacter (char c, bool lookAhead = true);
static bool isWhitespace (char c); static bool isWhitespace (char c);
public: public:

@ -12,6 +12,8 @@ void ESM::GlobalScript::load (ESMReader &esm)
mRunning = 0; mRunning = 0;
esm.getHNOT (mRunning, "RUN_"); esm.getHNOT (mRunning, "RUN_");
mTargetId = esm.getHNOString ("TARG");
} }
void ESM::GlobalScript::save (ESMWriter &esm) const void ESM::GlobalScript::save (ESMWriter &esm) const
@ -22,4 +24,6 @@ void ESM::GlobalScript::save (ESMWriter &esm) const
if (mRunning) if (mRunning)
esm.writeHNT ("RUN_", mRunning); esm.writeHNT ("RUN_", mRunning);
esm.writeHNOString ("TARG", mTargetId);
} }

@ -15,6 +15,7 @@ namespace ESM
std::string mId; std::string mId;
Locals mLocals; Locals mLocals;
int mRunning; int mRunning;
std::string mTargetId; // for targeted scripts
void load (ESMReader &esm); void load (ESMReader &esm);
void save (ESMWriter &esm) const; void save (ESMWriter &esm) const;

@ -23,29 +23,8 @@ public:
struct SCHDstruct struct SCHDstruct
{ {
/* Script name. /// Data from script-precompling in the editor.
/// \warning Do not use them. OpenCS currently does not precompile scripts.
NOTE: You should handle the name "Main" (case insensitive) with
care. With tribunal, modders got the ability to add 'start
scripts' to their mods, which is a script that is run at
startup and which runs throughout the game (I think.)
However, before Tribunal, there was only one startup script,
called "Main". If mods wanted to make their own start scripts,
they had to overwrite Main. This is obviously problem if
multiple mods to this at the same time.
Although most mods have switched to using Trib-style startup
scripts, some legacy mods might still overwrite Main, and this
can cause problems if several mods do it. I think the best
course of action is to NEVER overwrite main, but instead add
each with a separate unique name and add them to the start
script list. But there might be other problems with this
approach though.
*/
// These describe the sizes we need to allocate for the script
// data.
int mNumShorts, mNumLongs, mNumFloats, mScriptDataSize, mStringTableSize; int mNumShorts, mNumLongs, mNumFloats, mScriptDataSize, mStringTableSize;
}; // 52 bytes }; // 52 bytes
@ -53,9 +32,16 @@ public:
SCHDstruct mData; SCHDstruct mData;
std::vector<std::string> mVarNames; // Variable names /// Variable names generated by script-precompiling in the editor.
std::vector<unsigned char> mScriptData; // Compiled bytecode /// \warning Do not use this field. OpenCS currently does not precompile scripts.
std::string mScriptText; // Uncompiled script std::vector<std::string> mVarNames;
/// Bytecode generated from script-precompiling in the editor.
/// \warning Do not use this field. OpenCS currently does not precompile scripts.
std::vector<unsigned char> mScriptData;
/// Script source code
std::string mScriptText;
void load(ESMReader &esm); void load(ESMReader &esm);
void save(ESMWriter &esm) const; void save(ESMWriter &esm) const;

@ -81,7 +81,7 @@ namespace Interpreter
virtual bool isScriptRunning (const std::string& name) const = 0; virtual bool isScriptRunning (const std::string& name) const = 0;
virtual void startScript (const std::string& name) = 0; virtual void startScript (const std::string& name, const std::string& targetId = "") = 0;
virtual void stopScript (const std::string& name) = 0; virtual void stopScript (const std::string& name) = 0;
@ -108,6 +108,8 @@ 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;
}; };
} }

@ -133,5 +133,6 @@ op 67: store stack[0] in member float stack[2] of global script with ID stack[1]
op 68: replace stack[0] with member short stack[1] of global script with ID stack[0] op 68: replace stack[0] with member short stack[1] of global script with ID stack[0]
op 69: replace stack[0] with member short stack[1] of global script with ID stack[0] op 69: replace stack[0] with member short stack[1] of global script with ID stack[0]
op 70: replace stack[0] with member short stack[1] of global script with ID stack[0] op 70: replace stack[0] with member short stack[1] of global script with ID stack[0]
opcodes 71-33554431 unused op 71: explicit reference (target) = stack[0]; pop; start script stack[0] and pop
opcodes 72-33554431 unused
opcodes 33554432-67108863 reserved for extensions opcodes 33554432-67108863 reserved for extensions

@ -113,6 +113,7 @@ namespace Interpreter
interpreter.installSegment5 (46, new OpScriptRunning); interpreter.installSegment5 (46, new OpScriptRunning);
interpreter.installSegment5 (47, new OpStartScript); interpreter.installSegment5 (47, new OpStartScript);
interpreter.installSegment5 (48, new OpStopScript); interpreter.installSegment5 (48, new OpStopScript);
interpreter.installSegment5 (71, new OpStartScriptExplicit);
// spacial // spacial
interpreter.installSegment5 (49, new OpGetDistance); interpreter.installSegment5 (49, new OpGetDistance);

@ -26,7 +26,23 @@ 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().startScript (name, runtime.getContext().getTargetId());
}
};
class OpStartScriptExplicit : public Opcode0
{
public:
virtual void execute (Runtime& runtime)
{
std::string targetId = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
std::string name = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
runtime.getContext().startScript (name, targetId);
} }
}; };

@ -2,3 +2,6 @@ data="?global?data"
data="?mw?Data Files" data="?mw?Data Files"
data-local="?userdata?data" data-local="?userdata?data"
resources=${OPENMW_RESOURCE_FILES} resources=${OPENMW_RESOURCE_FILES}
script-blacklist=Museum
script-blacklist=MockChangeScript
script-blacklist=doortestwarp

@ -3,3 +3,6 @@ data="?mw?Data Files"
data=./data data=./data
data-local="?userdata?data" data-local="?userdata?data"
resources=./resources resources=./resources
script-blacklist=Museum
script-blacklist=MockChangeScript
script-blacklist=doortestwarp

Loading…
Cancel
Save