1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-05-11 11:11:27 +00:00

[Lua] Move class-specific functions to openmw.types

This commit is contained in:
Petr Mikheev 2022-02-15 19:38:47 +01:00
parent aa6cba9b17
commit af93ebf433
15 changed files with 286 additions and 200 deletions

View file

@ -60,6 +60,7 @@ add_openmw_dir (mwlua
luamanagerimp actions object worldview userdataserializer eventqueue query luamanagerimp actions object worldview userdataserializer eventqueue query
luabindings localscripts playerscripts objectbindings cellbindings asyncbindings settingsbindings luabindings localscripts playerscripts objectbindings cellbindings asyncbindings settingsbindings
camerabindings uibindings inputbindings nearbybindings camerabindings uibindings inputbindings nearbybindings
types/types types/door types/actor
) )
add_openmw_dir (mwsound add_openmw_dir (mwsound

View file

@ -43,31 +43,12 @@ namespace MWLua
#undef CONTROL #undef CONTROL
sol::usertype<SelfObject> selfAPI = sol::usertype<SelfObject> selfAPI =
context.mLua->sol().new_usertype<SelfObject>("SelfObject", sol::base_classes, sol::bases<LObject>()); context.mLua->sol().new_usertype<SelfObject>("SelfObject", sol::base_classes, sol::bases<LObject, Object>());
selfAPI[sol::meta_function::to_string] = [](SelfObject& self) { return "openmw.self[" + self.toString() + "]"; }; 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["object"] = sol::readonly_property([](SelfObject& self) -> LObject { return LObject(self); });
selfAPI["controls"] = sol::readonly_property([](SelfObject& self) { return &self.mControls; }); selfAPI["controls"] = sol::readonly_property([](SelfObject& self) { return &self.mControls; });
selfAPI["isActive"] = [](SelfObject& self) { return &self.mIsActive; }; selfAPI["isActive"] = [](SelfObject& self) { return &self.mIsActive; };
selfAPI["enableAI"] = [](SelfObject& self, bool v) { self.mControls.mDisableAI = !v; }; selfAPI["enableAI"] = [](SelfObject& self, bool v) { self.mControls.mDisableAI = !v; };
selfAPI["setEquipment"] = [context](const SelfObject& obj, sol::table equipment)
{
if (!obj.ptr().getClass().hasInventoryStore(obj.ptr()))
{
if (!equipment.empty())
throw std::runtime_error(ptrToString(obj.ptr()) + " has no equipment slots");
return;
}
SetEquipmentAction::Equipment eqp;
for (auto& [key, value] : equipment)
{
int slot = key.as<int>();
if (value.is<LObject>())
eqp[slot] = value.as<LObject>().id();
else
eqp[slot] = value.as<std::string>();
}
context.mLuaManager->addAction(std::make_unique<SetEquipmentAction>(context.mLua, obj.id(), std::move(eqp)));
};
using AiPackage = MWMechanics::AiPackage; using AiPackage = MWMechanics::AiPackage;
sol::usertype<AiPackage> aiPackage = context.mLua->sol().new_usertype<AiPackage>("AiPackage"); sol::usertype<AiPackage> aiPackage = context.mLua->sol().new_usertype<AiPackage>("AiPackage");

View file

@ -12,6 +12,7 @@
#include "eventqueue.hpp" #include "eventqueue.hpp"
#include "worldview.hpp" #include "worldview.hpp"
#include "types/types.hpp"
namespace MWLua namespace MWLua
{ {
@ -45,11 +46,57 @@ namespace MWLua
// api["resume"] = []() {}; // api["resume"] = []() {};
} }
sol::table initTypesPackage(const Context& context)
{
auto* lua = context.mLua;
sol::table types(lua->sol(), sol::create);
auto addType = [&](std::string_view name, std::optional<std::string_view> base = std::nullopt) -> sol::table
{
sol::table t(lua->sol(), sol::create);
sol::table ro = LuaUtil::makeReadOnly(t);
sol::table meta = ro[sol::metatable_key];
meta[sol::meta_function::to_string] = [name]() { return name; };
if (base)
{
t[sol::metatable_key] = LuaUtil::getMutableFromReadOnly(types[*base]);
t["baseType"] = types[*base];
}
types[name] = ro;
return t;
};
addActorBindings(addType("Actor"), context);
addType("Item");
addType(ObjectTypeName::Creature, "Actor");
addType(ObjectTypeName::NPC, "Actor");
addType(ObjectTypeName::Player, ObjectTypeName::NPC);
addType(ObjectTypeName::Armor, "Item");
addType(ObjectTypeName::Book, "Item");
addType(ObjectTypeName::Clothing, "Item");
addType(ObjectTypeName::Ingredient, "Item");
addType(ObjectTypeName::Light, "Item");
addType(ObjectTypeName::MiscItem, "Item");
addType(ObjectTypeName::Potion, "Item");
addType(ObjectTypeName::Weapon, "Item");
addType(ObjectTypeName::Apparatus, "Item");
addType(ObjectTypeName::Lockpick, "Item");
addType(ObjectTypeName::Probe, "Item");
addType(ObjectTypeName::Repair, "Item");
addType(ObjectTypeName::Activator);
addDoorBindings(addType(ObjectTypeName::Door), context);
addType(ObjectTypeName::Static);
return LuaUtil::makeReadOnly(types);
}
sol::table initCorePackage(const Context& context) sol::table initCorePackage(const Context& context)
{ {
auto* lua = context.mLua; auto* lua = context.mLua;
sol::table api(lua->sol(), sol::create); sol::table api(lua->sol(), sol::create);
api["API_REVISION"] = 17; api["API_REVISION"] = 18;
api["quit"] = [lua]() api["quit"] = [lua]()
{ {
Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback(); Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
@ -60,35 +107,14 @@ namespace MWLua
context.mGlobalEventQueue->push_back({std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer)}); context.mGlobalEventQueue->push_back({std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer)});
}; };
addTimeBindings(api, context, false); addTimeBindings(api, context, false);
api["OBJECT_TYPE"] = definitionList(*lua, api["OBJECT_TYPE"] = definitionList(*lua, // TODO: remove, use require('openmw.types') instead
{ {
ObjectTypeName::Activator, ObjectTypeName::Armor, ObjectTypeName::Book, ObjectTypeName::Clothing, ObjectTypeName::Activator, ObjectTypeName::Armor, ObjectTypeName::Book, ObjectTypeName::Clothing,
ObjectTypeName::Creature, ObjectTypeName::Door, ObjectTypeName::Ingredient, ObjectTypeName::Light, ObjectTypeName::Creature, ObjectTypeName::Door, ObjectTypeName::Ingredient, ObjectTypeName::Light,
ObjectTypeName::MiscItem, ObjectTypeName::NPC, ObjectTypeName::Player, ObjectTypeName::Potion, ObjectTypeName::MiscItem, ObjectTypeName::NPC, ObjectTypeName::Player, ObjectTypeName::Potion,
ObjectTypeName::Static, ObjectTypeName::Weapon, ObjectTypeName::Activator, ObjectTypeName::Lockpick, ObjectTypeName::Static, ObjectTypeName::Weapon, ObjectTypeName::Apparatus, ObjectTypeName::Lockpick,
ObjectTypeName::Probe, ObjectTypeName::Repair ObjectTypeName::Probe, ObjectTypeName::Repair
}); });
api["EQUIPMENT_SLOT"] = LuaUtil::makeReadOnly(context.mLua->tableFromPairs<std::string_view, int>({
{"Helmet", MWWorld::InventoryStore::Slot_Helmet},
{"Cuirass", MWWorld::InventoryStore::Slot_Cuirass},
{"Greaves", MWWorld::InventoryStore::Slot_Greaves},
{"LeftPauldron", MWWorld::InventoryStore::Slot_LeftPauldron},
{"RightPauldron", MWWorld::InventoryStore::Slot_RightPauldron},
{"LeftGauntlet", MWWorld::InventoryStore::Slot_LeftGauntlet},
{"RightGauntlet", MWWorld::InventoryStore::Slot_RightGauntlet},
{"Boots", MWWorld::InventoryStore::Slot_Boots},
{"Shirt", MWWorld::InventoryStore::Slot_Shirt},
{"Pants", MWWorld::InventoryStore::Slot_Pants},
{"Skirt", MWWorld::InventoryStore::Slot_Skirt},
{"Robe", MWWorld::InventoryStore::Slot_Robe},
{"LeftRing", MWWorld::InventoryStore::Slot_LeftRing},
{"RightRing", MWWorld::InventoryStore::Slot_RightRing},
{"Amulet", MWWorld::InventoryStore::Slot_Amulet},
{"Belt", MWWorld::InventoryStore::Slot_Belt},
{"CarriedRight", MWWorld::InventoryStore::Slot_CarriedRight},
{"CarriedLeft", MWWorld::InventoryStore::Slot_CarriedLeft},
{"Ammunition", MWWorld::InventoryStore::Slot_Ammunition}
}));
api["i18n"] = [i18n=context.mI18n](const std::string& context) { return i18n->getContext(context); }; api["i18n"] = [i18n=context.mI18n](const std::string& context) { return i18n->getContext(context); };
const MWWorld::Store<ESM::GameSetting>* gmst = &MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting>* gmst = &MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
api["getGMST"] = [lua=context.mLua, gmst](const std::string& setting) -> sol::object api["getGMST"] = [lua=context.mLua, gmst](const std::string& setting) -> sol::object

View file

@ -23,6 +23,7 @@ namespace MWLua
sol::table initCorePackage(const Context&); sol::table initCorePackage(const Context&);
sol::table initWorldPackage(const Context&); sol::table initWorldPackage(const Context&);
sol::table initQueryPackage(const Context&); sol::table initQueryPackage(const Context&);
sol::table initTypesPackage(const Context&);
sol::table initFieldGroup(const Context&, const QueryFieldGroup&); sol::table initFieldGroup(const Context&, const QueryFieldGroup&);
@ -38,14 +39,6 @@ namespace MWLua
void initObjectBindingsForGlobalScripts(const Context&); void initObjectBindingsForGlobalScripts(const Context&);
// Implemented in cellbindings.cpp // Implemented in cellbindings.cpp
struct LCell // for local scripts
{
MWWorld::CellStore* mStore;
};
struct GCell // for global scripts
{
MWWorld::CellStore* mStore;
};
void initCellBindingsForLocalScripts(const Context&); void initCellBindingsForLocalScripts(const Context&);
void initCellBindingsForGlobalScripts(const Context&); void initCellBindingsForGlobalScripts(const Context&);

View file

@ -82,6 +82,7 @@ namespace MWLua
mLua.addCommonPackage("openmw.util", LuaUtil::initUtilPackage(mLua.sol())); mLua.addCommonPackage("openmw.util", LuaUtil::initUtilPackage(mLua.sol()));
mLua.addCommonPackage("openmw.core", initCorePackage(context)); mLua.addCommonPackage("openmw.core", initCorePackage(context));
mLua.addCommonPackage("openmw.query", initQueryPackage(context)); mLua.addCommonPackage("openmw.query", initQueryPackage(context));
mLua.addCommonPackage("openmw.types", initTypesPackage(context));
mGlobalScripts.addPackage("openmw.world", initWorldPackage(context)); mGlobalScripts.addPackage("openmw.world", initWorldPackage(context));
mGlobalScripts.addPackage("openmw.settings", initGlobalSettingsPackage(context)); mGlobalScripts.addPackage("openmw.settings", initGlobalSettingsPackage(context));
mGlobalScripts.addPackage("openmw.storage", initGlobalStoragePackage(context, &mGlobalStorage)); mGlobalScripts.addPackage("openmw.storage", initGlobalStoragePackage(context, &mGlobalStorage));

View file

@ -60,7 +60,7 @@ namespace MWLua
// for types that are not present in `luaObjectTypeInfo` (for such types result stability // for types that are not present in `luaObjectTypeInfo` (for such types result stability
// is not necessary because they are not listed in OpenMW Lua documentation). // is not necessary because they are not listed in OpenMW Lua documentation).
if (ptr.getCellRef().getRefId() == "player") if (ptr.getCellRef().getRefId() == "player")
return "Player"; return ObjectTypeName::Player;
if (isMarker(ptr)) if (isMarker(ptr))
return "Marker"; return "Marker";
return getLuaObjectTypeName(static_cast<ESM::RecNameInts>(ptr.getType()), /*fallback=*/ptr.getTypeDescription()); return getLuaObjectTypeName(static_cast<ESM::RecNameInts>(ptr.getType()), /*fallback=*/ptr.getTypeDescription());
@ -79,6 +79,19 @@ namespace MWLua
return 0; return 0;
} }
const MWWorld::Ptr& verifyType(ESM::RecNameInts recordType, const MWWorld::Ptr& ptr)
{
if (ptr.getType() != recordType)
{
std::string msg = "Requires type '";
msg.append(getLuaObjectTypeName(recordType));
msg.append("', but applied to ");
msg.append(ptrToString(ptr));
throw std::runtime_error(msg);
}
return ptr;
}
std::string ptrToString(const MWWorld::Ptr& ptr) std::string ptrToString(const MWWorld::Ptr& ptr)
{ {
std::string res = "object"; std::string res = "object";

View file

@ -3,6 +3,8 @@
#include <typeindex> #include <typeindex>
#include <sol/sol.hpp>
#include <components/esm3/cellref.hpp> #include <components/esm3/cellref.hpp>
#include <components/esm/defs.hpp> #include <components/esm/defs.hpp>
#include <components/esm/luascripts.hpp> #include <components/esm/luascripts.hpp>
@ -48,6 +50,7 @@ namespace MWLua
bool isMarker(const MWWorld::Ptr& ptr); bool isMarker(const MWWorld::Ptr& ptr);
std::string_view getLuaObjectTypeName(ESM::RecNameInts recordType, std::string_view fallback = "Unknown"); std::string_view getLuaObjectTypeName(ESM::RecNameInts recordType, std::string_view fallback = "Unknown");
std::string_view getLuaObjectTypeName(const MWWorld::Ptr& ptr); std::string_view getLuaObjectTypeName(const MWWorld::Ptr& ptr);
const MWWorld::Ptr& verifyType(ESM::RecNameInts recordType, const MWWorld::Ptr& ptr);
// Each script has a set of flags that controls to which objects the script should be // Each script has a set of flags that controls to which objects the script should be
// automatically attached. This function maps each object types to one of the flags. // automatically attached. This function maps each object types to one of the flags.
@ -103,6 +106,9 @@ namespace MWLua
// Returns `true` if calling `ptr()` is safe. // Returns `true` if calling `ptr()` is safe.
bool isValid() const; bool isValid() const;
virtual sol::object getObject(lua_State* lua, ObjectId id) const = 0; // returns LObject or GOBject
virtual sol::object getCell(lua_State* lua, MWWorld::CellStore* store) const = 0; // returns LCell or GCell
protected: protected:
virtual void updatePtr() const = 0; virtual void updatePtr() const = 0;
@ -114,17 +120,29 @@ namespace MWLua
}; };
// Used only in local scripts // Used only in local scripts
struct LCell
{
MWWorld::CellStore* mStore;
};
class LObject : public Object class LObject : public Object
{ {
using Object::Object; using Object::Object;
void updatePtr() const final { mPtr = mObjectRegistry->getPtr(mId, true); } void updatePtr() const final { mPtr = mObjectRegistry->getPtr(mId, true); }
sol::object getObject(lua_State* lua, ObjectId id) const final { return sol::make_object<LObject>(lua, id, mObjectRegistry); }
sol::object getCell(lua_State* lua, MWWorld::CellStore* store) const final { return sol::make_object(lua, LCell{store}); }
}; };
// Used only in global scripts // Used only in global scripts
struct GCell
{
MWWorld::CellStore* mStore;
};
class GObject : public Object class GObject : public Object
{ {
using Object::Object; using Object::Object;
void updatePtr() const final { mPtr = mObjectRegistry->getPtr(mId, false); } void updatePtr() const final { mPtr = mObjectRegistry->getPtr(mId, false); }
sol::object getObject(lua_State* lua, ObjectId id) const final { return sol::make_object<GObject>(lua, id, mObjectRegistry); }
sol::object getCell(lua_State* lua, MWWorld::CellStore* store) const final { return sol::make_object(lua, GCell{store}); }
}; };
using ObjectIdList = std::shared_ptr<std::vector<ObjectId>>; using ObjectIdList = std::shared_ptr<std::vector<ObjectId>>;

View file

@ -42,19 +42,6 @@ namespace MWLua
template <typename ObjT> template <typename ObjT>
using Cell = std::conditional_t<std::is_same_v<ObjT, LObject>, LCell, GCell>; using Cell = std::conditional_t<std::is_same_v<ObjT, LObject>, LCell, GCell>;
static const MWWorld::Ptr& requireRecord(ESM::RecNameInts recordType, const MWWorld::Ptr& ptr)
{
if (ptr.getType() != recordType)
{
std::string msg = "Requires type '";
msg.append(getLuaObjectTypeName(recordType));
msg.append("', but applied to ");
msg.append(ptrToString(ptr));
throw std::runtime_error(msg);
}
return ptr;
}
template <class ObjectT> template <class ObjectT>
static void registerObjectList(const std::string& prefix, const Context& context) static void registerObjectList(const std::string& prefix, const Context& context)
{ {
@ -122,21 +109,6 @@ namespace MWLua
context.mLocalEventQueue->push_back({dest.id(), std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer)}); context.mLocalEventQueue->push_back({dest.id(), std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer)});
}; };
objectT["canMove"] = [](const ObjectT& o)
{
const MWWorld::Class& cls = o.ptr().getClass();
return cls.getMaxSpeed(o.ptr()) > 0;
};
objectT["getRunSpeed"] = [](const ObjectT& o)
{
const MWWorld::Class& cls = o.ptr().getClass();
return cls.getRunSpeed(o.ptr());
};
objectT["getWalkSpeed"] = [](const ObjectT& o)
{
const MWWorld::Class& cls = o.ptr().getClass();
return cls.getWalkSpeed(o.ptr());
};
objectT["activateBy"] = [context](const ObjectT& o, const ObjectT& actor) objectT["activateBy"] = [context](const ObjectT& o, const ObjectT& actor)
{ {
uint32_t esmRecordType = actor.ptr().getType(); uint32_t esmRecordType = actor.ptr().getType();
@ -199,112 +171,14 @@ namespace MWLua
context.mLuaManager->addAction(std::move(action)); context.mLuaManager->addAction(std::move(action));
}; };
} }
else
{ // Only for local scripts
objectT["isOnGround"] = [](const ObjectT& o)
{
return MWBase::Environment::get().getWorld()->isOnGround(o.ptr());
};
objectT["isSwimming"] = [](const ObjectT& o)
{
return MWBase::Environment::get().getWorld()->isSwimming(o.ptr());
};
objectT["isInWeaponStance"] = [](const ObjectT& o)
{
const MWWorld::Class& cls = o.ptr().getClass();
return cls.isActor() && cls.getCreatureStats(o.ptr()).getDrawState() == MWMechanics::DrawState_Weapon;
};
objectT["isInMagicStance"] = [](const ObjectT& o)
{
const MWWorld::Class& cls = o.ptr().getClass();
return cls.isActor() && cls.getCreatureStats(o.ptr()).getDrawState() == MWMechanics::DrawState_Spell;
};
objectT["getCurrentSpeed"] = [](const ObjectT& o)
{
const MWWorld::Class& cls = o.ptr().getClass();
return cls.getCurrentSpeed(o.ptr());
};
}
} }
template <class ObjectT>
static void addDoorBindings(sol::usertype<ObjectT>& objectT, const Context& context)
{
auto ptr = [](const ObjectT& o) -> const MWWorld::Ptr& { return requireRecord(ESM::REC_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, worldView=context.mWorldView](const ObjectT& o) -> sol::optional<Cell<ObjectT>>
{
const MWWorld::CellRef& cellRef = ptr(o).getCellRef();
if (!cellRef.getTeleport())
return sol::nullopt;
MWWorld::CellStore* cell = worldView->findCell(cellRef.getDestCell(), cellRef.getDoorDest().asVec3());
if (cell)
return Cell<ObjectT>{cell};
else
return sol::nullopt;
});
}
static SetEquipmentAction::Equipment parseEquipmentTable(sol::table equipment)
{
SetEquipmentAction::Equipment eqp;
for (auto& [key, value] : equipment)
{
int slot = key.as<int>();
if (value.is<GObject>())
eqp[slot] = value.as<GObject>().id();
else
eqp[slot] = value.as<std::string>();
}
return eqp;
}
template <class ObjectT> template <class ObjectT>
static void addInventoryBindings(sol::usertype<ObjectT>& objectT, const std::string& prefix, const Context& context) static void addInventoryBindings(sol::usertype<ObjectT>& objectT, const std::string& prefix, const Context& context)
{ {
using InventoryT = Inventory<ObjectT>; using InventoryT = Inventory<ObjectT>;
sol::usertype<InventoryT> inventoryT = context.mLua->sol().new_usertype<InventoryT>(prefix + "Inventory"); sol::usertype<InventoryT> inventoryT = context.mLua->sol().new_usertype<InventoryT>(prefix + "Inventory");
objectT["getEquipment"] = [context](const ObjectT& o)
{
const MWWorld::Ptr& ptr = o.ptr();
sol::table equipment(context.mLua->sol(), sol::create);
if (!ptr.getClass().hasInventoryStore(ptr))
return equipment;
MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr);
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
{
auto it = store.getSlot(slot);
if (it == store.end())
continue;
context.mWorldView->getObjectRegistry()->registerPtr(*it);
equipment[slot] = ObjectT(getId(*it), context.mWorldView->getObjectRegistry());
}
return equipment;
};
objectT["isEquipped"] = [](const ObjectT& actor, const ObjectT& item)
{
const MWWorld::Ptr& ptr = actor.ptr();
if (!ptr.getClass().hasInventoryStore(ptr))
return false;
MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr);
return store.isEquipped(item.ptr());
};
objectT["inventory"] = sol::readonly_property([](const ObjectT& o) { return InventoryT{o}; }); objectT["inventory"] = sol::readonly_property([](const ObjectT& o) { return InventoryT{o}; });
inventoryT[sol::meta_function::to_string] = inventoryT[sol::meta_function::to_string] =
[](const InventoryT& inv) { return "Inventory[" + inv.mObj.toString() + "]"; }; [](const InventoryT& inv) { return "Inventory[" + inv.mObj.toString() + "]"; };
@ -363,18 +237,6 @@ namespace MWLua
if constexpr (std::is_same_v<ObjectT, GObject>) if constexpr (std::is_same_v<ObjectT, GObject>)
{ // Only for global scripts { // Only for global scripts
objectT["setEquipment"] = [context](const GObject& obj, sol::table equipment)
{
if (!obj.ptr().getClass().hasInventoryStore(obj.ptr()))
{
if (!equipment.empty())
throw std::runtime_error(ptrToString(obj.ptr()) + " has no equipment slots");
return;
}
context.mLuaManager->addAction(std::make_unique<SetEquipmentAction>(
context.mLua, obj.id(), parseEquipmentTable(equipment)));
};
// TODO // TODO
// obj.inventory:drop(obj2, [count]) // obj.inventory:drop(obj2, [count])
// obj.inventory:drop(recordId, [count]) // obj.inventory:drop(recordId, [count])
@ -390,9 +252,9 @@ namespace MWLua
template <class ObjectT> template <class ObjectT>
static void initObjectBindings(const std::string& prefix, const Context& context) static void initObjectBindings(const std::string& prefix, const Context& context)
{ {
sol::usertype<ObjectT> objectT = context.mLua->sol().new_usertype<ObjectT>(prefix + "Object"); sol::usertype<ObjectT> objectT = context.mLua->sol().new_usertype<ObjectT>(
prefix + "Object", sol::base_classes, sol::bases<Object>());
addBasicBindings<ObjectT>(objectT, context); addBasicBindings<ObjectT>(objectT, context);
addDoorBindings<ObjectT>(objectT, context);
addInventoryBindings<ObjectT>(objectT, prefix, context); addInventoryBindings<ObjectT>(objectT, prefix, context);
registerObjectList<ObjectT>(prefix, context); registerObjectList<ObjectT>(prefix, context);

View file

@ -0,0 +1,137 @@
#include "types.hpp"
#include <components/lua/luastate.hpp>
#include <apps/openmw/mwmechanics/drawstate.hpp>
#include <apps/openmw/mwworld/inventorystore.hpp>
#include <apps/openmw/mwworld/class.hpp>
#include "../actions.hpp"
#include "../luabindings.hpp"
#include "../localscripts.hpp"
#include "../luamanagerimp.hpp"
namespace MWLua
{
using SelfObject = LocalScripts::SelfObject;
void addActorBindings(sol::table actor, const Context& context)
{
actor["STANCE"] = LuaUtil::makeReadOnly(context.mLua->tableFromPairs<std::string_view, int>({
{"Nothing", MWMechanics::DrawState_Nothing},
{"Weapon", MWMechanics::DrawState_Weapon},
{"Spell", MWMechanics::DrawState_Spell},
}));
actor["EQUIPMENT_SLOT"] = LuaUtil::makeReadOnly(context.mLua->tableFromPairs<std::string_view, int>({
{"Helmet", MWWorld::InventoryStore::Slot_Helmet},
{"Cuirass", MWWorld::InventoryStore::Slot_Cuirass},
{"Greaves", MWWorld::InventoryStore::Slot_Greaves},
{"LeftPauldron", MWWorld::InventoryStore::Slot_LeftPauldron},
{"RightPauldron", MWWorld::InventoryStore::Slot_RightPauldron},
{"LeftGauntlet", MWWorld::InventoryStore::Slot_LeftGauntlet},
{"RightGauntlet", MWWorld::InventoryStore::Slot_RightGauntlet},
{"Boots", MWWorld::InventoryStore::Slot_Boots},
{"Shirt", MWWorld::InventoryStore::Slot_Shirt},
{"Pants", MWWorld::InventoryStore::Slot_Pants},
{"Skirt", MWWorld::InventoryStore::Slot_Skirt},
{"Robe", MWWorld::InventoryStore::Slot_Robe},
{"LeftRing", MWWorld::InventoryStore::Slot_LeftRing},
{"RightRing", MWWorld::InventoryStore::Slot_RightRing},
{"Amulet", MWWorld::InventoryStore::Slot_Amulet},
{"Belt", MWWorld::InventoryStore::Slot_Belt},
{"CarriedRight", MWWorld::InventoryStore::Slot_CarriedRight},
{"CarriedLeft", MWWorld::InventoryStore::Slot_CarriedLeft},
{"Ammunition", MWWorld::InventoryStore::Slot_Ammunition}
}));
actor["stance"] = [](const Object& o)
{
const MWWorld::Class& cls = o.ptr().getClass();
if (cls.isActor())
return cls.getCreatureStats(o.ptr()).getDrawState();
else
throw std::runtime_error("Actor expected");
};
actor["canMove"] = [](const Object& o)
{
const MWWorld::Class& cls = o.ptr().getClass();
return cls.getMaxSpeed(o.ptr()) > 0;
};
actor["runSpeed"] = [](const Object& o)
{
const MWWorld::Class& cls = o.ptr().getClass();
return cls.getRunSpeed(o.ptr());
};
actor["walkSpeed"] = [](const Object& o)
{
const MWWorld::Class& cls = o.ptr().getClass();
return cls.getWalkSpeed(o.ptr());
};
actor["currentSpeed"] = [](const Object& o)
{
const MWWorld::Class& cls = o.ptr().getClass();
return cls.getCurrentSpeed(o.ptr());
};
actor["isOnGround"] = [](const LObject& o)
{
return MWBase::Environment::get().getWorld()->isOnGround(o.ptr());
};
actor["isSwimming"] = [](const LObject& o)
{
return MWBase::Environment::get().getWorld()->isSwimming(o.ptr());
};
actor["getEquipment"] = [context](const Object& o)
{
const MWWorld::Ptr& ptr = o.ptr();
sol::table equipment(context.mLua->sol(), sol::create);
if (!ptr.getClass().hasInventoryStore(ptr))
return equipment;
MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr);
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
{
auto it = store.getSlot(slot);
if (it == store.end())
continue;
context.mWorldView->getObjectRegistry()->registerPtr(*it);
equipment[slot] = o.getObject(context.mLua->sol(), getId(*it));
}
return equipment;
};
actor["hasEquipped"] = [](const Object& o, const Object& item)
{
const MWWorld::Ptr& ptr = o.ptr();
if (!ptr.getClass().hasInventoryStore(ptr))
return false;
MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr);
return store.isEquipped(item.ptr());
};
actor["setEquipment"] = [context](const sol::object& luaObj, const sol::table& equipment)
{
if (!luaObj.is<GObject>() && !luaObj.is<SelfObject>())
throw std::runtime_error("Incorrect type of the first argument. "
"Can be either self (in local scripts) or game object (in global scripts)");
const Object& obj = luaObj.as<Object>();
if (!obj.ptr().getClass().hasInventoryStore(obj.ptr()))
{
if (!equipment.empty())
throw std::runtime_error(ptrToString(obj.ptr()) + " has no equipment slots");
return;
}
SetEquipmentAction::Equipment eqp;
for (auto& [key, value] : equipment)
{
int slot = key.as<int>();
if (value.is<Object>())
eqp[slot] = value.as<Object>().id();
else
eqp[slot] = value.as<std::string>();
}
context.mLuaManager->addAction(std::make_unique<SetEquipmentAction>(context.mLua, obj.id(), std::move(eqp)));
};
}
}

View file

@ -0,0 +1,34 @@
#include "types.hpp"
#include "../luabindings.hpp"
namespace MWLua
{
static const MWWorld::Ptr& doorPtr(const Object& o) { return verifyType(ESM::REC_DOOR, o.ptr()); }
void addDoorBindings(sol::table door, const Context& context)
{
door["isTeleport"] = [](const Object& o) { return doorPtr(o).getCellRef().getTeleport(); };
door["destPosition"] = [](const Object& o) -> osg::Vec3f
{
return doorPtr(o).getCellRef().getDoorDest().asVec3();
};
door["destRotation"] = [](const Object& o) -> osg::Vec3f
{
return doorPtr(o).getCellRef().getDoorDest().asRotationVec3();
};
door["destCell"] = [worldView=context.mWorldView](sol::this_state lua, const Object& o) -> sol::object
{
const MWWorld::CellRef& cellRef = doorPtr(o).getCellRef();
if (!cellRef.getTeleport())
return sol::nil;
MWWorld::CellStore* cell = worldView->findCell(cellRef.getDestCell(), cellRef.getDoorDest().asVec3());
if (cell)
return o.getCell(lua, cell);
else
return sol::nil;
};
}
}

View file

@ -0,0 +1,14 @@
#ifndef MWLUA_TYPES_H
#define MWLUA_TYPES_H
#include <sol/sol.hpp>
#include "../context.hpp"
namespace MWLua
{
void addDoorBindings(sol::table door, const Context& context);
void addActorBindings(sol::table actor, const Context& context);
}
#endif // MWLUA_TYPES_H

View file

@ -91,7 +91,7 @@ local function formatGameTime(formatStr, timestamp)
error('Unknown tag "'..tag..'"') error('Unknown tag "'..tag..'"')
end end
res, _ = string.gsub(formatStr or '%c', '%%.', replFn) local res, _ = string.gsub(formatStr or '%c', '%%.', replFn)
return res return res
end end

View file

@ -5,6 +5,8 @@ local settings = require('openmw.settings')
local util = require('openmw.util') local util = require('openmw.util')
local self = require('openmw.self') local self = require('openmw.self')
local Actor = require('openmw.types').Actor
local head_bobbing = require('scripts.omw.head_bobbing') local head_bobbing = require('scripts.omw.head_bobbing')
local third_person = require('scripts.omw.third_person') local third_person = require('scripts.omw.third_person')
@ -77,7 +79,7 @@ local function updateVanity(dt)
end end
local function updateSmoothedSpeed(dt) local function updateSmoothedSpeed(dt)
local speed = self:getCurrentSpeed() local speed = Actor.currentSpeed(self)
speed = speed / (1 + speed / 500) speed = speed / (1 + speed / 500)
local maxDelta = 300 * dt local maxDelta = 300 * dt
smoothedSpeed = smoothedSpeed + util.clamp(speed - smoothedSpeed, -maxDelta, maxDelta) smoothedSpeed = smoothedSpeed + util.clamp(speed - smoothedSpeed, -maxDelta, maxDelta)
@ -126,7 +128,7 @@ local function updateStandingPreview()
third_person.standingPreview = false third_person.standingPreview = false
return return
end end
local standingStill = self:getCurrentSpeed() == 0 and not self:isInWeaponStance() and not self:isInMagicStance() local standingStill = Actor.currentSpeed(self) == 0 and Actor.stance(self) == Actor.STANCE.Nothing
if standingStill and mode == MODE.ThirdPerson then if standingStill and mode == MODE.ThirdPerson then
third_person.standingPreview = true third_person.standingPreview = true
camera.setMode(MODE.Preview) camera.setMode(MODE.Preview)

View file

@ -3,6 +3,8 @@ local self = require('openmw.self')
local settings = require('openmw.settings') local settings = require('openmw.settings')
local util = require('openmw.util') local util = require('openmw.util')
local Actor = require('openmw.types').Actor
local doubleStepLength = settings._getFloatFromSettingsCfg('Camera', 'head bobbing step') * 2 local doubleStepLength = settings._getFloatFromSettingsCfg('Camera', 'head bobbing step') * 2
local stepHeight = settings._getFloatFromSettingsCfg('Camera', 'head bobbing height') local stepHeight = settings._getFloatFromSettingsCfg('Camera', 'head bobbing height')
local maxRoll = math.rad(settings._getFloatFromSettingsCfg('Camera', 'head bobbing roll')) local maxRoll = math.rad(settings._getFloatFromSettingsCfg('Camera', 'head bobbing roll'))
@ -20,14 +22,14 @@ local sampleArc = function(x) return 1 - math.cos(x * halfArc) end
local arcHeight = sampleArc(1) local arcHeight = sampleArc(1)
function M.update(dt, smoothedSpeed) function M.update(dt, smoothedSpeed)
local speed = self:getCurrentSpeed() local speed = Actor.currentSpeed(self)
speed = speed / (1 + speed / 500) -- limit bobbing frequency if the speed is very high speed = speed / (1 + speed / 500) -- limit bobbing frequency if the speed is very high
totalMovement = totalMovement + speed * dt totalMovement = totalMovement + speed * dt
if not M.enabled or camera.getMode() ~= camera.MODE.FirstPerson then if not M.enabled or camera.getMode() ~= camera.MODE.FirstPerson then
effectWeight = 0 effectWeight = 0
return return
end end
if self:isOnGround() then if Actor.isOnGround(self) then
effectWeight = math.min(1, effectWeight + dt * 5) effectWeight = math.min(1, effectWeight + dt * 5)
else else
effectWeight = math.max(0, effectWeight - dt * 5) effectWeight = math.max(0, effectWeight - dt * 5)

View file

@ -4,6 +4,8 @@ local util = require('openmw.util')
local self = require('openmw.self') local self = require('openmw.self')
local nearby = require('openmw.nearby') local nearby = require('openmw.nearby')
local Actor = require('openmw.types').Actor
local MODE = camera.MODE local MODE = camera.MODE
local STATE = { RightShoulder = 0, LeftShoulder = 1, Combat = 2, Swimming = 3 } local STATE = { RightShoulder = 0, LeftShoulder = 1, Combat = 2, Swimming = 3 }
@ -70,9 +72,9 @@ local noThirdPersonLastFrame = true
local function updateState() local function updateState()
local mode = camera.getMode() local mode = camera.getMode()
local oldState = state local oldState = state
if (self:isInWeaponStance() or self:isInMagicStance()) and mode == MODE.ThirdPerson then if Actor.stance(self) ~= Actor.STANCE.Nothing and mode == MODE.ThirdPerson then
state = STATE.Combat state = STATE.Combat
elseif self:isSwimming() then elseif Actor.isSwimming(self) then
state = STATE.Swimming state = STATE.Swimming
elseif oldState == STATE.Combat or oldState == STATE.Swimming then elseif oldState == STATE.Combat or oldState == STATE.Swimming then
state = defaultShoulder state = defaultShoulder