mirror of
https://github.com/OpenMW/openmw.git
synced 2025-05-01 04:11:23 +00:00
Add apps/openmw/mwlua
This commit is contained in:
parent
7df500c385
commit
3d7e306064
24 changed files with 1180 additions and 1 deletions
|
@ -55,6 +55,11 @@ add_openmw_dir (mwscript
|
||||||
animationextensions transformationextensions consoleextensions userextensions
|
animationextensions transformationextensions consoleextensions userextensions
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_openmw_dir (mwlua
|
||||||
|
luamanagerimp localscripts object worldview luabindings userdataserializer
|
||||||
|
objectbindings
|
||||||
|
)
|
||||||
|
|
||||||
add_openmw_dir (mwsound
|
add_openmw_dir (mwsound
|
||||||
soundmanagerimp openal_output ffmpeg_decoder sound sound_buffer sound_decoder sound_output
|
soundmanagerimp openal_output ffmpeg_decoder sound sound_buffer sound_decoder sound_output
|
||||||
loudness movieaudiofactory alext efx efx-presets regionsoundselector watersoundupdater volumesettings
|
loudness movieaudiofactory alext efx efx-presets regionsoundselector watersoundupdater volumesettings
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "engine.hpp"
|
#include "engine.hpp"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
@ -46,6 +47,8 @@
|
||||||
|
|
||||||
#include "mwgui/windowmanagerimp.hpp"
|
#include "mwgui/windowmanagerimp.hpp"
|
||||||
|
|
||||||
|
#include "mwlua/luamanagerimp.hpp"
|
||||||
|
|
||||||
#include "mwscript/scriptmanagerimp.hpp"
|
#include "mwscript/scriptmanagerimp.hpp"
|
||||||
#include "mwscript/interpretercontext.hpp"
|
#include "mwscript/interpretercontext.hpp"
|
||||||
|
|
||||||
|
@ -101,6 +104,7 @@ namespace
|
||||||
PhysicsWorker,
|
PhysicsWorker,
|
||||||
World,
|
World,
|
||||||
Gui,
|
Gui,
|
||||||
|
Lua,
|
||||||
|
|
||||||
Number,
|
Number,
|
||||||
};
|
};
|
||||||
|
@ -138,6 +142,9 @@ namespace
|
||||||
template <>
|
template <>
|
||||||
const UserStats UserStatsValue<UserStatsType::Gui>::sValue {"Gui", "gui"};
|
const UserStats UserStatsValue<UserStatsType::Gui>::sValue {"Gui", "gui"};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
const UserStats UserStatsValue<UserStatsType::Lua>::sValue {"Lua", "lua"};
|
||||||
|
|
||||||
template <UserStatsType type>
|
template <UserStatsType type>
|
||||||
struct ForEachUserStatsValue
|
struct ForEachUserStatsValue
|
||||||
{
|
{
|
||||||
|
@ -700,6 +707,9 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
||||||
|
|
||||||
mViewer->addEventHandler(mScreenCaptureHandler);
|
mViewer->addEventHandler(mScreenCaptureHandler);
|
||||||
|
|
||||||
|
mLuaManager = new MWLua::LuaManager(mVFS.get());
|
||||||
|
mEnvironment.setLuaManager(mLuaManager);
|
||||||
|
|
||||||
// Create input and UI first to set up a bootstrapping environment for
|
// Create input and UI first to set up a bootstrapping environment for
|
||||||
// showing a loading screen and keeping the window responsive while doing so
|
// showing a loading screen and keeping the window responsive while doing so
|
||||||
|
|
||||||
|
@ -811,6 +821,8 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
||||||
<< 100*static_cast<double> (result.second)/result.first
|
<< 100*static_cast<double> (result.second)/result.first
|
||||||
<< "%)";
|
<< "%)";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mLuaManager->init();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialise and enter main loop.
|
// Initialise and enter main loop.
|
||||||
|
@ -895,6 +907,28 @@ void OMW::Engine::go()
|
||||||
mEnvironment.getWindowManager()->executeInConsole(mStartupScript);
|
mEnvironment.getWindowManager()->executeInConsole(mStartupScript);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start Lua scripting thread
|
||||||
|
std::atomic_bool luaUpdateRequest;
|
||||||
|
double luaDt = 0;
|
||||||
|
std::thread scriptingThread([&]() {
|
||||||
|
const osg::Timer* const timer = osg::Timer::instance();
|
||||||
|
osg::Stats* const stats = mViewer->getViewerStats();
|
||||||
|
while (!mViewer->done() && !mEnvironment.getStateManager()->hasQuitRequest())
|
||||||
|
{
|
||||||
|
while (!luaUpdateRequest)
|
||||||
|
std::this_thread::yield();
|
||||||
|
|
||||||
|
{
|
||||||
|
const osg::Timer_t frameStart = mViewer->getStartTick();
|
||||||
|
const unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber();
|
||||||
|
ScopedProfile<UserStatsType::Lua> profile(frameStart, frameNumber, *timer, *stats);
|
||||||
|
|
||||||
|
mLuaManager->update(mEnvironment.getWindowManager()->isGuiMode(), luaDt);
|
||||||
|
}
|
||||||
|
luaUpdateRequest = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Start the main rendering loop
|
// Start the main rendering loop
|
||||||
double simulationTime = 0.0;
|
double simulationTime = 0.0;
|
||||||
Misc::FrameRateLimiter frameRateLimiter = Misc::makeFrameRateLimiter(mEnvironment.getFrameRateLimit());
|
Misc::FrameRateLimiter frameRateLimiter = Misc::makeFrameRateLimiter(mEnvironment.getFrameRateLimit());
|
||||||
|
@ -920,8 +954,17 @@ void OMW::Engine::go()
|
||||||
|
|
||||||
mEnvironment.getWorld()->updateWindowManager();
|
mEnvironment.getWorld()->updateWindowManager();
|
||||||
|
|
||||||
|
// scriptingThread starts processing Lua scripts
|
||||||
|
luaDt = dt;
|
||||||
|
luaUpdateRequest = true;
|
||||||
|
|
||||||
mViewer->renderingTraversals();
|
mViewer->renderingTraversals();
|
||||||
|
|
||||||
|
// wait for scriptingThread to finish
|
||||||
|
while (luaUpdateRequest)
|
||||||
|
std::this_thread::yield();
|
||||||
|
mLuaManager->applyQueuedChanges();
|
||||||
|
|
||||||
bool guiActive = mEnvironment.getWindowManager()->isGuiMode();
|
bool guiActive = mEnvironment.getWindowManager()->isGuiMode();
|
||||||
if (!guiActive)
|
if (!guiActive)
|
||||||
simulationTime += dt;
|
simulationTime += dt;
|
||||||
|
@ -943,6 +986,8 @@ void OMW::Engine::go()
|
||||||
frameRateLimiter.limit();
|
frameRateLimiter.limit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scriptingThread.join();
|
||||||
|
|
||||||
// Save user settings
|
// Save user settings
|
||||||
settings.saveUser(settingspath);
|
settings.saveUser(settingspath);
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,11 @@ namespace Compiler
|
||||||
class Context;
|
class Context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
class LuaManager;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Files
|
namespace Files
|
||||||
{
|
{
|
||||||
struct ConfigurationManager;
|
struct ConfigurationManager;
|
||||||
|
@ -85,6 +90,8 @@ namespace OMW
|
||||||
Compiler::Extensions mExtensions;
|
Compiler::Extensions mExtensions;
|
||||||
Compiler::Context *mScriptContext;
|
Compiler::Context *mScriptContext;
|
||||||
|
|
||||||
|
MWLua::LuaManager* mLuaManager;
|
||||||
|
|
||||||
Files::Collections mFileCollections;
|
Files::Collections mFileCollections;
|
||||||
bool mFSStrict;
|
bool mFSStrict;
|
||||||
Translation::Storage mTranslationDataStorage;
|
Translation::Storage mTranslationDataStorage;
|
||||||
|
|
|
@ -13,13 +13,14 @@
|
||||||
#include "inputmanager.hpp"
|
#include "inputmanager.hpp"
|
||||||
#include "windowmanager.hpp"
|
#include "windowmanager.hpp"
|
||||||
#include "statemanager.hpp"
|
#include "statemanager.hpp"
|
||||||
|
#include "luamanager.hpp"
|
||||||
|
|
||||||
MWBase::Environment *MWBase::Environment::sThis = nullptr;
|
MWBase::Environment *MWBase::Environment::sThis = nullptr;
|
||||||
|
|
||||||
MWBase::Environment::Environment()
|
MWBase::Environment::Environment()
|
||||||
: mWorld (nullptr), mSoundManager (nullptr), mScriptManager (nullptr), mWindowManager (nullptr),
|
: mWorld (nullptr), mSoundManager (nullptr), mScriptManager (nullptr), mWindowManager (nullptr),
|
||||||
mMechanicsManager (nullptr), mDialogueManager (nullptr), mJournal (nullptr), mInputManager (nullptr),
|
mMechanicsManager (nullptr), mDialogueManager (nullptr), mJournal (nullptr), mInputManager (nullptr),
|
||||||
mStateManager (nullptr), mResourceSystem (nullptr), mFrameDuration (0), mFrameRateLimit(0.f)
|
mStateManager (nullptr), mLuaManager (nullptr), mResourceSystem (nullptr), mFrameDuration (0), mFrameRateLimit(0.f)
|
||||||
{
|
{
|
||||||
assert (!sThis);
|
assert (!sThis);
|
||||||
sThis = this;
|
sThis = this;
|
||||||
|
@ -76,6 +77,11 @@ void MWBase::Environment::setStateManager (StateManager *stateManager)
|
||||||
mStateManager = stateManager;
|
mStateManager = stateManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MWBase::Environment::setLuaManager (LuaManager *luaManager)
|
||||||
|
{
|
||||||
|
mLuaManager = luaManager;
|
||||||
|
}
|
||||||
|
|
||||||
void MWBase::Environment::setResourceSystem (Resource::ResourceSystem *resourceSystem)
|
void MWBase::Environment::setResourceSystem (Resource::ResourceSystem *resourceSystem)
|
||||||
{
|
{
|
||||||
mResourceSystem = resourceSystem;
|
mResourceSystem = resourceSystem;
|
||||||
|
@ -150,6 +156,12 @@ MWBase::StateManager *MWBase::Environment::getStateManager() const
|
||||||
return mStateManager;
|
return mStateManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MWBase::LuaManager *MWBase::Environment::getLuaManager() const
|
||||||
|
{
|
||||||
|
assert (mLuaManager);
|
||||||
|
return mLuaManager;
|
||||||
|
}
|
||||||
|
|
||||||
Resource::ResourceSystem *MWBase::Environment::getResourceSystem() const
|
Resource::ResourceSystem *MWBase::Environment::getResourceSystem() const
|
||||||
{
|
{
|
||||||
return mResourceSystem;
|
return mResourceSystem;
|
||||||
|
@ -188,6 +200,9 @@ void MWBase::Environment::cleanup()
|
||||||
|
|
||||||
delete mStateManager;
|
delete mStateManager;
|
||||||
mStateManager = nullptr;
|
mStateManager = nullptr;
|
||||||
|
|
||||||
|
delete mLuaManager;
|
||||||
|
mLuaManager = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MWBase::Environment& MWBase::Environment::get()
|
const MWBase::Environment& MWBase::Environment::get()
|
||||||
|
|
|
@ -22,6 +22,7 @@ namespace MWBase
|
||||||
class InputManager;
|
class InputManager;
|
||||||
class WindowManager;
|
class WindowManager;
|
||||||
class StateManager;
|
class StateManager;
|
||||||
|
class LuaManager;
|
||||||
|
|
||||||
/// \brief Central hub for mw-subsystems
|
/// \brief Central hub for mw-subsystems
|
||||||
///
|
///
|
||||||
|
@ -42,6 +43,7 @@ namespace MWBase
|
||||||
Journal *mJournal;
|
Journal *mJournal;
|
||||||
InputManager *mInputManager;
|
InputManager *mInputManager;
|
||||||
StateManager *mStateManager;
|
StateManager *mStateManager;
|
||||||
|
LuaManager *mLuaManager;
|
||||||
Resource::ResourceSystem *mResourceSystem;
|
Resource::ResourceSystem *mResourceSystem;
|
||||||
float mFrameDuration;
|
float mFrameDuration;
|
||||||
float mFrameRateLimit;
|
float mFrameRateLimit;
|
||||||
|
@ -76,6 +78,8 @@ namespace MWBase
|
||||||
|
|
||||||
void setStateManager (StateManager *stateManager);
|
void setStateManager (StateManager *stateManager);
|
||||||
|
|
||||||
|
void setLuaManager (LuaManager *luaManager);
|
||||||
|
|
||||||
void setResourceSystem (Resource::ResourceSystem *resourceSystem);
|
void setResourceSystem (Resource::ResourceSystem *resourceSystem);
|
||||||
|
|
||||||
void setFrameDuration (float duration);
|
void setFrameDuration (float duration);
|
||||||
|
@ -102,6 +106,8 @@ namespace MWBase
|
||||||
|
|
||||||
StateManager *getStateManager() const;
|
StateManager *getStateManager() const;
|
||||||
|
|
||||||
|
LuaManager *getLuaManager() const;
|
||||||
|
|
||||||
Resource::ResourceSystem *getResourceSystem() const;
|
Resource::ResourceSystem *getResourceSystem() const;
|
||||||
|
|
||||||
float getFrameDuration() const;
|
float getFrameDuration() const;
|
||||||
|
|
42
apps/openmw/mwbase/luamanager.hpp
Normal file
42
apps/openmw/mwbase/luamanager.hpp
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef GAME_MWBASE_LUAMANAGER_H
|
||||||
|
#define GAME_MWBASE_LUAMANAGER_H
|
||||||
|
|
||||||
|
#include <SDL_events.h>
|
||||||
|
|
||||||
|
namespace MWWorld
|
||||||
|
{
|
||||||
|
class Ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWBase
|
||||||
|
{
|
||||||
|
|
||||||
|
class LuaManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~LuaManager() = default;
|
||||||
|
|
||||||
|
virtual void newGameStarted() = 0;
|
||||||
|
virtual void objectAddedToScene(const MWWorld::Ptr& ptr) = 0;
|
||||||
|
virtual void objectRemovedFromScene(const MWWorld::Ptr& ptr) = 0;
|
||||||
|
virtual void keyPressed(const SDL_KeyboardEvent &arg) = 0;
|
||||||
|
|
||||||
|
struct ActorControls {
|
||||||
|
bool controlledFromLua;
|
||||||
|
|
||||||
|
bool jump;
|
||||||
|
bool run;
|
||||||
|
float movement;
|
||||||
|
float sideMovement;
|
||||||
|
float turn;
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual const ActorControls* getActorControls(const MWWorld::Ptr&) const = 0;
|
||||||
|
|
||||||
|
virtual void clear() = 0;
|
||||||
|
virtual void setupPlayer(const MWWorld::Ptr&) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // GAME_MWBASE_LUAMANAGER_H
|
23
apps/openmw/mwlua/eventqueue.hpp
Normal file
23
apps/openmw/mwlua/eventqueue.hpp
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef MWLUA_EVENTQUEUE_H
|
||||||
|
#define MWLUA_EVENTQUEUE_H
|
||||||
|
|
||||||
|
#include "object.hpp"
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
struct GlobalEvent
|
||||||
|
{
|
||||||
|
std::string eventName;
|
||||||
|
std::string eventData;
|
||||||
|
};
|
||||||
|
struct LocalEvent
|
||||||
|
{
|
||||||
|
ObjectId dest;
|
||||||
|
std::string eventName;
|
||||||
|
std::string eventData;
|
||||||
|
};
|
||||||
|
using GlobalEventQueue = std::vector<GlobalEvent>;
|
||||||
|
using LocalEventQueue = std::vector<LocalEvent>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MWLUA_EVENTQUEUE_H
|
36
apps/openmw/mwlua/globalscripts.hpp
Normal file
36
apps/openmw/mwlua/globalscripts.hpp
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#ifndef MWLUA_GLOBALSCRIPTS_H
|
||||||
|
#define MWLUA_GLOBALSCRIPTS_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <components/lua/luastate.hpp>
|
||||||
|
#include <components/lua/scriptscontainer.hpp>
|
||||||
|
|
||||||
|
#include "object.hpp"
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
|
||||||
|
class GlobalScripts : public LuaUtil::ScriptsContainer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GlobalScripts(LuaUtil::LuaState* lua) : LuaUtil::ScriptsContainer(lua, "Global")
|
||||||
|
{
|
||||||
|
registerEngineHandlers({&mActorActiveHandlers, &mNewGameHandlers, &mPlayerAddedHandlers});
|
||||||
|
}
|
||||||
|
|
||||||
|
void newGameStarted() { callEngineHandlers(mNewGameHandlers); }
|
||||||
|
void actorActive(const GObject& obj) { callEngineHandlers(mActorActiveHandlers, obj); }
|
||||||
|
void playerAdded(const GObject& obj) { callEngineHandlers(mPlayerAddedHandlers, obj); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
EngineHandlerList mActorActiveHandlers{"onActorActive"};
|
||||||
|
EngineHandlerList mNewGameHandlers{"onNewGame"};
|
||||||
|
EngineHandlerList mPlayerAddedHandlers{"onPlayerAdded"};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MWLUA_GLOBALSCRIPTS_H
|
44
apps/openmw/mwlua/localscripts.cpp
Normal file
44
apps/openmw/mwlua/localscripts.cpp
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#include "localscripts.hpp"
|
||||||
|
|
||||||
|
namespace sol
|
||||||
|
{
|
||||||
|
template <>
|
||||||
|
struct is_automagical<MWBase::LuaManager::ActorControls> : std::false_type {};
|
||||||
|
template <>
|
||||||
|
struct is_automagical<MWLua::LocalScripts::SelfObject> : std::false_type {};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
|
||||||
|
void LocalScripts::initializeSelfPackage(const Context& context)
|
||||||
|
{
|
||||||
|
using ActorControls = MWBase::LuaManager::ActorControls;
|
||||||
|
sol::usertype<ActorControls> controls = context.mLua->sol().new_usertype<ActorControls>("ActorControls");
|
||||||
|
controls["movement"] = &ActorControls::movement;
|
||||||
|
controls["sideMovement"] = &ActorControls::sideMovement;
|
||||||
|
controls["turn"] = &ActorControls::turn;
|
||||||
|
controls["run"] = &ActorControls::run;
|
||||||
|
controls["jump"] = &ActorControls::jump;
|
||||||
|
|
||||||
|
sol::usertype<SelfObject> selfAPI =
|
||||||
|
context.mLua->sol().new_usertype<SelfObject>("SelfObject", sol::base_classes, sol::bases<LObject>());
|
||||||
|
selfAPI[sol::meta_function::to_string] = [](SelfObject& self) { return "openmw.self[" + self.toString() + "]"; };
|
||||||
|
selfAPI["object"] = sol::readonly_property([](SelfObject& self) -> LObject { return LObject(self); });
|
||||||
|
selfAPI["controls"] = sol::readonly_property([](SelfObject& self) { return &self.mControls; });
|
||||||
|
selfAPI["setDirectControl"] = [](SelfObject& self, bool v) { self.mControls.controlledFromLua = v; };
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<LocalScripts> LocalScripts::create(LuaUtil::LuaState* lua, const LObject& obj)
|
||||||
|
{
|
||||||
|
return std::unique_ptr<LocalScripts>(new LocalScripts(lua, obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalScripts::LocalScripts(LuaUtil::LuaState* lua, const LObject& obj)
|
||||||
|
: LuaUtil::ScriptsContainer(lua, "L" + idToString(obj.id())), mData(obj)
|
||||||
|
{
|
||||||
|
mData.mControls.controlledFromLua = false;
|
||||||
|
this->addPackage("openmw.self", sol::make_object(lua->sol(), &mData));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
39
apps/openmw/mwlua/localscripts.hpp
Normal file
39
apps/openmw/mwlua/localscripts.hpp
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#ifndef MWLUA_LOCALSCRIPTS_H
|
||||||
|
#define MWLUA_LOCALSCRIPTS_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <components/lua/luastate.hpp>
|
||||||
|
#include <components/lua/scriptscontainer.hpp>
|
||||||
|
|
||||||
|
#include "../mwbase/luamanager.hpp"
|
||||||
|
|
||||||
|
#include "object.hpp"
|
||||||
|
#include "luabindings.hpp"
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
|
||||||
|
class LocalScripts : public LuaUtil::ScriptsContainer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static std::unique_ptr<LocalScripts> create(LuaUtil::LuaState* lua, const LObject& obj);
|
||||||
|
static void initializeSelfPackage(const Context&);
|
||||||
|
|
||||||
|
const MWBase::LuaManager::ActorControls* getActorControls() const { return &mData.mControls; }
|
||||||
|
|
||||||
|
struct SelfObject : public LObject
|
||||||
|
{
|
||||||
|
SelfObject(const LObject& obj) : LObject(obj) {}
|
||||||
|
MWBase::LuaManager::ActorControls mControls;
|
||||||
|
};
|
||||||
|
protected:
|
||||||
|
LocalScripts(LuaUtil::LuaState* lua, const LObject& obj);
|
||||||
|
SelfObject mData;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MWLUA_LOCALSCRIPTS_H
|
39
apps/openmw/mwlua/luabindings.cpp
Normal file
39
apps/openmw/mwlua/luabindings.cpp
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#include "luabindings.hpp"
|
||||||
|
|
||||||
|
#include <components/lua/luastate.hpp>
|
||||||
|
|
||||||
|
#include "eventqueue.hpp"
|
||||||
|
#include "worldview.hpp"
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
|
||||||
|
sol::table initCorePackage(const Context& context)
|
||||||
|
{
|
||||||
|
sol::table api(context.mLua->sol(), sol::create);
|
||||||
|
api["sendGlobalEvent"] = [context](std::string eventName, const sol::object& eventData)
|
||||||
|
{
|
||||||
|
context.mGlobalEventQueue->push_back({std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer)});
|
||||||
|
};
|
||||||
|
return context.mLua->makeReadOnly(api);
|
||||||
|
}
|
||||||
|
|
||||||
|
sol::table initWorldPackage(const Context& context)
|
||||||
|
{
|
||||||
|
sol::table api(context.mLua->sol(), sol::create);
|
||||||
|
WorldView* worldView = context.mWorldView;
|
||||||
|
api["activeActors"] = GObjectList{worldView->getActorsInScene()};
|
||||||
|
return context.mLua->makeReadOnly(api);
|
||||||
|
}
|
||||||
|
|
||||||
|
sol::table initNearbyPackage(const Context& context)
|
||||||
|
{
|
||||||
|
sol::table api(context.mLua->sol(), sol::create);
|
||||||
|
WorldView* worldView = context.mWorldView;
|
||||||
|
api["actors"] = LObjectList{worldView->getActorsInScene()};
|
||||||
|
api["items"] = LObjectList{worldView->getItemsInScene()};
|
||||||
|
return context.mLua->makeReadOnly(api);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
36
apps/openmw/mwlua/luabindings.hpp
Normal file
36
apps/openmw/mwlua/luabindings.hpp
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#ifndef MWLUA_LUABINDINGS_H
|
||||||
|
#define MWLUA_LUABINDINGS_H
|
||||||
|
|
||||||
|
#include <components/lua/luastate.hpp>
|
||||||
|
#include <components/lua/serialization.hpp>
|
||||||
|
|
||||||
|
#include "eventqueue.hpp"
|
||||||
|
#include "object.hpp"
|
||||||
|
#include "worldview.hpp"
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
class LuaManager;
|
||||||
|
|
||||||
|
struct Context
|
||||||
|
{
|
||||||
|
LuaManager* mLuaManager;
|
||||||
|
LuaUtil::LuaState* mLua;
|
||||||
|
LuaUtil::UserdataSerializer* mSerializer;
|
||||||
|
WorldView* mWorldView;
|
||||||
|
LocalEventQueue* mLocalEventQueue;
|
||||||
|
GlobalEventQueue* mGlobalEventQueue;
|
||||||
|
};
|
||||||
|
|
||||||
|
sol::table initCorePackage(const Context&);
|
||||||
|
sol::table initWorldPackage(const Context&);
|
||||||
|
sol::table initNearbyPackage(const Context&);
|
||||||
|
|
||||||
|
// Implemented in objectbindings.cpp
|
||||||
|
void initObjectBindingsForLocalScripts(const Context&);
|
||||||
|
void initObjectBindingsForGlobalScripts(const Context&);
|
||||||
|
|
||||||
|
// openmw.self package is implemented in localscripts.cpp
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MWLUA_LUABINDINGS_H
|
202
apps/openmw/mwlua/luamanagerimp.cpp
Normal file
202
apps/openmw/mwlua/luamanagerimp.cpp
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
#include "luamanagerimp.hpp"
|
||||||
|
|
||||||
|
#include <components/debug/debuglog.hpp>
|
||||||
|
#include <components/lua/utilpackage.hpp>
|
||||||
|
|
||||||
|
#include "../mwworld/class.hpp"
|
||||||
|
#include "../mwworld/ptr.hpp"
|
||||||
|
|
||||||
|
#include "luabindings.hpp"
|
||||||
|
#include "userdataserializer.hpp"
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
|
||||||
|
LuaManager::LuaManager(const VFS::Manager* vfs) : mLua(vfs)
|
||||||
|
{
|
||||||
|
Log(Debug::Info) << "Lua version: " << LuaUtil::getLuaVersion();
|
||||||
|
mGlobalSerializer = createUserdataSerializer(false, mWorldView.getObjectRegistry());
|
||||||
|
mLocalSerializer = createUserdataSerializer(true, mWorldView.getObjectRegistry());
|
||||||
|
mGlobalScripts.setSerializer(mGlobalSerializer.get());
|
||||||
|
|
||||||
|
Context context;
|
||||||
|
context.mLuaManager = this;
|
||||||
|
context.mLua = &mLua;
|
||||||
|
context.mWorldView = &mWorldView;
|
||||||
|
context.mLocalEventQueue = &mLocalEvents;
|
||||||
|
context.mGlobalEventQueue = &mGlobalEvents;
|
||||||
|
context.mSerializer = mGlobalSerializer.get();
|
||||||
|
|
||||||
|
Context localContext = context;
|
||||||
|
localContext.mSerializer = mLocalSerializer.get();
|
||||||
|
|
||||||
|
initObjectBindingsForGlobalScripts(context);
|
||||||
|
initObjectBindingsForLocalScripts(localContext);
|
||||||
|
LocalScripts::initializeSelfPackage(localContext);
|
||||||
|
|
||||||
|
mLua.addCommonPackage("openmw.util", LuaUtil::initUtilPackage(mLua.sol()));
|
||||||
|
mLua.addCommonPackage("openmw.core", initCorePackage(context));
|
||||||
|
mGlobalScripts.addPackage("openmw.world", initWorldPackage(context));
|
||||||
|
mNearbyPackage = initNearbyPackage(localContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaManager::init()
|
||||||
|
{
|
||||||
|
mKeyPressEvents.clear();
|
||||||
|
if (mGlobalScripts.addNewScript("test.lua"))
|
||||||
|
Log(Debug::Info) << "Global script started: test.lua";
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaManager::update(bool paused, float dt)
|
||||||
|
{
|
||||||
|
mWorldView.update();
|
||||||
|
|
||||||
|
if (paused)
|
||||||
|
{
|
||||||
|
mKeyPressEvents.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<GlobalEvent> globalEvents = std::move(mGlobalEvents);
|
||||||
|
std::vector<LocalEvent> localEvents = std::move(mLocalEvents);
|
||||||
|
mGlobalEvents = std::vector<GlobalEvent>();
|
||||||
|
mLocalEvents = std::vector<LocalEvent>();
|
||||||
|
|
||||||
|
for (GlobalEvent& e : globalEvents)
|
||||||
|
mGlobalScripts.receiveEvent(e.eventName, e.eventData);
|
||||||
|
for (LocalEvent& e : localEvents)
|
||||||
|
{
|
||||||
|
LObject obj(e.dest, mWorldView.getObjectRegistry());
|
||||||
|
LocalScripts* scripts = obj.isValid() ? obj.ptr().getRefData().getLuaScripts() : nullptr;
|
||||||
|
if (scripts)
|
||||||
|
scripts->receiveEvent(e.eventName, e.eventData);
|
||||||
|
else
|
||||||
|
Log(Debug::Debug) << "Ignored event " << e.eventName << " to L" << idToString(e.dest)
|
||||||
|
<< ". Object not found or has no attached scripts";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mPlayerChanged)
|
||||||
|
{
|
||||||
|
mPlayerChanged = false;
|
||||||
|
mGlobalScripts.playerAdded(GObject(getId(mPlayer), mWorldView.getObjectRegistry()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mPlayerScripts)
|
||||||
|
{
|
||||||
|
for (const SDL_Keysym key : mKeyPressEvents)
|
||||||
|
mPlayerScripts->keyPress(key.sym, key.mod);
|
||||||
|
}
|
||||||
|
mKeyPressEvents.clear();
|
||||||
|
|
||||||
|
for (ObjectId id : mActorAddedEvents)
|
||||||
|
mGlobalScripts.actorActive(GObject(id, mWorldView.getObjectRegistry()));
|
||||||
|
mActorAddedEvents.clear();
|
||||||
|
|
||||||
|
mGlobalScripts.update(dt);
|
||||||
|
for (LocalScripts* scripts : mActiveLocalScripts)
|
||||||
|
scripts->update(dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaManager::applyQueuedChanges()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaManager::clear()
|
||||||
|
{
|
||||||
|
mActiveLocalScripts.clear();
|
||||||
|
mLocalEvents.clear();
|
||||||
|
mGlobalEvents.clear();
|
||||||
|
mKeyPressEvents.clear();
|
||||||
|
mActorAddedEvents.clear();
|
||||||
|
mPlayerChanged = false;
|
||||||
|
mPlayerScripts = nullptr;
|
||||||
|
mWorldView.clear();
|
||||||
|
if (!mPlayer.isEmpty())
|
||||||
|
{
|
||||||
|
mPlayer.getCellRef().unsetRefNum();
|
||||||
|
mPlayer.getRefData().setLuaScripts(nullptr);
|
||||||
|
mPlayer = MWWorld::Ptr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaManager::objectAddedToScene(const MWWorld::Ptr& ptr)
|
||||||
|
{
|
||||||
|
mWorldView.objectAddedToScene(ptr); // assigns generated RefNum if it is not set yet.
|
||||||
|
|
||||||
|
LocalScripts* localScripts = ptr.getRefData().getLuaScripts();
|
||||||
|
if (localScripts)
|
||||||
|
mActiveLocalScripts.insert(localScripts);
|
||||||
|
|
||||||
|
if (ptr.getClass().isActor() && ptr != mPlayer)
|
||||||
|
mActorAddedEvents.push_back(getId(ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaManager::setupPlayer(const MWWorld::Ptr& ptr)
|
||||||
|
{
|
||||||
|
if (!mPlayer.isEmpty())
|
||||||
|
throw std::logic_error("Player is initialized twice");
|
||||||
|
mWorldView.objectAddedToScene(ptr);
|
||||||
|
mPlayer = ptr;
|
||||||
|
MWWorld::RefData& refData = ptr.getRefData();
|
||||||
|
if (!refData.getLuaScripts())
|
||||||
|
createLocalScripts(ptr);
|
||||||
|
if (!mPlayerScripts)
|
||||||
|
throw std::logic_error("mPlayerScripts not initialized");
|
||||||
|
mActiveLocalScripts.insert(mPlayerScripts);
|
||||||
|
mPlayerChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaManager::objectRemovedFromScene(const MWWorld::Ptr& ptr)
|
||||||
|
{
|
||||||
|
mWorldView.objectRemovedFromScene(ptr);
|
||||||
|
LocalScripts* localScripts = ptr.getRefData().getLuaScripts();
|
||||||
|
if (localScripts)
|
||||||
|
mActiveLocalScripts.erase(localScripts);
|
||||||
|
|
||||||
|
// TODO: call mWorldView.objectUnloaded if object is unloaded from memory (does it ever happen?) and ptr becomes invalid.
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaManager::keyPressed(const SDL_KeyboardEvent& arg)
|
||||||
|
{
|
||||||
|
mKeyPressEvents.push_back(arg.keysym);
|
||||||
|
}
|
||||||
|
|
||||||
|
const MWBase::LuaManager::ActorControls* LuaManager::getActorControls(const MWWorld::Ptr& ptr) const
|
||||||
|
{
|
||||||
|
LocalScripts* localScripts = ptr.getRefData().getLuaScripts();
|
||||||
|
if (!localScripts)
|
||||||
|
return nullptr;
|
||||||
|
return localScripts->getActorControls();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaManager::addLocalScript(const MWWorld::Ptr& ptr, const std::string& scriptPath)
|
||||||
|
{
|
||||||
|
MWWorld::RefData& refData = ptr.getRefData();
|
||||||
|
if (!refData.getLuaScripts())
|
||||||
|
mActiveLocalScripts.insert(createLocalScripts(ptr));
|
||||||
|
refData.getLuaScripts()->addNewScript(scriptPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalScripts* LuaManager::createLocalScripts(const MWWorld::Ptr& ptr)
|
||||||
|
{
|
||||||
|
std::unique_ptr<LocalScripts> scripts;
|
||||||
|
// When loading a game, it can be called before LuaManager::setPlayer,
|
||||||
|
// so we can't just check ptr == mPlayer here.
|
||||||
|
if (*ptr.getCellRef().getRefIdPtr() == "player")
|
||||||
|
{
|
||||||
|
mPlayerScripts = new PlayerScripts(&mLua, LObject(getId(ptr), mWorldView.getObjectRegistry()));
|
||||||
|
scripts = std::unique_ptr<LocalScripts>(mPlayerScripts);
|
||||||
|
// TODO: scripts->addPackage("openmw.ui", ...);
|
||||||
|
// TODO: scripts->addPackage("openmw.camera", ...);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
scripts = LocalScripts::create(&mLua, LObject(getId(ptr), mWorldView.getObjectRegistry()));
|
||||||
|
scripts->addPackage("openmw.nearby", mNearbyPackage);
|
||||||
|
scripts->setSerializer(mLocalSerializer.get());
|
||||||
|
|
||||||
|
MWWorld::RefData& refData = ptr.getRefData();
|
||||||
|
refData.setLuaScripts(std::move(scripts));
|
||||||
|
return refData.getLuaScripts();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
78
apps/openmw/mwlua/luamanagerimp.hpp
Normal file
78
apps/openmw/mwlua/luamanagerimp.hpp
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
#ifndef MWLUA_LUAMANAGERIMP_H
|
||||||
|
#define MWLUA_LUAMANAGERIMP_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include <components/lua/luastate.hpp>
|
||||||
|
|
||||||
|
#include "../mwbase/luamanager.hpp"
|
||||||
|
|
||||||
|
#include "object.hpp"
|
||||||
|
#include "eventqueue.hpp"
|
||||||
|
#include "globalscripts.hpp"
|
||||||
|
#include "localscripts.hpp"
|
||||||
|
#include "playerscripts.hpp"
|
||||||
|
#include "worldview.hpp"
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
|
||||||
|
class LuaManager : public MWBase::LuaManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LuaManager(const VFS::Manager* vfs);
|
||||||
|
~LuaManager() {}
|
||||||
|
|
||||||
|
// Called by engine.cpp when environment is fully initialized.
|
||||||
|
void init();
|
||||||
|
|
||||||
|
// Called by engine.cpp every frame. For performance reasons it works in a separate
|
||||||
|
// thread (in parallel with osg Cull). Can not use scene graph.
|
||||||
|
void update(bool paused, float dt);
|
||||||
|
|
||||||
|
// Called by engine.cpp from the main thread. Can use scene graph.
|
||||||
|
void applyQueuedChanges();
|
||||||
|
|
||||||
|
// Available everywhere through the MWBase::LuaManager interface.
|
||||||
|
// LuaManager queues these events and propagates to scripts on the next `update` call.
|
||||||
|
void newGameStarted() override { mGlobalScripts.newGameStarted(); }
|
||||||
|
void objectAddedToScene(const MWWorld::Ptr& ptr) override;
|
||||||
|
void objectRemovedFromScene(const MWWorld::Ptr& ptr) override;
|
||||||
|
void keyPressed(const SDL_KeyboardEvent &arg) override;
|
||||||
|
|
||||||
|
const MWBase::LuaManager::ActorControls* getActorControls(const MWWorld::Ptr&) const override;
|
||||||
|
|
||||||
|
void clear() override; // should be called before loading game or starting a new game to reset internal state.
|
||||||
|
void setupPlayer(const MWWorld::Ptr& ptr) override; // Should be called once after each "clear".
|
||||||
|
|
||||||
|
// Used only in luabindings.cpp
|
||||||
|
void addLocalScript(const MWWorld::Ptr&, const std::string& scriptPath);
|
||||||
|
|
||||||
|
private:
|
||||||
|
LocalScripts* createLocalScripts(const MWWorld::Ptr& ptr);
|
||||||
|
|
||||||
|
LuaUtil::LuaState mLua;
|
||||||
|
sol::table mNearbyPackage;
|
||||||
|
|
||||||
|
GlobalScripts mGlobalScripts{&mLua};
|
||||||
|
std::set<LocalScripts*> mActiveLocalScripts;
|
||||||
|
WorldView mWorldView;
|
||||||
|
|
||||||
|
bool mPlayerChanged = false;
|
||||||
|
MWWorld::Ptr mPlayer;
|
||||||
|
PlayerScripts* mPlayerScripts = nullptr;
|
||||||
|
|
||||||
|
GlobalEventQueue mGlobalEvents;
|
||||||
|
LocalEventQueue mLocalEvents;
|
||||||
|
|
||||||
|
std::unique_ptr<LuaUtil::UserdataSerializer> mGlobalSerializer;
|
||||||
|
std::unique_ptr<LuaUtil::UserdataSerializer> mLocalSerializer;
|
||||||
|
|
||||||
|
std::vector<SDL_Keysym> mKeyPressEvents;
|
||||||
|
std::vector<ObjectId> mActorAddedEvents;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MWLUA_LUAMANAGERIMP_H
|
111
apps/openmw/mwlua/object.cpp
Normal file
111
apps/openmw/mwlua/object.cpp
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
#include "object.hpp"
|
||||||
|
|
||||||
|
#include <components/esm/loadnpc.hpp>
|
||||||
|
#include <components/esm/loadcrea.hpp>
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
|
||||||
|
std::string idToString(const ObjectId& id)
|
||||||
|
{
|
||||||
|
return std::to_string(id.mIndex) + "_" + std::to_string(id.mContentFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Object::toString() const
|
||||||
|
{
|
||||||
|
std::string res = idToString(mId);
|
||||||
|
if (isValid())
|
||||||
|
{
|
||||||
|
res.append(" (");
|
||||||
|
res.append(type());
|
||||||
|
res.append(", ");
|
||||||
|
res.append(*ptr().getCellRef().getRefIdPtr());
|
||||||
|
res.append(")");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
res.append(" (not found)");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view Object::type() const
|
||||||
|
{
|
||||||
|
if (*ptr().getCellRef().getRefIdPtr() == "player")
|
||||||
|
return "Player";
|
||||||
|
const std::string& typeName = ptr().getTypeName();
|
||||||
|
if (typeName == typeid(ESM::NPC).name())
|
||||||
|
return "NPC";
|
||||||
|
else if (typeName == typeid(ESM::Creature).name())
|
||||||
|
return "Creature";
|
||||||
|
else
|
||||||
|
return typeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Object::isValid() const
|
||||||
|
{
|
||||||
|
if (mLastUpdate < mObjectRegistry->mUpdateCounter)
|
||||||
|
{
|
||||||
|
updatePtr();
|
||||||
|
mLastUpdate = mObjectRegistry->mUpdateCounter;
|
||||||
|
}
|
||||||
|
return !mPtr.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
const MWWorld::Ptr& Object::ptr() const
|
||||||
|
{
|
||||||
|
if (!isValid())
|
||||||
|
throw std::runtime_error("Object is not available: " + idToString(mId));
|
||||||
|
return mPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectRegistry::update()
|
||||||
|
{
|
||||||
|
if (mChanged)
|
||||||
|
{
|
||||||
|
mUpdateCounter++;
|
||||||
|
mChanged = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectRegistry::clear()
|
||||||
|
{
|
||||||
|
mObjectMapping.clear();
|
||||||
|
mChanged = false;
|
||||||
|
mUpdateCounter = 0;
|
||||||
|
mLastAssignedId.unset();
|
||||||
|
}
|
||||||
|
|
||||||
|
MWWorld::Ptr ObjectRegistry::getPtr(ObjectId id, bool onlyActive)
|
||||||
|
{
|
||||||
|
MWWorld::Ptr ptr;
|
||||||
|
auto it = mObjectMapping.find(id);
|
||||||
|
if (it != mObjectMapping.end())
|
||||||
|
ptr = it->second;
|
||||||
|
if (onlyActive)
|
||||||
|
{
|
||||||
|
// TODO: add flag `isActive` to LiveCellRefBase. Return empty Ptr if the flag is not set.
|
||||||
|
// Needed because in multiplayer mode inactive objects will not be synchronized, so will likely be out of date.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: If Ptr is empty then try to load the object from esp/esm.
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectId ObjectRegistry::registerPtr(const MWWorld::Ptr& ptr)
|
||||||
|
{
|
||||||
|
ObjectId id = ptr.getCellRef().getOrAssignRefNum(mLastAssignedId);
|
||||||
|
mChanged = true;
|
||||||
|
mObjectMapping[id] = ptr;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectId ObjectRegistry::deregisterPtr(const MWWorld::Ptr& ptr)
|
||||||
|
{
|
||||||
|
ObjectId id = getId(ptr);
|
||||||
|
mChanged = true;
|
||||||
|
mObjectMapping.erase(id);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
103
apps/openmw/mwlua/object.hpp
Normal file
103
apps/openmw/mwlua/object.hpp
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
#ifndef MWLUA_OBJECT_H
|
||||||
|
#define MWLUA_OBJECT_H
|
||||||
|
|
||||||
|
#include <components/esm/cellref.hpp>
|
||||||
|
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/world.hpp"
|
||||||
|
|
||||||
|
#include "../mwworld/ptr.hpp"
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
// ObjectId is a unique identifier of a game object.
|
||||||
|
// It can change only if the order of content files was change.
|
||||||
|
using ObjectId = ESM::RefNum;
|
||||||
|
inline const ObjectId& getId(const MWWorld::Ptr& ptr) { return ptr.getCellRef().getRefNum(); }
|
||||||
|
std::string idToString(const ObjectId& id);
|
||||||
|
|
||||||
|
// Holds a mapping ObjectId -> MWWord::Ptr.
|
||||||
|
class ObjectRegistry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ObjectRegistry() { mLastAssignedId.unset(); }
|
||||||
|
|
||||||
|
void update(); // Should be called every frame.
|
||||||
|
void clear(); // Should be called before starting or loading a new game.
|
||||||
|
|
||||||
|
ObjectId registerPtr(const MWWorld::Ptr& ptr);
|
||||||
|
ObjectId deregisterPtr(const MWWorld::Ptr& ptr);
|
||||||
|
|
||||||
|
// Returns Ptr by id. If object is not found, returns empty Ptr.
|
||||||
|
// If onlyActive = true, returns non-empty ptr only if it is registered and is in an active cell.
|
||||||
|
// If onlyActive = false, tries to load and register the object if it is not loaded yet.
|
||||||
|
// NOTE: `onlyActive` logic is not yet implemented.
|
||||||
|
MWWorld::Ptr getPtr(ObjectId id, bool onlyActive);
|
||||||
|
|
||||||
|
// Needed only for saving/loading.
|
||||||
|
const ObjectId& getLastAssignedId() const { return mLastAssignedId; }
|
||||||
|
void setLastAssignedId(ObjectId id) { mLastAssignedId = id; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class Object;
|
||||||
|
friend class GObject;
|
||||||
|
friend class LObject;
|
||||||
|
|
||||||
|
bool mChanged = false;
|
||||||
|
int64_t mUpdateCounter = 0;
|
||||||
|
std::map<ObjectId, MWWorld::Ptr> mObjectMapping;
|
||||||
|
ObjectId mLastAssignedId;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Lua scripts can't use MWWorld::Ptr directly, because lifetime of a script can be longer than lifetime of Ptr.
|
||||||
|
// `GObject` and `LObject` are intended to be passed to Lua as a userdata.
|
||||||
|
// It automatically updates the underlying Ptr when needed.
|
||||||
|
class Object
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Object(ObjectId id, ObjectRegistry* reg) : mId(id), mObjectRegistry(reg) {}
|
||||||
|
virtual ~Object() {}
|
||||||
|
ObjectId id() const { return mId; }
|
||||||
|
|
||||||
|
std::string toString() const;
|
||||||
|
std::string_view type() const;
|
||||||
|
|
||||||
|
// Updates and returns the underlying Ptr. Throws an exception if object is not available.
|
||||||
|
const MWWorld::Ptr& ptr() const;
|
||||||
|
|
||||||
|
// Returns `true` if calling `ptr()` is safe.
|
||||||
|
bool isValid() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void updatePtr() const = 0;
|
||||||
|
|
||||||
|
const ObjectId mId;
|
||||||
|
ObjectRegistry* mObjectRegistry;
|
||||||
|
|
||||||
|
mutable MWWorld::Ptr mPtr;
|
||||||
|
mutable int64_t mLastUpdate = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Used only in local scripts
|
||||||
|
class LObject : public Object
|
||||||
|
{
|
||||||
|
using Object::Object;
|
||||||
|
void updatePtr() const final { mPtr = mObjectRegistry->getPtr(mId, true); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Used only in global scripts
|
||||||
|
class GObject : public Object
|
||||||
|
{
|
||||||
|
using Object::Object;
|
||||||
|
void updatePtr() const final { mPtr = mObjectRegistry->getPtr(mId, false); }
|
||||||
|
};
|
||||||
|
|
||||||
|
using ObjectIdList = std::shared_ptr<std::vector<ObjectId>>;
|
||||||
|
template <typename Obj>
|
||||||
|
struct ObjectList { ObjectIdList mIds; };
|
||||||
|
using GObjectList = ObjectList<GObject>;
|
||||||
|
using LObjectList = ObjectList<LObject>;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MWLUA_OBJECT_H
|
106
apps/openmw/mwlua/objectbindings.cpp
Normal file
106
apps/openmw/mwlua/objectbindings.cpp
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
#include "luabindings.hpp"
|
||||||
|
|
||||||
|
#include <components/lua/luastate.hpp>
|
||||||
|
|
||||||
|
#include "eventqueue.hpp"
|
||||||
|
#include "luamanagerimp.hpp"
|
||||||
|
|
||||||
|
namespace sol
|
||||||
|
{
|
||||||
|
template <>
|
||||||
|
struct is_automagical<MWLua::LObject> : std::false_type {};
|
||||||
|
template <>
|
||||||
|
struct is_automagical<MWLua::GObject> : std::false_type {};
|
||||||
|
template <>
|
||||||
|
struct is_automagical<MWLua::LObjectList> : std::false_type {};
|
||||||
|
template <>
|
||||||
|
struct is_automagical<MWLua::GObjectList> : std::false_type {};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
|
||||||
|
template <class ObjectT>
|
||||||
|
static void registerObjectList(const std::string& prefix, const Context& context)
|
||||||
|
{
|
||||||
|
using ListT = ObjectList<ObjectT>;
|
||||||
|
sol::state& lua = context.mLua->sol();
|
||||||
|
ObjectRegistry* registry = context.mWorldView->getObjectRegistry();
|
||||||
|
sol::usertype<ListT> listT = lua.new_usertype<ListT>(prefix + "ObjectList");
|
||||||
|
listT[sol::meta_function::to_string] =
|
||||||
|
[](const ListT& list) { return "{" + std::to_string(list.mIds->size()) + " objects}"; };
|
||||||
|
listT[sol::meta_function::length] = [](const ListT& list) { return list.mIds->size(); };
|
||||||
|
listT[sol::meta_function::index] = [registry](const ListT& list, size_t index)
|
||||||
|
{
|
||||||
|
if (index > 0 && index <= list.mIds->size())
|
||||||
|
return ObjectT((*list.mIds)[index - 1], registry);
|
||||||
|
else
|
||||||
|
throw std::runtime_error("Index out of range");
|
||||||
|
};
|
||||||
|
listT["ipairs"] = [registry](const ListT& list)
|
||||||
|
{
|
||||||
|
auto iter = [registry](const ListT& l, int64_t i) -> sol::optional<std::tuple<int64_t, ObjectT>>
|
||||||
|
{
|
||||||
|
if (i >= 0 && i < static_cast<int64_t>(l.mIds->size()))
|
||||||
|
return std::make_tuple(i + 1, ObjectT((*l.mIds)[i], registry));
|
||||||
|
else
|
||||||
|
return sol::nullopt;
|
||||||
|
};
|
||||||
|
return std::make_tuple(iter, list, 0);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class ObjectT>
|
||||||
|
static void addBasicBindings(sol::usertype<ObjectT>& objectT, const Context& context)
|
||||||
|
{
|
||||||
|
objectT["isValid"] = [](const ObjectT& o) { return o.isValid(); };
|
||||||
|
objectT["recordId"] = sol::readonly_property([](const ObjectT& o) -> std::string
|
||||||
|
{
|
||||||
|
return o.ptr().getCellRef().getRefId();
|
||||||
|
});
|
||||||
|
objectT["position"] = sol::readonly_property([](const ObjectT& o) -> osg::Vec3f
|
||||||
|
{
|
||||||
|
return o.ptr().getRefData().getPosition().asVec3();
|
||||||
|
});
|
||||||
|
objectT["rotation"] = sol::readonly_property([](const ObjectT& o) -> osg::Vec3f
|
||||||
|
{
|
||||||
|
return o.ptr().getRefData().getPosition().asRotationVec3();
|
||||||
|
});
|
||||||
|
objectT["type"] = sol::readonly_property(&ObjectT::type);
|
||||||
|
objectT[sol::meta_function::equal_to] = [](const ObjectT& a, const ObjectT& b) { return a.id() == b.id(); };
|
||||||
|
objectT[sol::meta_function::to_string] = &ObjectT::toString;
|
||||||
|
objectT["sendEvent"] = [context](const ObjectT& dest, std::string eventName, const sol::object& eventData)
|
||||||
|
{
|
||||||
|
context.mLocalEventQueue->push_back({dest.id(), std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer)});
|
||||||
|
};
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<ObjectT, GObject>)
|
||||||
|
{ // Only for global scripts
|
||||||
|
objectT["addScript"] = [luaManager=context.mLuaManager](const GObject& object, const std::string& path)
|
||||||
|
{
|
||||||
|
luaManager->addLocalScript(object.ptr(), path);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class ObjectT>
|
||||||
|
static void initObjectBindings(const std::string& prefix, const Context& context)
|
||||||
|
{
|
||||||
|
sol::usertype<ObjectT> objectT = context.mLua->sol().new_usertype<ObjectT>(prefix + "Object");
|
||||||
|
addBasicBindings<ObjectT>(objectT, context);
|
||||||
|
|
||||||
|
registerObjectList<ObjectT>(prefix, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void initObjectBindingsForLocalScripts(const Context& context)
|
||||||
|
{
|
||||||
|
initObjectBindings<LObject>("L", context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void initObjectBindingsForGlobalScripts(const Context& context)
|
||||||
|
{
|
||||||
|
initObjectBindings<GObject>("G", context);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
25
apps/openmw/mwlua/playerscripts.hpp
Normal file
25
apps/openmw/mwlua/playerscripts.hpp
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef MWLUA_PLAYERSCRIPTS_H
|
||||||
|
#define MWLUA_PLAYERSCRIPTS_H
|
||||||
|
|
||||||
|
#include "localscripts.hpp"
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
|
||||||
|
class PlayerScripts : public LocalScripts
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PlayerScripts(LuaUtil::LuaState* lua, const LObject& obj) : LocalScripts(lua, obj)
|
||||||
|
{
|
||||||
|
registerEngineHandlers({&mKeyPressHandlers});
|
||||||
|
}
|
||||||
|
|
||||||
|
void keyPress(int sym, int mod) { callEngineHandlers(mKeyPressHandlers, sym, mod); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
EngineHandlerList mKeyPressHandlers{"onKeyPress"};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MWLUA_PLAYERSCRIPTS_H
|
64
apps/openmw/mwlua/userdataserializer.cpp
Normal file
64
apps/openmw/mwlua/userdataserializer.cpp
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
#include "userdataserializer.hpp"
|
||||||
|
|
||||||
|
#include <components/lua/serialization.hpp>
|
||||||
|
#include <components/misc/endianness.hpp>
|
||||||
|
|
||||||
|
#include "object.hpp"
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
|
||||||
|
class Serializer final : public LuaUtil::UserdataSerializer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Serializer(bool localSerializer, ObjectRegistry* registry)
|
||||||
|
: mLocalSerializer(localSerializer), mObjectRegistry(registry) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Appends serialized sol::userdata to the end of BinaryData.
|
||||||
|
// Returns false if this type of userdata is not supported by this serializer.
|
||||||
|
bool serialize(LuaUtil::BinaryData& out, const sol::userdata& data) const override
|
||||||
|
{
|
||||||
|
if (data.is<GObject>() || data.is<LObject>())
|
||||||
|
{
|
||||||
|
ObjectId id = data.as<Object>().id();
|
||||||
|
static_assert(sizeof(ObjectId) == 8);
|
||||||
|
id.mIndex = Misc::toLittleEndian(id.mIndex);
|
||||||
|
id.mContentFile = Misc::toLittleEndian(id.mContentFile);
|
||||||
|
append(out, "o", &id, sizeof(ObjectId));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserializes userdata of type "typeName" from binaryData. Should push the result on stack using sol::stack::push.
|
||||||
|
// Returns false if this type is not supported by this serializer.
|
||||||
|
bool deserialize(std::string_view typeName, std::string_view binaryData, sol::state& lua) const override
|
||||||
|
{
|
||||||
|
if (typeName == "o")
|
||||||
|
{
|
||||||
|
if (binaryData.size() != sizeof(ObjectId))
|
||||||
|
throw std::runtime_error("Incorrect serialization format. Size of ObjectId doesn't match.");
|
||||||
|
ObjectId id;
|
||||||
|
std::memcpy(&id, binaryData.data(), sizeof(ObjectId));
|
||||||
|
id.mIndex = Misc::fromLittleEndian(id.mIndex);
|
||||||
|
id.mContentFile = Misc::fromLittleEndian(id.mContentFile);
|
||||||
|
if (mLocalSerializer)
|
||||||
|
sol::stack::push<LObject>(lua, LObject(id, mObjectRegistry));
|
||||||
|
else
|
||||||
|
sol::stack::push<GObject>(lua, GObject(id, mObjectRegistry));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mLocalSerializer;
|
||||||
|
ObjectRegistry* mObjectRegistry;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<LuaUtil::UserdataSerializer> createUserdataSerializer(bool local, ObjectRegistry* registry)
|
||||||
|
{
|
||||||
|
return std::make_unique<Serializer>(local, registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
19
apps/openmw/mwlua/userdataserializer.hpp
Normal file
19
apps/openmw/mwlua/userdataserializer.hpp
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef MWLUA_USERDATASERIALIZER_H
|
||||||
|
#define MWLUA_USERDATASERIALIZER_H
|
||||||
|
|
||||||
|
#include "object.hpp"
|
||||||
|
|
||||||
|
namespace LuaUtil
|
||||||
|
{
|
||||||
|
class UserdataSerializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
// UserdataSerializer is an extension for components/lua/serialization.hpp
|
||||||
|
// Needed to serialize references to objects.
|
||||||
|
// If local=true, then during deserialization creates LObject, otherwise creates GObject.
|
||||||
|
std::unique_ptr<LuaUtil::UserdataSerializer> createUserdataSerializer(bool local, ObjectRegistry* registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MWLUA_USERDATASERIALIZER_H
|
68
apps/openmw/mwlua/worldview.cpp
Normal file
68
apps/openmw/mwlua/worldview.cpp
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
#include "worldview.hpp"
|
||||||
|
|
||||||
|
#include "../mwworld/class.hpp"
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
|
||||||
|
void WorldView::update()
|
||||||
|
{
|
||||||
|
mObjectRegistry.update();
|
||||||
|
mActorsInScene.updateList();
|
||||||
|
mItemsInScene.updateList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldView::clear()
|
||||||
|
{
|
||||||
|
mObjectRegistry.clear();
|
||||||
|
mActorsInScene.clear();
|
||||||
|
mItemsInScene.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldView::objectAddedToScene(const MWWorld::Ptr& ptr)
|
||||||
|
{
|
||||||
|
if (ptr.getClass().isActor())
|
||||||
|
addToGroup(mActorsInScene, ptr);
|
||||||
|
else
|
||||||
|
addToGroup(mItemsInScene, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldView::objectRemovedFromScene(const MWWorld::Ptr& ptr)
|
||||||
|
{
|
||||||
|
if (ptr.getClass().isActor())
|
||||||
|
removeFromGroup(mActorsInScene, ptr);
|
||||||
|
else
|
||||||
|
removeFromGroup(mItemsInScene, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldView::ObjectGroup::updateList()
|
||||||
|
{
|
||||||
|
if (mChanged)
|
||||||
|
{
|
||||||
|
mList->clear();
|
||||||
|
for (const ObjectId& id : mSet)
|
||||||
|
mList->push_back(id);
|
||||||
|
mChanged = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldView::ObjectGroup::clear()
|
||||||
|
{
|
||||||
|
mChanged = false;
|
||||||
|
mList->clear();
|
||||||
|
mSet.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldView::addToGroup(ObjectGroup& group, const MWWorld::Ptr& ptr)
|
||||||
|
{
|
||||||
|
group.mSet.insert(getId(ptr));
|
||||||
|
group.mChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldView::removeFromGroup(ObjectGroup& group, const MWWorld::Ptr& ptr)
|
||||||
|
{
|
||||||
|
group.mSet.erase(getId(ptr));
|
||||||
|
group.mChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
47
apps/openmw/mwlua/worldview.hpp
Normal file
47
apps/openmw/mwlua/worldview.hpp
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#ifndef MWLUA_WORLDVIEW_H
|
||||||
|
#define MWLUA_WORLDVIEW_H
|
||||||
|
|
||||||
|
#include "object.hpp"
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
|
||||||
|
// Tracks all used game objects.
|
||||||
|
class WorldView
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void update(); // Should be called every frame.
|
||||||
|
void clear(); // Should be called every time before starting or loading a new game.
|
||||||
|
|
||||||
|
ObjectIdList getActorsInScene() const { return mActorsInScene.mList; }
|
||||||
|
ObjectIdList getItemsInScene() const { return mItemsInScene.mList; }
|
||||||
|
|
||||||
|
ObjectRegistry* getObjectRegistry() { return &mObjectRegistry; }
|
||||||
|
|
||||||
|
void objectUnloaded(const MWWorld::Ptr& ptr) { mObjectRegistry.deregisterPtr(ptr); }
|
||||||
|
|
||||||
|
void objectAddedToScene(const MWWorld::Ptr& ptr);
|
||||||
|
void objectRemovedFromScene(const MWWorld::Ptr& ptr);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct ObjectGroup
|
||||||
|
{
|
||||||
|
void updateList();
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
bool mChanged = false;
|
||||||
|
ObjectIdList mList = std::make_shared<std::vector<ObjectId>>();
|
||||||
|
std::set<ObjectId> mSet;
|
||||||
|
};
|
||||||
|
|
||||||
|
void addToGroup(ObjectGroup& group, const MWWorld::Ptr& ptr);
|
||||||
|
void removeFromGroup(ObjectGroup& group, const MWWorld::Ptr& ptr);
|
||||||
|
|
||||||
|
ObjectRegistry mObjectRegistry;
|
||||||
|
ObjectGroup mActorsInScene;
|
||||||
|
ObjectGroup mItemsInScene;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MWLUA_WORLDVIEW_H
|
|
@ -8,6 +8,8 @@
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
|
||||||
|
#include "../mwlua/localscripts.hpp"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
enum RefDataFlags
|
enum RefDataFlags
|
||||||
|
@ -21,6 +23,12 @@ enum RefDataFlags
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
|
|
||||||
|
void RefData::setLuaScripts(std::unique_ptr<MWLua::LocalScripts>&& scripts)
|
||||||
|
{
|
||||||
|
mChanged = true;
|
||||||
|
mLuaScripts = std::move(scripts);
|
||||||
|
}
|
||||||
|
|
||||||
void RefData::copy (const RefData& refData)
|
void RefData::copy (const RefData& refData)
|
||||||
{
|
{
|
||||||
mBaseNode = refData.mBaseNode;
|
mBaseNode = refData.mBaseNode;
|
||||||
|
@ -36,12 +44,14 @@ namespace MWWorld
|
||||||
mAnimationState = refData.mAnimationState;
|
mAnimationState = refData.mAnimationState;
|
||||||
|
|
||||||
mCustomData = refData.mCustomData ? refData.mCustomData->clone() : nullptr;
|
mCustomData = refData.mCustomData ? refData.mCustomData->clone() : nullptr;
|
||||||
|
mLuaScripts = refData.mLuaScripts;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RefData::cleanup()
|
void RefData::cleanup()
|
||||||
{
|
{
|
||||||
mBaseNode = nullptr;
|
mBaseNode = nullptr;
|
||||||
mCustomData = nullptr;
|
mCustomData = nullptr;
|
||||||
|
mLuaScripts = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
RefData::RefData()
|
RefData::RefData()
|
||||||
|
|
|
@ -22,6 +22,11 @@ namespace ESM
|
||||||
struct ObjectState;
|
struct ObjectState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
class LocalScripts;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -32,6 +37,7 @@ namespace MWWorld
|
||||||
SceneUtil::PositionAttitudeTransform* mBaseNode;
|
SceneUtil::PositionAttitudeTransform* mBaseNode;
|
||||||
|
|
||||||
MWScript::Locals mLocals;
|
MWScript::Locals mLocals;
|
||||||
|
std::shared_ptr<MWLua::LocalScripts> mLuaScripts;
|
||||||
|
|
||||||
/// separate delete flag used for deletion by a content file
|
/// separate delete flag used for deletion by a content file
|
||||||
/// @note not stored in the save game file.
|
/// @note not stored in the save game file.
|
||||||
|
@ -96,6 +102,9 @@ namespace MWWorld
|
||||||
|
|
||||||
void setLocals (const ESM::Script& script);
|
void setLocals (const ESM::Script& script);
|
||||||
|
|
||||||
|
MWLua::LocalScripts* getLuaScripts() { return mLuaScripts.get(); }
|
||||||
|
void setLuaScripts(std::unique_ptr<MWLua::LocalScripts>&&);
|
||||||
|
|
||||||
void setCount (int count);
|
void setCount (int count);
|
||||||
///< Set object count (an object pile is a simple object with a count >1).
|
///< Set object count (an object pile is a simple object with a count >1).
|
||||||
///
|
///
|
||||||
|
|
Loading…
Reference in a new issue