Skip to content

Commit

Permalink
Merge pull request #2648 from Assumeru/start-scripts
Browse files Browse the repository at this point in the history
Allow targeting non-unique actors with StartScript. Fixes #2311
  • Loading branch information
psi29a committed May 12, 2020
2 parents 507adac + 3b47829 commit 6229018
Show file tree
Hide file tree
Showing 20 changed files with 309 additions and 118 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
------

Bug #1952: Incorrect particle lighting
Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs
Bug #3676: NiParticleColorModifier isn't applied properly
Bug #5358: ForceGreeting always resets the dialogue window completely
Bug #5363: Enchantment autocalc not always 0/1
Expand Down
2 changes: 1 addition & 1 deletion apps/openmw/mwbase/scriptmanager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ namespace MWBase

virtual ~ScriptManager() {}

virtual void run (const std::string& name, Interpreter::Context& interpreterContext) = 0;
virtual bool run (const std::string& name, Interpreter::Context& interpreterContext) = 0;
///< Run the script with the given name (compile first, if not compiled yet)

virtual bool compile (const std::string& name) = 0;
Expand Down
2 changes: 2 additions & 0 deletions apps/openmw/mwbase/world.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ namespace MWBase
virtual MWWorld::Ptr searchPtrViaActorId (int actorId) = 0;
///< Search is limited to the active cells.

virtual MWWorld::Ptr searchPtrViaRefNum (const std::string& id, const ESM::RefNum& refNum) = 0;

virtual MWWorld::Ptr findContainer (const MWWorld::ConstPtr& ptr) = 0;
///< Return a pointer to a liveCellRef which contains \a ptr.
/// \note Search is limited to the active cells.
Expand Down
189 changes: 143 additions & 46 deletions apps/openmw/mwscript/globalscripts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,83 +5,171 @@
#include <components/esm/esmwriter.hpp>
#include <components/esm/globalscript.hpp>

#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"

#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/scriptmanager.hpp"

#include "../mwmechanics/creaturestats.hpp"

#include "interpretercontext.hpp"

namespace
{
struct ScriptCreatingVisitor : public boost::static_visitor<ESM::GlobalScript>
{
ESM::GlobalScript operator()(const MWWorld::Ptr &ptr) const
{
ESM::GlobalScript script;
script.mTargetRef.unset();
if (!ptr.isEmpty())
{
if (ptr.getCellRef().hasContentFile())
{
script.mTargetId = ptr.getCellRef().getRefId();
script.mTargetRef = ptr.getCellRef().getRefNum();
}
else if (MWBase::Environment::get().getWorld()->getPlayerPtr() == ptr)
script.mTargetId = ptr.getCellRef().getRefId();
}
return script;
}

ESM::GlobalScript operator()(const std::pair<ESM::RefNum, std::string> &pair) const
{
ESM::GlobalScript script;
script.mTargetId = pair.second;
script.mTargetRef = pair.first;
return script;
}
};

struct PtrGettingVisitor : public boost::static_visitor<const MWWorld::Ptr*>
{
const MWWorld::Ptr* operator()(const MWWorld::Ptr &ptr) const
{
return &ptr;
}

const MWWorld::Ptr* operator()(const std::pair<ESM::RefNum, std::string> &pair) const
{
return nullptr;
}
};

struct PtrResolvingVisitor : public boost::static_visitor<MWWorld::Ptr>
{
MWWorld::Ptr operator()(const MWWorld::Ptr &ptr) const
{
return ptr;
}

MWWorld::Ptr operator()(const std::pair<ESM::RefNum, std::string> &pair) const
{
if (pair.second.empty())
return MWWorld::Ptr();
else if(pair.first.hasContentFile())
return MWBase::Environment::get().getWorld()->searchPtrViaRefNum(pair.second, pair.first);
return MWBase::Environment::get().getWorld()->searchPtr(pair.second, false);
}
};

class MatchPtrVisitor : public boost::static_visitor<bool>
{
const MWWorld::Ptr& mPtr;
public:
MatchPtrVisitor(const MWWorld::Ptr& ptr) : mPtr(ptr) {}

bool operator()(const MWWorld::Ptr &ptr) const
{
return ptr == mPtr;
}

bool operator()(const std::pair<ESM::RefNum, std::string> &pair) const
{
return false;
}
};
}

namespace MWScript
{
GlobalScriptDesc::GlobalScriptDesc() : mRunning (false) {}

const MWWorld::Ptr* GlobalScriptDesc::getPtrIfPresent() const
{
return boost::apply_visitor(PtrGettingVisitor(), mTarget);
}

MWWorld::Ptr GlobalScriptDesc::getPtr()
{
MWWorld::Ptr ptr = boost::apply_visitor(PtrResolvingVisitor(), mTarget);
mTarget = ptr;
return ptr;
}


GlobalScripts::GlobalScripts (const MWWorld::ESMStore& store)
: mStore (store)
{}

void GlobalScripts::addScript (const std::string& name, const std::string& targetId)
void GlobalScripts::addScript (const std::string& name, const MWWorld::Ptr& target)
{
std::map<std::string, GlobalScriptDesc>::iterator iter =
mScripts.find (::Misc::StringUtils::lowerCase (name));
const auto iter = mScripts.find (::Misc::StringUtils::lowerCase (name));

if (iter==mScripts.end())
{
if (const ESM::Script *script = mStore.get<ESM::Script>().search(name))
{
GlobalScriptDesc desc;
desc.mRunning = true;
desc.mLocals.configure (*script);
desc.mId = targetId;

mScripts.insert (std::make_pair (name, desc));
auto desc = std::make_shared<GlobalScriptDesc>();
MWWorld::Ptr ptr = target;
desc->mTarget = ptr;
desc->mRunning = true;
desc->mLocals.configure (*script);
mScripts.insert (std::make_pair(name, desc));
}
else
{
Log(Debug::Error) << "Failed to add global script " << name << ": script record not found";
}
}
else if (!iter->second.mRunning)
else if (!iter->second->mRunning)
{
iter->second.mRunning = true;
iter->second.mId = targetId;
iter->second->mRunning = true;
MWWorld::Ptr ptr = target;
iter->second->mTarget = ptr;
}
}

void GlobalScripts::removeScript (const std::string& name)
{
std::map<std::string, GlobalScriptDesc>::iterator iter =
mScripts.find (::Misc::StringUtils::lowerCase (name));
const auto iter = mScripts.find (::Misc::StringUtils::lowerCase (name));

if (iter!=mScripts.end())
iter->second.mRunning = false;
iter->second->mRunning = false;
}

bool GlobalScripts::isRunning (const std::string& name) const
{
std::map<std::string, GlobalScriptDesc>::const_iterator iter =
mScripts.find (::Misc::StringUtils::lowerCase (name));
const auto iter = mScripts.find (::Misc::StringUtils::lowerCase (name));

if (iter==mScripts.end())
return false;

return iter->second.mRunning;
return iter->second->mRunning;
}

void GlobalScripts::run()
{
for (std::map<std::string, GlobalScriptDesc>::iterator iter (mScripts.begin());
iter!=mScripts.end(); ++iter)
for (const auto& script : mScripts)
{
if (iter->second.mRunning)
if (script.second->mRunning)
{
MWWorld::Ptr ptr;

MWScript::InterpreterContext interpreterContext (
&iter->second.mLocals, MWWorld::Ptr(), iter->second.mId);

MWBase::Environment::get().getScriptManager()->run (iter->first, interpreterContext);
MWScript::InterpreterContext context(script.second);
if (!MWBase::Environment::get().getScriptManager()->run(script.first, context))
script.second->mRunning = false;
}
}
}
Expand Down Expand Up @@ -129,18 +217,15 @@ namespace MWScript

void GlobalScripts::write (ESM::ESMWriter& writer, Loading::Listener& progress) const
{
for (std::map<std::string, GlobalScriptDesc>::const_iterator iter (mScripts.begin());
iter!=mScripts.end(); ++iter)
for (const auto& iter : mScripts)
{
ESM::GlobalScript script;
ESM::GlobalScript script = boost::apply_visitor (ScriptCreatingVisitor(), iter.second->mTarget);

script.mId = iter->first;
script.mId = iter.first;

iter->second.mLocals.write (script.mLocals, iter->first);
iter.second->mLocals.write (script.mLocals, iter.first);

script.mRunning = iter->second.mRunning ? 1 : 0;

script.mTargetId = iter->second.mId;
script.mRunning = iter.second->mRunning ? 1 : 0;

writer.startRecord (ESM::REC_GSCR);
script.save (writer);
Expand All @@ -155,17 +240,20 @@ namespace MWScript
ESM::GlobalScript script;
script.load (reader);

std::map<std::string, GlobalScriptDesc>::iterator iter =
mScripts.find (script.mId);
auto iter = mScripts.find (script.mId);

if (iter==mScripts.end())
{
if (const ESM::Script *scriptRecord = mStore.get<ESM::Script>().search (script.mId))
{
try
{
GlobalScriptDesc desc;
desc.mLocals.configure (*scriptRecord);
auto desc = std::make_shared<GlobalScriptDesc>();
if (!script.mTargetId.empty())
{
desc->mTarget = std::make_pair(script.mTargetRef, script.mTargetId);
}
desc->mLocals.configure (*scriptRecord);

iter = mScripts.insert (std::make_pair (script.mId, desc)).first;
}
Expand All @@ -182,9 +270,8 @@ namespace MWScript
return true;
}

iter->second.mRunning = script.mRunning!=0;
iter->second.mLocals.read (script.mLocals, script.mId);
iter->second.mId = script.mTargetId;
iter->second->mRunning = script.mRunning!=0;
iter->second->mLocals.read (script.mLocals, script.mId);

return true;
}
Expand All @@ -195,18 +282,28 @@ namespace MWScript
Locals& GlobalScripts::getLocals (const std::string& name)
{
std::string name2 = ::Misc::StringUtils::lowerCase (name);
std::map<std::string, GlobalScriptDesc>::iterator iter = mScripts.find (name2);
auto iter = mScripts.find (name2);

if (iter==mScripts.end())
{
const ESM::Script *script = mStore.get<ESM::Script>().find (name);

GlobalScriptDesc desc;
desc.mLocals.configure (*script);
auto desc = std::make_shared<GlobalScriptDesc>();
desc->mLocals.configure (*script);

iter = mScripts.insert (std::make_pair (name2, desc)).first;
}

return iter->second.mLocals;
return iter->second->mLocals;
}

void GlobalScripts::updatePtrs(const MWWorld::Ptr& base, const MWWorld::Ptr& updated)
{
MatchPtrVisitor visitor(base);
for (const auto& script : mScripts)
{
if (boost::apply_visitor (visitor, script.second->mTarget))
script.second->mTarget = updated;
}
}
}
20 changes: 17 additions & 3 deletions apps/openmw/mwscript/globalscripts.hpp
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
#ifndef GAME_SCRIPT_GLOBALSCRIPTS_H
#define GAME_SCRIPT_GLOBALSCRIPTS_H

#include <boost/variant/variant.hpp>

#include <string>
#include <map>
#include <memory>
#include <utility>

#include <stdint.h>

#include "locals.hpp"

#include "../mwworld/ptr.hpp"

namespace ESM
{
class ESMWriter;
class ESMReader;
struct RefNum;
}

namespace Loading
Expand All @@ -30,21 +37,25 @@ namespace MWScript
{
bool mRunning;
Locals mLocals;
std::string mId; // ID used to start targeted script (empty if not a targeted script)
boost::variant<MWWorld::Ptr, std::pair<ESM::RefNum, std::string> > mTarget; // Used to start targeted script

GlobalScriptDesc();

const MWWorld::Ptr* getPtrIfPresent() const; // Returns a Ptr if one has been resolved

MWWorld::Ptr getPtr(); // Resolves mTarget to a Ptr and caches the (potentially empty) result
};

class GlobalScripts
{
const MWWorld::ESMStore& mStore;
std::map<std::string, GlobalScriptDesc> mScripts;
std::map<std::string, std::shared_ptr<GlobalScriptDesc> > mScripts;

public:

GlobalScripts (const MWWorld::ESMStore& store);

void addScript (const std::string& name, const std::string& targetId = "");
void addScript (const std::string& name, const MWWorld::Ptr& target = MWWorld::Ptr());

void removeScript (const std::string& name);

Expand All @@ -70,6 +81,9 @@ namespace MWScript
Locals& getLocals (const std::string& name);
///< If the script \a name has not been added as a global script yet, it is added
/// automatically, but is not set to running state.

void updatePtrs(const MWWorld::Ptr& base, const MWWorld::Ptr& updated);
///< Update the Ptrs stored in mTarget. Should be called after the reference has been moved to a new cell.
};
}

Expand Down

0 comments on commit 6229018

Please sign in to comment.