mirror of
https://github.com/OpenMW/openmw.git
synced 2025-03-02 21:09:40 +00:00
More Lua bindings
This commit is contained in:
parent
87b5afb9bf
commit
32218f6dd5
13 changed files with 240 additions and 34 deletions
|
@ -57,7 +57,7 @@ add_openmw_dir (mwscript
|
|||
|
||||
add_openmw_dir (mwlua
|
||||
luamanagerimp localscripts object worldview luabindings userdataserializer eventqueue
|
||||
objectbindings asyncbindings
|
||||
objectbindings asyncbindings camerabindings uibindings
|
||||
)
|
||||
|
||||
add_openmw_dir (mwsound
|
||||
|
|
13
apps/openmw/mwlua/camerabindings.cpp
Normal file
13
apps/openmw/mwlua/camerabindings.cpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
#include "luabindings.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
sol::table initCameraPackage(const Context& context)
|
||||
{
|
||||
sol::table api(context.mLua->sol(), sol::create);
|
||||
// TODO
|
||||
return context.mLua->makeReadOnly(api);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,10 @@
|
|||
#include "localscripts.hpp"
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwmechanics/aisequence.hpp"
|
||||
#include "../mwmechanics/aicombat.hpp"
|
||||
|
||||
namespace sol
|
||||
{
|
||||
template <>
|
||||
|
@ -27,6 +32,28 @@ namespace MWLua
|
|||
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; };
|
||||
selfAPI["getCombatTarget"] = [worldView=context.mWorldView](SelfObject& self) -> sol::optional<LObject>
|
||||
{
|
||||
const MWWorld::Ptr& ptr = self.ptr();
|
||||
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
||||
MWWorld::Ptr target;
|
||||
if (ai.getCombatTarget(target))
|
||||
return LObject(getId(target), worldView->getObjectRegistry());
|
||||
else
|
||||
return {};
|
||||
};
|
||||
selfAPI["stopCombat"] = [](SelfObject& self)
|
||||
{
|
||||
const MWWorld::Ptr& ptr = self.ptr();
|
||||
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
||||
ai.stopCombat();
|
||||
};
|
||||
selfAPI["startCombat"] = [](SelfObject& self, const LObject& target)
|
||||
{
|
||||
const MWWorld::Ptr& ptr = self.ptr();
|
||||
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
||||
ai.stack(MWMechanics::AiCombat(target.ptr()), ptr);
|
||||
};
|
||||
}
|
||||
|
||||
std::unique_ptr<LocalScripts> LocalScripts::create(LuaUtil::LuaState* lua, const LObject& obj)
|
||||
|
|
|
@ -8,6 +8,14 @@
|
|||
namespace MWLua
|
||||
{
|
||||
|
||||
static sol::table definitionList(LuaUtil::LuaState& lua, std::initializer_list<std::string> values)
|
||||
{
|
||||
sol::table res(lua.sol(), sol::create);
|
||||
for (const std::string& v : values)
|
||||
res[v] = v;
|
||||
return lua.makeReadOnly(res);
|
||||
}
|
||||
|
||||
sol::table initCorePackage(const Context& context)
|
||||
{
|
||||
sol::table api(context.mLua->sol(), sol::create);
|
||||
|
@ -17,6 +25,11 @@ namespace MWLua
|
|||
};
|
||||
api["getGameTimeInSeconds"] = [world=context.mWorldView]() { return world->getGameTimeInSeconds(); };
|
||||
api["getGameTimeInHours"] = [world=context.mWorldView]() { return world->getGameTimeInHours(); };
|
||||
api["OBJECT_TYPE"] = definitionList(*context.mLua,
|
||||
{
|
||||
"Activator", "Armor", "Book", "Clothing", "Creature", "Door", "Ingredient",
|
||||
"Light", "Miscellaneous", "NPC", "Player", "Potion", "Static", "Weapon"
|
||||
});
|
||||
return context.mLua->makeReadOnly(api);
|
||||
}
|
||||
|
||||
|
@ -32,7 +45,10 @@ namespace MWLua
|
|||
{
|
||||
sol::table api(context.mLua->sol(), sol::create);
|
||||
WorldView* worldView = context.mWorldView;
|
||||
api["activators"] = LObjectList{worldView->getActivatorsInScene()};
|
||||
api["actors"] = LObjectList{worldView->getActorsInScene()};
|
||||
api["containers"] = LObjectList{worldView->getContainersInScene()};
|
||||
api["doors"] = LObjectList{worldView->getDoorsInScene()};
|
||||
api["items"] = LObjectList{worldView->getItemsInScene()};
|
||||
return context.mLua->makeReadOnly(api);
|
||||
}
|
||||
|
|
|
@ -40,6 +40,12 @@ namespace MWLua
|
|||
};
|
||||
sol::function getAsyncPackageInitializer(const Context&);
|
||||
|
||||
// Implemented in camerabindings.cpp
|
||||
sol::table initCameraPackage(const Context&);
|
||||
|
||||
// Implemented in uibindings.cpp
|
||||
sol::table initUserInterfacePackage(const Context&);
|
||||
|
||||
// openmw.self package is implemented in localscripts.cpp
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
#include <components/lua/utilpackage.hpp>
|
||||
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
|
@ -47,6 +49,8 @@ namespace MWLua
|
|||
mLua.addCommonPackage("openmw.util", LuaUtil::initUtilPackage(mLua.sol()));
|
||||
mLua.addCommonPackage("openmw.core", initCorePackage(context));
|
||||
mGlobalScripts.addPackage("openmw.world", initWorldPackage(context));
|
||||
mCameraPackage = initCameraPackage(localContext);
|
||||
mUserInterfacePackage = initUserInterfacePackage(localContext);
|
||||
mNearbyPackage = initNearbyPackage(localContext);
|
||||
|
||||
auto endsWith = [](std::string_view s, std::string_view suffix)
|
||||
|
@ -151,6 +155,10 @@ namespace MWLua
|
|||
|
||||
void LuaManager::applyQueuedChanges()
|
||||
{
|
||||
MWBase::WindowManager* windowManager = MWBase::Environment::get().getWindowManager();
|
||||
for (const std::string& message : mUIMessages)
|
||||
windowManager->messageBox(message);
|
||||
mUIMessages.clear();
|
||||
}
|
||||
|
||||
void LuaManager::clear()
|
||||
|
@ -238,8 +246,8 @@ namespace MWLua
|
|||
{
|
||||
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", ...);
|
||||
scripts->addPackage("openmw.ui", mUserInterfacePackage);
|
||||
scripts->addPackage("openmw.camera", mCameraPackage);
|
||||
}
|
||||
else
|
||||
scripts = LocalScripts::create(&mLua, LObject(getId(ptr), mWorldView.getObjectRegistry()));
|
||||
|
|
|
@ -48,6 +48,7 @@ namespace MWLua
|
|||
|
||||
// Used only in luabindings.cpp
|
||||
void addLocalScript(const MWWorld::Ptr&, const std::string& scriptPath);
|
||||
void addUIMessage(std::string_view message) { mUIMessages.emplace_back(message); }
|
||||
|
||||
// Saving
|
||||
void write(ESM::ESMWriter& writer, Loading::Listener& progress) override;
|
||||
|
@ -63,6 +64,8 @@ namespace MWLua
|
|||
|
||||
LuaUtil::LuaState mLua;
|
||||
sol::table mNearbyPackage;
|
||||
sol::table mUserInterfacePackage;
|
||||
sol::table mCameraPackage;
|
||||
|
||||
std::vector<std::string> mGlobalScriptList;
|
||||
GlobalScripts mGlobalScripts{&mLua};
|
||||
|
@ -85,6 +88,9 @@ namespace MWLua
|
|||
|
||||
std::vector<SDL_Keysym> mKeyPressEvents;
|
||||
std::vector<ObjectId> mActorAddedEvents;
|
||||
|
||||
// Queued actions that should be done in main thread. Processed by applyQueuedChanges().
|
||||
std::vector<std::string> mUIMessages;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,19 @@
|
|||
#include "object.hpp"
|
||||
|
||||
#include <components/esm/loadnpc.hpp>
|
||||
#include <components/esm/loadcrea.hpp>
|
||||
#include "../mwclass/activator.hpp"
|
||||
#include "../mwclass/armor.hpp"
|
||||
#include "../mwclass/book.hpp"
|
||||
#include "../mwclass/clothing.hpp"
|
||||
#include "../mwclass/container.hpp"
|
||||
#include "../mwclass/creature.hpp"
|
||||
#include "../mwclass/door.hpp"
|
||||
#include "../mwclass/ingredient.hpp"
|
||||
#include "../mwclass/light.hpp"
|
||||
#include "../mwclass/misc.hpp"
|
||||
#include "../mwclass/npc.hpp"
|
||||
#include "../mwclass/potion.hpp"
|
||||
#include "../mwclass/static.hpp"
|
||||
#include "../mwclass/weapon.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
@ -11,33 +23,58 @@ namespace MWLua
|
|||
return std::to_string(id.mIndex) + "_" + std::to_string(id.mContentFile);
|
||||
}
|
||||
|
||||
std::string Object::toString() const
|
||||
const static std::map<std::type_index, std::string_view> classNames = {
|
||||
{typeid(MWClass::Activator), "Activator"},
|
||||
{typeid(MWClass::Armor), "Armor"},
|
||||
{typeid(MWClass::Book), "Book"},
|
||||
{typeid(MWClass::Clothing), "Clothing"},
|
||||
{typeid(MWClass::Container), "Container"},
|
||||
{typeid(MWClass::Creature), "Creature"},
|
||||
{typeid(MWClass::Door), "Door"},
|
||||
{typeid(MWClass::Ingredient), "Ingredient"},
|
||||
{typeid(MWClass::Light), "Light"},
|
||||
{typeid(MWClass::Miscellaneous), "Miscellaneous"},
|
||||
{typeid(MWClass::Npc), "NPC"},
|
||||
{typeid(MWClass::Potion), "Potion"},
|
||||
{typeid(MWClass::Static), "Static"},
|
||||
{typeid(MWClass::Weapon), "Weapon"},
|
||||
};
|
||||
|
||||
std::string_view getMWClassName(const std::type_index& cls_type, std::string_view fallback)
|
||||
{
|
||||
std::string res = idToString(mId);
|
||||
if (isValid())
|
||||
{
|
||||
res.append(" (");
|
||||
res.append(type());
|
||||
res.append(", ");
|
||||
res.append(*ptr().getCellRef().getRefIdPtr());
|
||||
res.append(")");
|
||||
}
|
||||
auto it = classNames.find(cls_type);
|
||||
if (it != classNames.end())
|
||||
return it->second;
|
||||
else
|
||||
res.append(" (not found)");
|
||||
return fallback;
|
||||
}
|
||||
|
||||
std::string_view getMWClassName(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
if (*ptr.getCellRef().getRefIdPtr() == "player")
|
||||
return "Player";
|
||||
else
|
||||
return getMWClassName(typeid(ptr.getClass()), ptr.getTypeName());
|
||||
}
|
||||
|
||||
std::string ptrToString(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
std::string res = "object";
|
||||
res.append(idToString(getId(ptr)));
|
||||
res.append(" (");
|
||||
res.append(getMWClassName(ptr));
|
||||
res.append(", ");
|
||||
res.append(*ptr.getCellRef().getRefIdPtr());
|
||||
res.append(")");
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string_view Object::type() const
|
||||
std::string Object::toString() 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";
|
||||
if (isValid())
|
||||
return ptrToString(ptr());
|
||||
else
|
||||
return typeName;
|
||||
return "object" + idToString(mId) + " (not found)";
|
||||
}
|
||||
|
||||
bool Object::isValid() const
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef MWLUA_OBJECT_H
|
||||
#define MWLUA_OBJECT_H
|
||||
|
||||
#include <typeindex>
|
||||
|
||||
#include <components/esm/cellref.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
@ -15,6 +17,9 @@ namespace MWLua
|
|||
using ObjectId = ESM::RefNum;
|
||||
inline const ObjectId& getId(const MWWorld::Ptr& ptr) { return ptr.getCellRef().getRefNum(); }
|
||||
std::string idToString(const ObjectId& id);
|
||||
std::string ptrToString(const MWWorld::Ptr& ptr);
|
||||
std::string_view getMWClassName(const std::type_index& cls_type, std::string_view fallback = "Unknown");
|
||||
std::string_view getMWClassName(const MWWorld::Ptr& ptr);
|
||||
|
||||
// Holds a mapping ObjectId -> MWWord::Ptr.
|
||||
class ObjectRegistry
|
||||
|
@ -60,7 +65,7 @@ namespace MWLua
|
|||
ObjectId id() const { return mId; }
|
||||
|
||||
std::string toString() const;
|
||||
std::string_view type() const;
|
||||
std::string_view type() const { return getMWClassName(ptr()); }
|
||||
|
||||
// Updates and returns the underlying Ptr. Throws an exception if object is not available.
|
||||
const MWWorld::Ptr& ptr() const;
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include <components/lua/luastate.hpp>
|
||||
|
||||
#include "../mwclass/door.hpp"
|
||||
|
||||
#include "eventqueue.hpp"
|
||||
#include "luamanagerimp.hpp"
|
||||
|
||||
|
@ -20,6 +22,20 @@ namespace sol
|
|||
namespace MWLua
|
||||
{
|
||||
|
||||
template <class Class>
|
||||
static const MWWorld::Ptr& requireClass(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
if (typeid(Class) != typeid(ptr.getClass()))
|
||||
{
|
||||
std::string msg = "Requires type '";
|
||||
msg.append(getMWClassName(typeid(Class)));
|
||||
msg.append("', but applied to ");
|
||||
msg.append(ptrToString(ptr));
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
template <class ObjectT>
|
||||
static void registerObjectList(const std::string& prefix, const Context& context)
|
||||
{
|
||||
|
@ -83,11 +99,35 @@ namespace MWLua
|
|||
}
|
||||
}
|
||||
|
||||
template <class ObjectT>
|
||||
static void addDoorBindings(sol::usertype<ObjectT>& objectT, const Context& context)
|
||||
{
|
||||
auto ptr = [](const ObjectT& o) -> const MWWorld::Ptr& { return requireClass<MWClass::Door>(o.ptr()); };
|
||||
|
||||
objectT["isTeleport"] = sol::readonly_property([ptr](const ObjectT& o)
|
||||
{
|
||||
return ptr(o).getCellRef().getTeleport();
|
||||
});
|
||||
objectT["destPosition"] = sol::readonly_property([ptr](const ObjectT& o) -> osg::Vec3f
|
||||
{
|
||||
return ptr(o).getCellRef().getDoorDest().asVec3();
|
||||
});
|
||||
objectT["destRotation"] = sol::readonly_property([ptr](const ObjectT& o) -> osg::Vec3f
|
||||
{
|
||||
return ptr(o).getCellRef().getDoorDest().asRotationVec3();
|
||||
});
|
||||
objectT["destCell"] = sol::readonly_property([ptr](const ObjectT& o) -> std::string_view
|
||||
{
|
||||
return ptr(o).getCellRef().getDestCell();
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
addDoorBindings<ObjectT>(objectT, context);
|
||||
|
||||
registerObjectList<ObjectT>(prefix, context);
|
||||
}
|
||||
|
|
18
apps/openmw/mwlua/uibindings.cpp
Normal file
18
apps/openmw/mwlua/uibindings.cpp
Normal file
|
@ -0,0 +1,18 @@
|
|||
#include "luabindings.hpp"
|
||||
|
||||
#include "luamanagerimp.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
sol::table initUserInterfacePackage(const Context& context)
|
||||
{
|
||||
sol::table api(context.mLua->sol(), sol::create);
|
||||
api["showMessage"] = [luaManager=context.mLuaManager](std::string_view message)
|
||||
{
|
||||
luaManager->addUIMessage(message);
|
||||
};
|
||||
return context.mLua->makeReadOnly(api);
|
||||
}
|
||||
|
||||
}
|
|
@ -3,6 +3,8 @@
|
|||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm/esmwriter.hpp>
|
||||
|
||||
#include "../mwclass/container.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/timestamp.hpp"
|
||||
|
||||
|
@ -12,31 +14,52 @@ namespace MWLua
|
|||
void WorldView::update()
|
||||
{
|
||||
mObjectRegistry.update();
|
||||
mActivatorsInScene.updateList();
|
||||
mActorsInScene.updateList();
|
||||
mContainersInScene.updateList();
|
||||
mDoorsInScene.updateList();
|
||||
mItemsInScene.updateList();
|
||||
}
|
||||
|
||||
void WorldView::clear()
|
||||
{
|
||||
mObjectRegistry.clear();
|
||||
mActivatorsInScene.clear();
|
||||
mActorsInScene.clear();
|
||||
mContainersInScene.clear();
|
||||
mDoorsInScene.clear();
|
||||
mItemsInScene.clear();
|
||||
}
|
||||
|
||||
WorldView::ObjectGroup* WorldView::chooseGroup(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
const MWWorld::Class& cls = ptr.getClass();
|
||||
if (cls.isActivator())
|
||||
return &mActivatorsInScene;
|
||||
if (cls.isActor())
|
||||
return &mActorsInScene;
|
||||
if (cls.isDoor())
|
||||
return &mDoorsInScene;
|
||||
if (typeid(cls) == typeid(MWClass::Container))
|
||||
return &mContainersInScene;
|
||||
if (cls.hasToolTip(ptr))
|
||||
return &mItemsInScene;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void WorldView::objectAddedToScene(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
if (ptr.getClass().isActor())
|
||||
addToGroup(mActorsInScene, ptr);
|
||||
else
|
||||
addToGroup(mItemsInScene, ptr);
|
||||
mObjectRegistry.registerPtr(ptr);
|
||||
ObjectGroup* group = chooseGroup(ptr);
|
||||
if (group)
|
||||
addToGroup(*group, ptr);
|
||||
}
|
||||
|
||||
void WorldView::objectRemovedFromScene(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
if (ptr.getClass().isActor())
|
||||
removeFromGroup(mActorsInScene, ptr);
|
||||
else
|
||||
removeFromGroup(mItemsInScene, ptr);
|
||||
ObjectGroup* group = chooseGroup(ptr);
|
||||
if (group)
|
||||
removeFromGroup(*group, ptr);
|
||||
}
|
||||
|
||||
double WorldView::getGameTimeInHours() const
|
||||
|
|
|
@ -27,7 +27,10 @@ namespace MWLua
|
|||
// Note that the number of seconds in a game hour is not fixed.
|
||||
double getGameTimeInHours() const;
|
||||
|
||||
ObjectIdList getActivatorsInScene() const { return mActivatorsInScene.mList; }
|
||||
ObjectIdList getActorsInScene() const { return mActorsInScene.mList; }
|
||||
ObjectIdList getContainersInScene() const { return mContainersInScene.mList; }
|
||||
ObjectIdList getDoorsInScene() const { return mDoorsInScene.mList; }
|
||||
ObjectIdList getItemsInScene() const { return mItemsInScene.mList; }
|
||||
|
||||
ObjectRegistry* getObjectRegistry() { return &mObjectRegistry; }
|
||||
|
@ -51,11 +54,15 @@ namespace MWLua
|
|||
std::set<ObjectId> mSet;
|
||||
};
|
||||
|
||||
ObjectGroup* chooseGroup(const MWWorld::Ptr& ptr);
|
||||
void addToGroup(ObjectGroup& group, const MWWorld::Ptr& ptr);
|
||||
void removeFromGroup(ObjectGroup& group, const MWWorld::Ptr& ptr);
|
||||
|
||||
ObjectRegistry mObjectRegistry;
|
||||
ObjectGroup mActivatorsInScene;
|
||||
ObjectGroup mActorsInScene;
|
||||
ObjectGroup mContainersInScene;
|
||||
ObjectGroup mDoorsInScene;
|
||||
ObjectGroup mItemsInScene;
|
||||
|
||||
double mGameSeconds = 0;
|
||||
|
|
Loading…
Reference in a new issue