1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-02-19 19:10:12 +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
luabindings localscripts playerscripts objectbindings cellbindings asyncbindings settingsbindings
camerabindings uibindings inputbindings nearbybindings
types/types types/door types/actor
)
add_openmw_dir (mwsound

View file

@ -43,31 +43,12 @@ namespace MWLua
#undef CONTROL
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["object"] = sol::readonly_property([](SelfObject& self) -> LObject { return LObject(self); });
selfAPI["controls"] = sol::readonly_property([](SelfObject& self) { return &self.mControls; });
selfAPI["isActive"] = [](SelfObject& self) { return &self.mIsActive; };
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;
sol::usertype<AiPackage> aiPackage = context.mLua->sol().new_usertype<AiPackage>("AiPackage");

View file

@ -12,6 +12,7 @@
#include "eventqueue.hpp"
#include "worldview.hpp"
#include "types/types.hpp"
namespace MWLua
{
@ -45,11 +46,57 @@ namespace MWLua
// 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)
{
auto* lua = context.mLua;
sol::table api(lua->sol(), sol::create);
api["API_REVISION"] = 17;
api["API_REVISION"] = 18;
api["quit"] = [lua]()
{
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)});
};
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::Creature, ObjectTypeName::Door, ObjectTypeName::Ingredient, ObjectTypeName::Light,
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
});
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); };
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

View file

@ -23,6 +23,7 @@ namespace MWLua
sol::table initCorePackage(const Context&);
sol::table initWorldPackage(const Context&);
sol::table initQueryPackage(const Context&);
sol::table initTypesPackage(const Context&);
sol::table initFieldGroup(const Context&, const QueryFieldGroup&);
@ -38,14 +39,6 @@ namespace MWLua
void initObjectBindingsForGlobalScripts(const Context&);
// 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 initCellBindingsForGlobalScripts(const Context&);

View file

@ -82,6 +82,7 @@ namespace MWLua
mLua.addCommonPackage("openmw.util", LuaUtil::initUtilPackage(mLua.sol()));
mLua.addCommonPackage("openmw.core", initCorePackage(context));
mLua.addCommonPackage("openmw.query", initQueryPackage(context));
mLua.addCommonPackage("openmw.types", initTypesPackage(context));
mGlobalScripts.addPackage("openmw.world", initWorldPackage(context));
mGlobalScripts.addPackage("openmw.settings", initGlobalSettingsPackage(context));
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
// is not necessary because they are not listed in OpenMW Lua documentation).
if (ptr.getCellRef().getRefId() == "player")
return "Player";
return ObjectTypeName::Player;
if (isMarker(ptr))
return "Marker";
return getLuaObjectTypeName(static_cast<ESM::RecNameInts>(ptr.getType()), /*fallback=*/ptr.getTypeDescription());
@ -79,6 +79,19 @@ namespace MWLua
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 res = "object";

View file

@ -3,6 +3,8 @@
#include <typeindex>
#include <sol/sol.hpp>
#include <components/esm3/cellref.hpp>
#include <components/esm/defs.hpp>
#include <components/esm/luascripts.hpp>
@ -48,6 +50,7 @@ namespace MWLua
bool isMarker(const MWWorld::Ptr& ptr);
std::string_view getLuaObjectTypeName(ESM::RecNameInts recordType, std::string_view fallback = "Unknown");
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
// 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.
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:
virtual void updatePtr() const = 0;
@ -114,17 +120,29 @@ namespace MWLua
};
// Used only in local scripts
struct LCell
{
MWWorld::CellStore* mStore;
};
class LObject : public Object
{
using Object::Object;
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
struct GCell
{
MWWorld::CellStore* mStore;
};
class GObject : public Object
{
using Object::Object;
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>>;

View file

@ -42,19 +42,6 @@ namespace MWLua
template <typename ObjT>
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>
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)});
};
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)
{
uint32_t esmRecordType = actor.ptr().getType();
@ -199,112 +171,14 @@ namespace MWLua
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>
static void addInventoryBindings(sol::usertype<ObjectT>& objectT, const std::string& prefix, const Context& context)
{
using InventoryT = Inventory<ObjectT>;
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}; });
inventoryT[sol::meta_function::to_string] =
[](const InventoryT& inv) { return "Inventory[" + inv.mObj.toString() + "]"; };
@ -363,18 +237,6 @@ namespace MWLua
if constexpr (std::is_same_v<ObjectT, GObject>)
{ // 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
// obj.inventory:drop(obj2, [count])
// obj.inventory:drop(recordId, [count])
@ -390,9 +252,9 @@ namespace MWLua
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");
sol::usertype<ObjectT> objectT = context.mLua->sol().new_usertype<ObjectT>(
prefix + "Object", sol::base_classes, sol::bases<Object>());
addBasicBindings<ObjectT>(objectT, context);
addDoorBindings<ObjectT>(objectT, context);
addInventoryBindings<ObjectT>(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..'"')
end
res, _ = string.gsub(formatStr or '%c', '%%.', replFn)
local res, _ = string.gsub(formatStr or '%c', '%%.', replFn)
return res
end

View file

@ -5,6 +5,8 @@ local settings = require('openmw.settings')
local util = require('openmw.util')
local self = require('openmw.self')
local Actor = require('openmw.types').Actor
local head_bobbing = require('scripts.omw.head_bobbing')
local third_person = require('scripts.omw.third_person')
@ -77,7 +79,7 @@ local function updateVanity(dt)
end
local function updateSmoothedSpeed(dt)
local speed = self:getCurrentSpeed()
local speed = Actor.currentSpeed(self)
speed = speed / (1 + speed / 500)
local maxDelta = 300 * dt
smoothedSpeed = smoothedSpeed + util.clamp(speed - smoothedSpeed, -maxDelta, maxDelta)
@ -126,7 +128,7 @@ local function updateStandingPreview()
third_person.standingPreview = false
return
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
third_person.standingPreview = true
camera.setMode(MODE.Preview)

View file

@ -3,6 +3,8 @@ local self = require('openmw.self')
local settings = require('openmw.settings')
local util = require('openmw.util')
local Actor = require('openmw.types').Actor
local doubleStepLength = settings._getFloatFromSettingsCfg('Camera', 'head bobbing step') * 2
local stepHeight = settings._getFloatFromSettingsCfg('Camera', 'head bobbing height')
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)
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
totalMovement = totalMovement + speed * dt
if not M.enabled or camera.getMode() ~= camera.MODE.FirstPerson then
effectWeight = 0
return
end
if self:isOnGround() then
if Actor.isOnGround(self) then
effectWeight = math.min(1, effectWeight + dt * 5)
else
effectWeight = math.max(0, effectWeight - dt * 5)

View file

@ -4,6 +4,8 @@ local util = require('openmw.util')
local self = require('openmw.self')
local nearby = require('openmw.nearby')
local Actor = require('openmw.types').Actor
local MODE = camera.MODE
local STATE = { RightShoulder = 0, LeftShoulder = 1, Combat = 2, Swimming = 3 }
@ -70,9 +72,9 @@ local noThirdPersonLastFrame = true
local function updateState()
local mode = camera.getMode()
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
elseif self:isSwimming() then
elseif Actor.isSwimming(self) then
state = STATE.Swimming
elseif oldState == STATE.Combat or oldState == STATE.Swimming then
state = defaultShoulder