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:
parent
aa6cba9b17
commit
af93ebf433
15 changed files with 286 additions and 200 deletions
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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&);
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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>>;
|
||||
|
|
|
@ -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);
|
||||
|
|
137
apps/openmw/mwlua/types/actor.cpp
Normal file
137
apps/openmw/mwlua/types/actor.cpp
Normal 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)));
|
||||
};
|
||||
}
|
||||
|
||||
}
|
34
apps/openmw/mwlua/types/door.cpp
Normal file
34
apps/openmw/mwlua/types/door.cpp
Normal 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;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
14
apps/openmw/mwlua/types/types.hpp
Normal file
14
apps/openmw/mwlua/types/types.hpp
Normal 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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue