More Lua bindings

dont-compose-content
Petr Mikheev 4 years ago
parent 87b5afb9bf
commit 32218f6dd5

@ -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

@ -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 res;
return fallback;
}
std::string_view Object::type() const
std::string_view getMWClassName(const MWWorld::Ptr& ptr)
{
if (*ptr().getCellRef().getRefIdPtr() == "player")
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;
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 Object::toString() const
{
if (isValid())
return ptrToString(ptr());
else
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);
}

@ -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…
Cancel
Save