mirror of https://github.com/OpenMW/openmw.git
Merge branch 'lua_new_api' into 'master'
Redesign OpenMW Lua API for game objects Closes #6610 See merge request OpenMW/openmw!1686pull/3226/head
commit
2325b16f8f
@ -1,191 +0,0 @@
|
||||
#include "query.hpp"
|
||||
|
||||
#include <sol/sol.hpp>
|
||||
|
||||
#include <components/lua/luastate.hpp>
|
||||
|
||||
#include "../mwclass/container.hpp"
|
||||
#include "../mwworld/cellstore.hpp"
|
||||
|
||||
#include "worldview.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
static std::vector<QueryFieldGroup> initBasicFieldGroups()
|
||||
{
|
||||
auto createGroup = [](std::string name, const auto& arr) -> QueryFieldGroup
|
||||
{
|
||||
std::vector<const Queries::Field*> fieldPtrs;
|
||||
fieldPtrs.reserve(arr.size());
|
||||
for (const Queries::Field& field : arr)
|
||||
fieldPtrs.push_back(&field);
|
||||
return {std::move(name), std::move(fieldPtrs)};
|
||||
};
|
||||
static std::array objectFields = {
|
||||
Queries::Field({"type"}, typeid(std::string)),
|
||||
Queries::Field({"recordId"}, typeid(std::string)),
|
||||
Queries::Field({"cell", "name"}, typeid(std::string)),
|
||||
Queries::Field({"cell", "region"}, typeid(std::string)),
|
||||
Queries::Field({"cell", "isExterior"}, typeid(bool)),
|
||||
Queries::Field({"count"}, typeid(int32_t)),
|
||||
};
|
||||
static std::array doorFields = {
|
||||
Queries::Field({"isTeleport"}, typeid(bool)),
|
||||
Queries::Field({"destCell", "name"}, typeid(std::string)),
|
||||
Queries::Field({"destCell", "region"}, typeid(std::string)),
|
||||
Queries::Field({"destCell", "isExterior"}, typeid(bool)),
|
||||
};
|
||||
return std::vector<QueryFieldGroup>{
|
||||
createGroup("OBJECT", objectFields),
|
||||
createGroup("DOOR", doorFields),
|
||||
};
|
||||
}
|
||||
|
||||
const std::vector<QueryFieldGroup>& getBasicQueryFieldGroups()
|
||||
{
|
||||
static std::vector<QueryFieldGroup> fieldGroups = initBasicFieldGroups();
|
||||
return fieldGroups;
|
||||
}
|
||||
|
||||
bool checkQueryConditions(const Queries::Query& query, const ObjectId& id, const Context& context)
|
||||
{
|
||||
auto compareFn = [](auto&& a, auto&& b, Queries::Condition::Type t)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case Queries::Condition::EQUAL: return a == b;
|
||||
case Queries::Condition::NOT_EQUAL: return a != b;
|
||||
case Queries::Condition::GREATER: return a > b;
|
||||
case Queries::Condition::GREATER_OR_EQUAL: return a >= b;
|
||||
case Queries::Condition::LESSER: return a < b;
|
||||
case Queries::Condition::LESSER_OR_EQUAL: return a <= b;
|
||||
default:
|
||||
throw std::runtime_error("Unsupported condition type");
|
||||
}
|
||||
};
|
||||
sol::object obj;
|
||||
MWWorld::Ptr ptr;
|
||||
if (context.mIsGlobal)
|
||||
{
|
||||
GObject g(id, context.mWorldView->getObjectRegistry());
|
||||
if (!g.isValid())
|
||||
return false;
|
||||
ptr = g.ptr();
|
||||
obj = sol::make_object(context.mLua->sol(), g);
|
||||
}
|
||||
else
|
||||
{
|
||||
LObject l(id, context.mWorldView->getObjectRegistry());
|
||||
if (!l.isValid())
|
||||
return false;
|
||||
ptr = l.ptr();
|
||||
obj = sol::make_object(context.mLua->sol(), l);
|
||||
}
|
||||
if (ptr.getRefData().getCount() == 0)
|
||||
return false;
|
||||
|
||||
// It is important to exclude all markers before checking what class it is.
|
||||
// For example "prisonmarker" has class "Door" despite that it is only an invisible marker.
|
||||
if (isMarker(ptr))
|
||||
return false;
|
||||
|
||||
const MWWorld::Class& cls = ptr.getClass();
|
||||
if (cls.isActivator() != (query.mQueryType == ObjectQueryTypes::ACTIVATORS))
|
||||
return false;
|
||||
if (cls.isActor() != (query.mQueryType == ObjectQueryTypes::ACTORS))
|
||||
return false;
|
||||
if (cls.isDoor() != (query.mQueryType == ObjectQueryTypes::DOORS))
|
||||
return false;
|
||||
if ((typeid(cls) == typeid(MWClass::Container)) != (query.mQueryType == ObjectQueryTypes::CONTAINERS))
|
||||
return false;
|
||||
|
||||
std::vector<char> condStack;
|
||||
for (const Queries::Operation& op : query.mFilter.mOperations)
|
||||
{
|
||||
switch(op.mType)
|
||||
{
|
||||
case Queries::Operation::PUSH:
|
||||
{
|
||||
const Queries::Condition& cond = query.mFilter.mConditions[op.mConditionIndex];
|
||||
sol::object fieldObj = obj;
|
||||
for (const std::string& field : cond.mField->path())
|
||||
fieldObj = LuaUtil::getFieldOrNil(fieldObj, field);
|
||||
bool c;
|
||||
if (fieldObj == sol::nil)
|
||||
c = false;
|
||||
else if (cond.mField->type() == typeid(std::string))
|
||||
c = compareFn(fieldObj.as<std::string_view>(), std::get<std::string>(cond.mValue), cond.mType);
|
||||
else if (cond.mField->type() == typeid(float))
|
||||
c = compareFn(fieldObj.as<float>(), std::get<float>(cond.mValue), cond.mType);
|
||||
else if (cond.mField->type() == typeid(double))
|
||||
c = compareFn(fieldObj.as<double>(), std::get<double>(cond.mValue), cond.mType);
|
||||
else if (cond.mField->type() == typeid(bool))
|
||||
c = compareFn(fieldObj.as<bool>(), std::get<bool>(cond.mValue), cond.mType);
|
||||
else if (cond.mField->type() == typeid(int32_t))
|
||||
c = compareFn(fieldObj.as<int32_t>(), std::get<int32_t>(cond.mValue), cond.mType);
|
||||
else if (cond.mField->type() == typeid(int64_t))
|
||||
c = compareFn(fieldObj.as<int64_t>(), std::get<int64_t>(cond.mValue), cond.mType);
|
||||
else
|
||||
throw std::runtime_error("Unknown field type");
|
||||
condStack.push_back(c);
|
||||
break;
|
||||
}
|
||||
case Queries::Operation::NOT:
|
||||
condStack.back() = !condStack.back();
|
||||
break;
|
||||
case Queries::Operation::AND:
|
||||
{
|
||||
bool v = condStack.back();
|
||||
condStack.pop_back();
|
||||
condStack.back() = condStack.back() && v;
|
||||
break;
|
||||
}
|
||||
case Queries::Operation::OR:
|
||||
{
|
||||
bool v = condStack.back();
|
||||
condStack.pop_back();
|
||||
condStack.back() = condStack.back() || v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return condStack.empty() || condStack.back() != 0;
|
||||
}
|
||||
|
||||
ObjectIdList selectObjectsFromList(const Queries::Query& query, const ObjectIdList& list, const Context& context)
|
||||
{
|
||||
if (!query.mOrderBy.empty() || !query.mGroupBy.empty() || query.mOffset > 0)
|
||||
throw std::runtime_error("OrderBy, GroupBy, and Offset are not supported");
|
||||
|
||||
ObjectIdList res = std::make_shared<std::vector<ObjectId>>();
|
||||
for (const ObjectId& id : *list)
|
||||
{
|
||||
if (static_cast<int64_t>(res->size()) == query.mLimit)
|
||||
break;
|
||||
if (checkQueryConditions(query, id, context))
|
||||
res->push_back(id);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
ObjectIdList selectObjectsFromCellStore(const Queries::Query& query, MWWorld::CellStore* store, const Context& context)
|
||||
{
|
||||
if (!query.mOrderBy.empty() || !query.mGroupBy.empty() || query.mOffset > 0)
|
||||
throw std::runtime_error("OrderBy, GroupBy, and Offset are not supported");
|
||||
|
||||
ObjectIdList res = std::make_shared<std::vector<ObjectId>>();
|
||||
auto visitor = [&](const MWWorld::Ptr& ptr)
|
||||
{
|
||||
if (static_cast<int64_t>(res->size()) == query.mLimit)
|
||||
return false;
|
||||
context.mWorldView->getObjectRegistry()->registerPtr(ptr);
|
||||
if (checkQueryConditions(query, getId(ptr), context))
|
||||
res->push_back(getId(ptr));
|
||||
return static_cast<int64_t>(res->size()) != query.mLimit;
|
||||
};
|
||||
store->forEach(std::move(visitor)); // TODO: maybe use store->forEachType<TYPE> depending on query.mType
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
#ifndef MWLUA_QUERY_H
|
||||
#define MWLUA_QUERY_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <components/queries/query.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
#include "object.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
struct ObjectQueryTypes
|
||||
{
|
||||
static constexpr std::string_view ACTIVATORS = "activators";
|
||||
static constexpr std::string_view ACTORS = "actors";
|
||||
static constexpr std::string_view CONTAINERS = "containers";
|
||||
static constexpr std::string_view DOORS = "doors";
|
||||
static constexpr std::string_view ITEMS = "items";
|
||||
|
||||
static constexpr std::string_view types[] = {ACTIVATORS, ACTORS, CONTAINERS, DOORS, ITEMS};
|
||||
};
|
||||
|
||||
struct QueryFieldGroup
|
||||
{
|
||||
std::string mName;
|
||||
std::vector<const Queries::Field*> mFields;
|
||||
};
|
||||
const std::vector<QueryFieldGroup>& getBasicQueryFieldGroups();
|
||||
|
||||
// TODO: Implement custom fields. QueryFieldGroup registerCustomFields(...);
|
||||
|
||||
ObjectIdList selectObjectsFromList(const Queries::Query& query, const ObjectIdList& list, const Context&);
|
||||
ObjectIdList selectObjectsFromCellStore(const Queries::Query& query, MWWorld::CellStore* store, const Context&);
|
||||
|
||||
}
|
||||
|
||||
#endif // MWLUA_QUERY_H
|
@ -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["inventory"] = sol::overload(
|
||||
[](const LObject& o) { return Inventory<LObject>{o}; },
|
||||
[](const GObject& o) { return Inventory<GObject>{o}; }
|
||||
);
|
||||
actor["equipment"] = [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 SelfObject& obj, const 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<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)));
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
#include "types.hpp"
|
||||
|
||||
#include "../luabindings.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
static const MWWorld::Ptr& containerPtr(const Object& o) { return verifyType(ESM::REC_CONT, o.ptr()); }
|
||||
|
||||
void addContainerBindings(sol::table container, const Context& context)
|
||||
{
|
||||
container["content"] = sol::overload(
|
||||
[](const LObject& o) { containerPtr(o); return Inventory<LObject>{o}; },
|
||||
[](const GObject& o) { containerPtr(o); return Inventory<GObject>{o}; }
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,193 @@
|
||||
#include "types.hpp"
|
||||
|
||||
#include <components/lua/luastate.hpp>
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
namespace ObjectTypeName
|
||||
{
|
||||
// Names of object types in Lua.
|
||||
// These names are part of OpenMW Lua API.
|
||||
constexpr std::string_view Actor = "Actor"; // base type for NPC, Creature, Player
|
||||
constexpr std::string_view Item = "Item"; // base type for all items
|
||||
|
||||
constexpr std::string_view Activator = "Activator";
|
||||
constexpr std::string_view Armor = "Armor";
|
||||
constexpr std::string_view Book = "Book";
|
||||
constexpr std::string_view Clothing = "Clothing";
|
||||
constexpr std::string_view Container = "Container";
|
||||
constexpr std::string_view Creature = "Creature";
|
||||
constexpr std::string_view Door = "Door";
|
||||
constexpr std::string_view Ingredient = "Ingredient";
|
||||
constexpr std::string_view Light = "Light";
|
||||
constexpr std::string_view MiscItem = "Miscellaneous";
|
||||
constexpr std::string_view NPC = "NPC";
|
||||
constexpr std::string_view Player = "Player";
|
||||
constexpr std::string_view Potion = "Potion";
|
||||
constexpr std::string_view Static = "Static";
|
||||
constexpr std::string_view Weapon = "Weapon";
|
||||
constexpr std::string_view Apparatus = "Apparatus";
|
||||
constexpr std::string_view Lockpick = "Lockpick";
|
||||
constexpr std::string_view Probe = "Probe";
|
||||
constexpr std::string_view Repair = "Repair";
|
||||
constexpr std::string_view Marker = "Marker";
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
struct LuaObjectTypeInfo
|
||||
{
|
||||
std::string_view mName;
|
||||
ESM::LuaScriptCfg::Flags mFlag = 0;
|
||||
};
|
||||
|
||||
const static std::unordered_map<ESM::RecNameInts, LuaObjectTypeInfo> luaObjectTypeInfo = {
|
||||
{ESM::REC_INTERNAL_PLAYER, {ObjectTypeName::Player, ESM::LuaScriptCfg::sPlayer}},
|
||||
{ESM::REC_INTERNAL_MARKER, {ObjectTypeName::Marker}},
|
||||
{ESM::REC_ACTI, {ObjectTypeName::Activator, ESM::LuaScriptCfg::sActivator}},
|
||||
{ESM::REC_ARMO, {ObjectTypeName::Armor, ESM::LuaScriptCfg::sArmor}},
|
||||
{ESM::REC_BOOK, {ObjectTypeName::Book, ESM::LuaScriptCfg::sBook}},
|
||||
{ESM::REC_CLOT, {ObjectTypeName::Clothing, ESM::LuaScriptCfg::sClothing}},
|
||||
{ESM::REC_CONT, {ObjectTypeName::Container, ESM::LuaScriptCfg::sContainer}},
|
||||
{ESM::REC_CREA, {ObjectTypeName::Creature, ESM::LuaScriptCfg::sCreature}},
|
||||
{ESM::REC_DOOR, {ObjectTypeName::Door, ESM::LuaScriptCfg::sDoor}},
|
||||
{ESM::REC_INGR, {ObjectTypeName::Ingredient, ESM::LuaScriptCfg::sIngredient}},
|
||||
{ESM::REC_LIGH, {ObjectTypeName::Light, ESM::LuaScriptCfg::sLight}},
|
||||
{ESM::REC_MISC, {ObjectTypeName::MiscItem, ESM::LuaScriptCfg::sMiscItem}},
|
||||
{ESM::REC_NPC_, {ObjectTypeName::NPC, ESM::LuaScriptCfg::sNPC}},
|
||||
{ESM::REC_ALCH, {ObjectTypeName::Potion, ESM::LuaScriptCfg::sPotion}},
|
||||
{ESM::REC_STAT, {ObjectTypeName::Static}},
|
||||
{ESM::REC_WEAP, {ObjectTypeName::Weapon, ESM::LuaScriptCfg::sWeapon}},
|
||||
{ESM::REC_APPA, {ObjectTypeName::Apparatus}},
|
||||
{ESM::REC_LOCK, {ObjectTypeName::Lockpick}},
|
||||
{ESM::REC_PROB, {ObjectTypeName::Probe}},
|
||||
{ESM::REC_REPA, {ObjectTypeName::Repair}},
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
std::string_view getLuaObjectTypeName(ESM::RecNameInts type, std::string_view fallback)
|
||||
{
|
||||
auto it = luaObjectTypeInfo.find(type);
|
||||
if (it != luaObjectTypeInfo.end())
|
||||
return it->second.mName;
|
||||
else
|
||||
return fallback;
|
||||
}
|
||||
|
||||
std::string_view getLuaObjectTypeName(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
return getLuaObjectTypeName(static_cast<ESM::RecNameInts>(ptr.getLuaType()), /*fallback=*/ptr.getTypeDescription());
|
||||
}
|
||||
|
||||
ESM::LuaScriptCfg::Flags getLuaScriptFlag(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
auto it = luaObjectTypeInfo.find(static_cast<ESM::RecNameInts>(ptr.getLuaType()));
|
||||
if (it != luaObjectTypeInfo.end())
|
||||
return it->second.mFlag;
|
||||
else
|
||||
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;
|
||||
}
|
||||
|
||||
sol::table getTypeToPackageTable(lua_State* L)
|
||||
{
|
||||
constexpr std::string_view key = "typeToPackage";
|
||||
sol::state_view lua(L);
|
||||
if (lua[key] == sol::nil)
|
||||
lua[key] = sol::table(lua, sol::create);
|
||||
return lua[key];
|
||||
}
|
||||
|
||||
sol::table getPackageToTypeTable(lua_State* L)
|
||||
{
|
||||
constexpr std::string_view key = "packageToType";
|
||||
sol::state_view lua(L);
|
||||
if (lua[key] == sol::nil)
|
||||
lua[key] = sol::table(lua, sol::create);
|
||||
return lua[key];
|
||||
}
|
||||
|
||||
sol::table initTypesPackage(const Context& context)
|
||||
{
|
||||
auto* lua = context.mLua;
|
||||
sol::table types(lua->sol(), sol::create);
|
||||
auto addType = [&](std::string_view name, std::vector<ESM::RecNameInts> recTypes,
|
||||
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["baseType"] = types[*base];
|
||||
sol::table baseMeta(lua->sol(), sol::create);
|
||||
baseMeta[sol::meta_function::index] = LuaUtil::getMutableFromReadOnly(types[*base]);
|
||||
t[sol::metatable_key] = baseMeta;
|
||||
}
|
||||
t["objectIsInstance"] = [types=recTypes](const Object& o)
|
||||
{
|
||||
unsigned int type = o.ptr().getLuaType();
|
||||
for (ESM::RecNameInts t : types)
|
||||
if (t == type)
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
types[name] = ro;
|
||||
return t;
|
||||
};
|
||||
|
||||
addActorBindings(addType(ObjectTypeName::Actor, {ESM::REC_INTERNAL_PLAYER, ESM::REC_CREA, ESM::REC_NPC_}), context);
|
||||
addType(ObjectTypeName::Item, {ESM::REC_ARMO, ESM::REC_BOOK, ESM::REC_CLOT, ESM::REC_INGR,
|
||||
ESM::REC_LIGH, ESM::REC_MISC, ESM::REC_ALCH, ESM::REC_WEAP,
|
||||
ESM::REC_APPA, ESM::REC_LOCK, ESM::REC_PROB, ESM::REC_REPA});
|
||||
|
||||
addType(ObjectTypeName::Creature, {ESM::REC_CREA}, ObjectTypeName::Actor);
|
||||
addType(ObjectTypeName::NPC, {ESM::REC_INTERNAL_PLAYER, ESM::REC_NPC_}, ObjectTypeName::Actor);
|
||||
addType(ObjectTypeName::Player, {ESM::REC_INTERNAL_PLAYER}, ObjectTypeName::NPC);
|
||||
|
||||
addType(ObjectTypeName::Armor, {ESM::REC_ARMO}, ObjectTypeName::Item);
|
||||
addType(ObjectTypeName::Book, {ESM::REC_BOOK}, ObjectTypeName::Item);
|
||||
addType(ObjectTypeName::Clothing, {ESM::REC_CLOT}, ObjectTypeName::Item);
|
||||
addType(ObjectTypeName::Ingredient, {ESM::REC_INGR}, ObjectTypeName::Item);
|
||||
addType(ObjectTypeName::Light, {ESM::REC_LIGH}, ObjectTypeName::Item);
|
||||
addType(ObjectTypeName::MiscItem, {ESM::REC_MISC}, ObjectTypeName::Item);
|
||||
addType(ObjectTypeName::Potion, {ESM::REC_ALCH}, ObjectTypeName::Item);
|
||||
addType(ObjectTypeName::Weapon, {ESM::REC_WEAP}, ObjectTypeName::Item);
|
||||
addType(ObjectTypeName::Apparatus, {ESM::REC_APPA}, ObjectTypeName::Item);
|
||||
addType(ObjectTypeName::Lockpick, {ESM::REC_LOCK}, ObjectTypeName::Item);
|
||||
addType(ObjectTypeName::Probe, {ESM::REC_PROB}, ObjectTypeName::Item);
|
||||
addType(ObjectTypeName::Repair, {ESM::REC_REPA}, ObjectTypeName::Item);
|
||||
|
||||
addType(ObjectTypeName::Activator, {ESM::REC_ACTI});
|
||||
addContainerBindings(addType(ObjectTypeName::Container, {ESM::REC_CONT}), context);
|
||||
addDoorBindings(addType(ObjectTypeName::Door, {ESM::REC_DOOR}), context);
|
||||
addType(ObjectTypeName::Static, {ESM::REC_STAT});
|
||||
|
||||
sol::table typeToPackage = getTypeToPackageTable(context.mLua->sol());
|
||||
sol::table packageToType = getPackageToTypeTable(context.mLua->sol());
|
||||
for (const auto& [type, v] : luaObjectTypeInfo)
|
||||
{
|
||||
sol::object t = types[v.mName];
|
||||
if (t == sol::nil)
|
||||
continue;
|
||||
typeToPackage[type] = t;
|
||||
packageToType[t] = type;
|
||||
}
|
||||
|
||||
return LuaUtil::makeReadOnly(types);
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
#ifndef MWLUA_TYPES_H
|
||||
#define MWLUA_TYPES_H
|
||||
|
||||
#include <sol/sol.hpp>
|
||||
|
||||
#include <components/esm/defs.hpp>
|
||||
#include <components/esm/luascripts.hpp>
|
||||
|
||||
#include "../context.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
std::string_view getLuaObjectTypeName(ESM::RecNameInts type, std::string_view fallback = "Unknown");
|
||||
std::string_view getLuaObjectTypeName(const MWWorld::Ptr& ptr);
|
||||
const MWWorld::Ptr& verifyType(ESM::RecNameInts type, const MWWorld::Ptr& ptr);
|
||||
|
||||
sol::table getTypeToPackageTable(lua_State* L);
|
||||
sol::table getPackageToTypeTable(lua_State* L);
|
||||
|
||||
// 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.
|
||||
ESM::LuaScriptCfg::Flags getLuaScriptFlag(const MWWorld::Ptr& ptr);
|
||||
|
||||
sol::table initTypesPackage(const Context& context);
|
||||
|
||||
// used in initTypesPackage
|
||||
void addContainerBindings(sol::table door, const Context& context);
|
||||
void addDoorBindings(sol::table door, const Context& context);
|
||||
void addActorBindings(sol::table actor, const Context& context);
|
||||
}
|
||||
|
||||
#endif // MWLUA_TYPES_H
|
@ -1,29 +0,0 @@
|
||||
#include "gmock/gmock.h"
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <components/queries/luabindings.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace testing;
|
||||
|
||||
TEST(LuaQueryPackageTest, basic)
|
||||
{
|
||||
sol::state lua;
|
||||
lua.open_libraries(sol::lib::base, sol::lib::string);
|
||||
Queries::registerQueryBindings(lua);
|
||||
lua["query"] = Queries::Query("test");
|
||||
lua["fieldX"] = Queries::Field({ "x" }, typeid(std::string));
|
||||
lua["fieldY"] = Queries::Field({ "y" }, typeid(int));
|
||||
lua.safe_script("t = query:where(fieldX:eq('abc') + fieldX:like('%abcd%'))");
|
||||
lua.safe_script("t = t:where(fieldY:gt(5))");
|
||||
lua.safe_script("t = t:orderBy(fieldX)");
|
||||
lua.safe_script("t = t:orderByDesc(fieldY)");
|
||||
lua.safe_script("t = t:groupBy(fieldY)");
|
||||
lua.safe_script("t = t:limit(10):offset(5)");
|
||||
EXPECT_EQ(
|
||||
lua.safe_script("return tostring(t)").get<std::string>(),
|
||||
"SELECT test WHERE ((x == \"abc\") OR (x LIKE \"%abcd%\")) AND (y > 5) ORDER BY x, y DESC GROUP BY y LIMIT 10 OFFSET 5");
|
||||
}
|
||||
}
|
||||
|
@ -1,118 +0,0 @@
|
||||
#include "luabindings.hpp"
|
||||
|
||||
namespace sol
|
||||
{
|
||||
template <>
|
||||
struct is_automagical<Queries::Field> : std::false_type {};
|
||||
|
||||
template <>
|
||||
struct is_automagical<Queries::Filter> : std::false_type {};
|
||||
|
||||
template <>
|
||||
struct is_automagical<Queries::Query> : std::false_type {};
|
||||
}
|
||||
|
||||
namespace Queries
|
||||
{
|
||||
template <Condition::Type type>
|
||||
struct CondBuilder
|
||||
{
|
||||
Filter operator()(const Field& field, const sol::object& o)
|
||||
{
|
||||
FieldValue value;
|
||||
if (field.type() == typeid(bool) && o.is<bool>())
|
||||
value = o.as<bool>();
|
||||
else if (field.type() == typeid(int32_t) && o.is<int32_t>())
|
||||
value = o.as<int32_t>();
|
||||
else if (field.type() == typeid(int64_t) && o.is<int64_t>())
|
||||
value = o.as<int64_t>();
|
||||
else if (field.type() == typeid(float) && o.is<float>())
|
||||
value = o.as<float>();
|
||||
else if (field.type() == typeid(double) && o.is<double>())
|
||||
value = o.as<double>();
|
||||
else if (field.type() == typeid(std::string) && o.is<std::string>())
|
||||
value = o.as<std::string>();
|
||||
else
|
||||
throw std::logic_error("Invalid value for field " + field.toString());
|
||||
Filter filter;
|
||||
filter.add({&field, type, value});
|
||||
return filter;
|
||||
}
|
||||
};
|
||||
|
||||
void registerQueryBindings(sol::state& lua)
|
||||
{
|
||||
sol::usertype<Field> field = lua.new_usertype<Field>("QueryField");
|
||||
sol::usertype<Filter> filter = lua.new_usertype<Filter>("QueryFilter");
|
||||
sol::usertype<Query> query = lua.new_usertype<Query>("Query");
|
||||
|
||||
field[sol::meta_function::to_string] = [](const Field& f) { return f.toString(); };
|
||||
field["eq"] = CondBuilder<Condition::EQUAL>();
|
||||
field["neq"] = CondBuilder<Condition::NOT_EQUAL>();
|
||||
field["lt"] = CondBuilder<Condition::LESSER>();
|
||||
field["lte"] = CondBuilder<Condition::LESSER_OR_EQUAL>();
|
||||
field["gt"] = CondBuilder<Condition::GREATER>();
|
||||
field["gte"] = CondBuilder<Condition::GREATER_OR_EQUAL>();
|
||||
field["like"] = CondBuilder<Condition::LIKE>();
|
||||
|
||||
filter[sol::meta_function::to_string] = [](const Filter& filter) { return filter.toString(); };
|
||||
filter[sol::meta_function::multiplication] = [](const Filter& a, const Filter& b)
|
||||
{
|
||||
Filter res = a;
|
||||
res.add(b, Operation::AND);
|
||||
return res;
|
||||
};
|
||||
filter[sol::meta_function::addition] = [](const Filter& a, const Filter& b)
|
||||
{
|
||||
Filter res = a;
|
||||
res.add(b, Operation::OR);
|
||||
return res;
|
||||
};
|
||||
filter[sol::meta_function::unary_minus] = [](const Filter& a)
|
||||
{
|
||||
Filter res = a;
|
||||
if (!a.mConditions.empty())
|
||||
res.mOperations.push_back({Operation::NOT, 0});
|
||||
return res;
|
||||
};
|
||||
|
||||
query[sol::meta_function::to_string] = [](const Query& q) { return q.toString(); };
|
||||
query["where"] = [](const Query& q, const Filter& filter)
|
||||
{
|
||||
Query res = q;
|
||||
res.mFilter.add(filter, Operation::AND);
|
||||
return res;
|
||||
};
|
||||
query["orderBy"] = [](const Query& q, const Field& field)
|
||||
{
|
||||
Query res = q;
|
||||
res.mOrderBy.push_back({&field, false});
|
||||
return res;
|
||||
};
|
||||
query["orderByDesc"] = [](const Query& q, const Field& field)
|
||||
{
|
||||
Query res = q;
|
||||
res.mOrderBy.push_back({&field, true});
|
||||
return res;
|
||||
};
|
||||
query["groupBy"] = [](const Query& q, const Field& field)
|
||||
{
|
||||
Query res = q;
|
||||
res.mGroupBy.push_back(&field);
|
||||
return res;
|
||||
};
|
||||
query["offset"] = [](const Query& q, int64_t offset)
|
||||
{
|
||||
Query res = q;
|
||||
res.mOffset = offset;
|
||||
return res;
|
||||
};
|
||||
query["limit"] = [](const Query& q, int64_t limit)
|
||||
{
|
||||
Query res = q;
|
||||
res.mLimit = limit;
|
||||
return res;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +0,0 @@
|
||||
#include <limits> // missing from sol/sol.hpp
|
||||
#include <sol/sol.hpp>
|
||||
|
||||
#include "query.hpp"
|
||||
|
||||
namespace Queries
|
||||
{
|
||||
void registerQueryBindings(sol::state& lua);
|
||||
}
|
@ -1,185 +0,0 @@
|
||||
#include "query.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
namespace Queries
|
||||
{
|
||||
Field::Field(std::vector<std::string> path, std::type_index type)
|
||||
: mPath(std::move(path))
|
||||
, mType(type) {}
|
||||
|
||||
std::string Field::toString() const
|
||||
{
|
||||
std::string result;
|
||||
for (const std::string& segment : mPath)
|
||||
{
|
||||
if (!result.empty())
|
||||
result += ".";
|
||||
result += segment;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string toString(const FieldValue& value)
|
||||
{
|
||||
return std::visit([](auto&& arg) -> std::string
|
||||
{
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr (std::is_same_v<T, std::string>)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << std::quoted(arg);
|
||||
return oss.str();
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, bool>)
|
||||
return arg ? "true" : "false";
|
||||
else
|
||||
return std::to_string(arg);
|
||||
}, value);
|
||||
}
|
||||
|
||||
std::string Condition::toString() const
|
||||
{
|
||||
std::string res;
|
||||
res += mField->toString();
|
||||
switch (mType)
|
||||
{
|
||||
case Condition::EQUAL: res += " == "; break;
|
||||
case Condition::NOT_EQUAL: res += " != "; break;
|
||||
case Condition::LESSER: res += " < "; break;
|
||||
case Condition::LESSER_OR_EQUAL: res += " <= "; break;
|
||||
case Condition::GREATER: res += " > "; break;
|
||||
case Condition::GREATER_OR_EQUAL: res += " >= "; break;
|
||||
case Condition::LIKE: res += " LIKE "; break;
|
||||
}
|
||||
res += Queries::toString(mValue);
|
||||
return res;
|
||||
}
|
||||
|
||||
void Filter::add(const Condition& c, Operation::Type op)
|
||||
{
|
||||
mOperations.push_back({Operation::PUSH, mConditions.size()});
|
||||
mConditions.push_back(c);
|
||||
if (mConditions.size() > 1)
|
||||
mOperations.push_back({op, 0});
|
||||
}
|
||||
|
||||
void Filter::add(const Filter& f, Operation::Type op)
|
||||
{
|
||||
size_t conditionOffset = mConditions.size();
|
||||
size_t operationsBefore = mOperations.size();
|
||||
mConditions.insert(mConditions.end(), f.mConditions.begin(), f.mConditions.end());
|
||||
mOperations.insert(mOperations.end(), f.mOperations.begin(), f.mOperations.end());
|
||||
for (size_t i = operationsBefore; i < mOperations.size(); ++i)
|
||||
mOperations[i].mConditionIndex += conditionOffset;
|
||||
if (conditionOffset > 0 && !f.mConditions.empty())
|
||||
mOperations.push_back({op, 0});
|
||||
}
|
||||
|
||||
std::string Filter::toString() const
|
||||
{
|
||||
if(mOperations.empty())
|
||||
return "";
|
||||
std::vector<std::string> stack;
|
||||
auto pop = [&stack](){ auto v = stack.back(); stack.pop_back(); return v; };
|
||||
auto push = [&stack](const std::string& s) { stack.push_back(s); };
|
||||
for (const Operation& op : mOperations)
|
||||
{
|
||||
if(op.mType == Operation::PUSH)
|
||||
push(mConditions[op.mConditionIndex].toString());
|
||||
else if(op.mType == Operation::AND)
|
||||
{
|
||||
auto rhs = pop();
|
||||
auto lhs = pop();
|
||||
std::string res;
|
||||
res += "(";
|
||||
res += lhs;
|
||||
res += ") AND (";
|
||||
res += rhs;
|
||||
res += ")";
|
||||
push(res);
|
||||
}
|
||||
else if (op.mType == Operation::OR)
|
||||
{
|
||||
auto rhs = pop();
|
||||
auto lhs = pop();
|
||||
std::string res;
|
||||
res += "(";
|
||||
res += lhs;
|
||||
res += ") OR (";
|
||||
res += rhs;
|
||||
res += ")";
|
||||
push(res);
|
||||
}
|
||||
else if (op.mType == Operation::NOT)
|
||||
{
|
||||
std::string res;
|
||||
res += "NOT (";
|
||||
res += pop();
|
||||
res += ")";
|
||||
push(res);
|
||||
}
|
||||
else
|
||||
throw std::logic_error("Unknown operation type!");
|
||||
}
|
||||
return pop();
|
||||
}
|
||||
|
||||
std::string Query::toString() const
|
||||
{
|
||||
std::string res;
|
||||
res += "SELECT ";
|
||||
res += mQueryType;
|
||||
|
||||
std::string filter = mFilter.toString();
|
||||
if(!filter.empty())
|
||||
{
|
||||
res += " WHERE ";
|
||||
res += filter;
|
||||
}
|
||||
|
||||
std::string order;
|
||||
for(const OrderBy& ord : mOrderBy)
|
||||
{
|
||||
if(!order.empty())
|
||||
order += ", ";
|
||||
order += ord.mField->toString();
|
||||
if(ord.mDescending)
|
||||
order += " DESC";
|
||||
}
|
||||
if (!order.empty())
|
||||
{
|
||||
res += " ORDER BY ";
|
||||
res += order;
|
||||
}
|
||||
|
||||
std::string group;
|
||||
for (const Field* f: mGroupBy)
|
||||
{
|
||||
if (!group.empty())
|
||||
group += " ,";
|
||||
group += f->toString();
|
||||
}
|
||||
if (!group.empty())
|
||||
{
|
||||
res += " GROUP BY ";
|
||||
res += group;
|
||||
}
|
||||
|
||||
if (mLimit != sNoLimit)
|
||||
{
|
||||
res += " LIMIT ";
|
||||
res += std::to_string(mLimit);
|
||||
}
|
||||
|
||||
if (mOffset != 0)
|
||||
{
|
||||
res += " OFFSET ";
|
||||
res += std::to_string(mOffset);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
@ -1,99 +0,0 @@
|
||||
#ifndef COMPONENTS_QUERIES_QUERY
|
||||
#define COMPONENTS_QUERIES_QUERY
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <typeindex>
|
||||
#include <variant>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace Queries
|
||||
{
|
||||
class Field
|
||||
{
|
||||
public:
|
||||
Field(std::vector<std::string> path, std::type_index type);
|
||||
|
||||
const std::vector<std::string>& path() const { return mPath; }
|
||||
const std::type_index type() const { return mType; }
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
private:
|
||||
std::vector<std::string> mPath;
|
||||
std::type_index mType;
|
||||
};
|
||||
|
||||
struct OrderBy
|
||||
{
|
||||
const Field* mField;
|
||||
bool mDescending;
|
||||
};
|
||||
|
||||
using FieldValue = std::variant<bool, int32_t, int64_t, float, double, std::string>;
|
||||
std::string toString(const FieldValue& value);
|
||||
|
||||
struct Condition
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
EQUAL = 0,
|
||||
NOT_EQUAL = 1,
|
||||
GREATER = 2,
|
||||
GREATER_OR_EQUAL = 3,
|
||||
LESSER = 4,
|
||||
LESSER_OR_EQUAL = 5,
|
||||
LIKE = 6,
|
||||
};
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
const Field* mField;
|
||||
Type mType;
|
||||
FieldValue mValue;
|
||||
};
|
||||
|
||||
struct Operation
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
PUSH = 0, // push condition on stack
|
||||
NOT = 1, // invert top condition on stack
|
||||
AND = 2, // logic AND for two top conditions
|
||||
OR = 3, // logic OR for two top conditions
|
||||
};
|
||||
|
||||
Type mType;
|
||||
size_t mConditionIndex; // used only if mType == PUSH
|
||||
};
|
||||
|
||||
struct Filter
|
||||
{
|
||||
std::string toString() const;
|
||||
|
||||
// combines with given condition or filter using operation `AND` or `OR`.
|
||||
void add(const Condition& c, Operation::Type op = Operation::AND);
|
||||
void add(const Filter& f, Operation::Type op = Operation::AND);
|
||||
|
||||
std::vector<Condition> mConditions;
|
||||
std::vector<Operation> mOperations; // operations on conditions in reverse polish notation
|
||||
};
|
||||
|
||||
struct Query
|
||||
{
|
||||
static constexpr int64_t sNoLimit = -1;
|
||||
|
||||
Query(std::string type) : mQueryType(std::move(type)) {}
|
||||
std::string toString() const;
|
||||
|
||||
std::string mQueryType;
|
||||
Filter mFilter;
|
||||
std::vector<OrderBy> mOrderBy;
|
||||
std::vector<const Field*> mGroupBy;
|
||||
int64_t mOffset = 0;
|
||||
int64_t mLimit = sNoLimit;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // !COMPONENTS_QUERIES_QUERY
|
||||
|
@ -1,5 +0,0 @@
|
||||
Package openmw.query
|
||||
====================
|
||||
|
||||
.. raw:: html
|
||||
:file: generated_html/openmw_query.html
|
@ -0,0 +1,5 @@
|
||||
Package openmw.types
|
||||
====================
|
||||
|
||||
.. raw:: html
|
||||
:file: generated_html/openmw_types.html
|
@ -1,116 +0,0 @@
|
||||
---
|
||||
-- `openmw.query` constructs queries that can be used in `world.selectObjects` and `nearby.selectObjects`.
|
||||
-- @module query
|
||||
-- @usage local query = require('openmw.query')
|
||||
|
||||
|
||||
|
||||
---
|
||||
-- Query. A Query itself can no return objects. It only holds search conditions.
|
||||
-- @type Query
|
||||
|
||||
---
|
||||
-- Add condition.
|
||||
-- @function [parent=#Query] where
|
||||
-- @param self
|
||||
-- @param condition
|
||||
-- @return #Query
|
||||
|
||||
---
|
||||
-- Limit result size.
|
||||
-- @function [parent=#Query] limit
|
||||
-- @param self
|
||||
-- @param #number maxCount
|
||||
-- @return #Query
|
||||
|
||||
|
||||
---
|
||||
-- A field that can be used in a condition
|
||||
-- @type Field
|
||||
|
||||
---
|
||||
-- Equal
|
||||
-- @function [parent=#Field] eq
|
||||
-- @param self
|
||||
-- @param value
|
||||
|
||||
---
|
||||
-- Not equal
|
||||
-- @function [parent=#Field] neq
|
||||
-- @param self
|
||||
-- @param value
|
||||
|
||||
---
|
||||
-- Lesser
|
||||
-- @function [parent=#Field] lt
|
||||
-- @param self
|
||||
-- @param value
|
||||
|
||||
---
|
||||
-- Lesser or equal
|
||||
-- @function [parent=#Field] lte
|
||||
-- @param self
|
||||
-- @param value
|
||||
|
||||
---
|
||||
-- Greater
|
||||
-- @function [parent=#Field] gt
|
||||
-- @param self
|
||||
-- @param value
|
||||
|
||||
---
|
||||
-- Greater or equal
|
||||
-- @function [parent=#Field] gte
|
||||
-- @param self
|
||||
-- @param value
|
||||
|
||||
|
||||
---
|
||||
-- @type OBJECT
|
||||
-- @field [parent=#OBJECT] #Field type
|
||||
-- @field [parent=#OBJECT] #Field recordId
|
||||
-- @field [parent=#OBJECT] #Field count
|
||||
-- @field [parent=#OBJECT] #CellFields cell
|
||||
|
||||
---
|
||||
-- Fields that can be used with any object.
|
||||
-- @field [parent=#query] #OBJECT OBJECT
|
||||
|
||||
---
|
||||
-- @type DOOR
|
||||
-- @field [parent=#DOOR] #Field isTeleport
|
||||
-- @field [parent=#DOOR] #CellFields destCell
|
||||
|
||||
---
|
||||
-- Fields that can be used only when search for doors.
|
||||
-- @field [parent=#query] #DOOR DOOR
|
||||
|
||||
---
|
||||
-- @type CellFields
|
||||
-- @field [parent=#CellFields] #Field name
|
||||
-- @field [parent=#CellFields] #Field region
|
||||
-- @field [parent=#CellFields] #Field isExterior
|
||||
|
||||
|
||||
---
|
||||
-- Base Query to select activators.
|
||||
-- @field [parent=#query] #Query activators
|
||||
|
||||
---
|
||||
-- Base Query to select actors.
|
||||
-- @field [parent=#query] #Query actors
|
||||
|
||||
---
|
||||
-- Base Query to select containers.
|
||||
-- @field [parent=#query] #Query containers
|
||||
|
||||
---
|
||||
-- Base Query to select doors.
|
||||
-- @field [parent=#query] #Query doors
|
||||
|
||||
---
|
||||
-- Base Query to select items.
|
||||
-- @field [parent=#query] #Query items
|
||||
|
||||
return nil
|
||||
|
@ -0,0 +1,456 @@
|
||||
---
|
||||
-- `openmw.types` defines functions for specific types of game objects.
|
||||
-- @module types
|
||||
-- @usage local types = require('openmw.types')
|
||||
|
||||
--- Common @{#Actor} functions for Creature, NPC, and Player.
|
||||
-- @field [parent=#types] #Actor Actor
|
||||
|
||||
--- Common functions for Creature, NPC, and Player.
|
||||
-- @type Actor
|
||||
|
||||
---
|
||||
-- Whether the object is an actor.
|
||||
-- @function [parent=#Actor] objectIsInstance
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return #boolean
|
||||
|
||||
---
|
||||
-- Actor inventory.
|
||||
-- @function [parent=#Actor] inventory
|
||||
-- @param openmw.core#GameObject actor
|
||||
-- @return openmw.core#Inventory
|
||||
|
||||
---
|
||||
-- @type EQUIPMENT_SLOT
|
||||
-- @field #number Helmet
|
||||
-- @field #number Cuirass
|
||||
-- @field #number Greaves
|
||||
-- @field #number LeftPauldron
|
||||
-- @field #number RightPauldron
|
||||
-- @field #number LeftGauntlet
|
||||
-- @field #number RightGauntlet
|
||||
-- @field #number Boots
|
||||
-- @field #number Shirt
|
||||
-- @field #number Pants
|
||||
-- @field #number Skirt
|
||||
-- @field #number Robe
|
||||
-- @field #number LeftRing
|
||||
-- @field #number RightRing
|
||||
-- @field #number Amulet
|
||||
-- @field #number Belt
|
||||
-- @field #number CarriedRight
|
||||
-- @field #number CarriedLeft
|
||||
-- @field #number Ammunition
|
||||
|
||||
---
|
||||
-- Available @{#EQUIPMENT_SLOT} values. Used in `Actor.equipment(obj)` and `Actor.setEquipment(obj, eqp)`.
|
||||
-- @field [parent=#Actor] #EQUIPMENT_SLOT EQUIPMENT_SLOT
|
||||
|
||||
---
|
||||
-- @type STANCE
|
||||
-- @field #number Nothing Default stance
|
||||
-- @field #number Weapon Weapon stance
|
||||
-- @field #number Spell Magic stance
|
||||
|
||||
--- @{#STANCE}
|
||||
-- @field [parent=#Actor] #STANCE STANCE
|
||||
|
||||
---
|
||||
-- Returns true if the object is an actor and is able to move. For dead, paralyzed,
|
||||
-- or knocked down actors it returns false.
|
||||
-- @function [parent=#Actor] canMove
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return #boolean
|
||||
|
||||
---
|
||||
-- Speed of running. For dead actors it still returns a positive value.
|
||||
-- @function [parent=#Actor] runSpeed
|
||||
-- @param openmw.core#GameObject actor
|
||||
-- @return #number
|
||||
|
||||
---
|
||||
-- Speed of walking. For dead actors it still returns a positive value.
|
||||
-- @function [parent=#Actor] walkSpeed
|
||||
-- @param openmw.core#GameObject actor
|
||||
-- @return #number
|
||||
|
||||
---
|
||||
-- Current speed.
|
||||
-- @function [parent=#Actor] currentSpeed
|
||||
-- @param openmw.core#GameObject actor
|
||||
-- @return #number
|
||||
|
||||
---
|
||||
-- Is the actor standing on ground. Can be called only from a local script.
|
||||
-- @function [parent=#Actor] isOnGround
|
||||
-- @param openmw.core#GameObject actor
|
||||
-- @return #boolean
|
||||
|
||||
---
|
||||
-- Is the actor in water. Can be called only from a local script.
|
||||
-- @function [parent=#Actor] isSwimming
|
||||
-- @param openmw.core#GameObject actor
|
||||
-- @return #boolean
|
||||
|
||||
---
|
||||
-- Returns the current stance (whether a weapon/spell is readied), see the list of @{#STANCE} values.
|
||||
-- @function [parent=#Actor] stance
|
||||
-- @param openmw.core#GameObject actor
|
||||
-- @return #number
|
||||
|
||||
---
|
||||
-- Returns `true` if the item is equipped on the actor.
|
||||
-- @function [parent=#Actor] isEquipped
|
||||
-- @param openmw.core#GameObject actor
|
||||
-- @param openmw.core#GameObject item
|
||||
-- @return #boolean
|
||||
|
||||
---
|
||||
-- Get equipment.
|
||||
-- Returns a table `slot` -> @{openmw.core#GameObject} of currently equipped items.
|
||||
-- See @{#EQUIPMENT_SLOT}. Returns empty table if the actor doesn't have
|
||||
-- equipment slots.
|
||||
-- @function [parent=#Actor] equipment
|
||||
-- @param openmw.core#GameObject actor
|
||||
-- @return #map<#number,openmw.core#GameObject>
|
||||
|
||||
---
|
||||
-- Set equipment.
|
||||
-- Keys in the table are equipment slots (see @{#EQUIPMENT_SLOT}). Each
|
||||
-- value can be either a `GameObject` or recordId. Raises an error if
|
||||
-- the actor doesn't have equipment slots and table is not empty. Can be
|
||||
-- used only in local scripts and only on self.
|
||||
-- @function [parent=#Actor] setEquipment
|
||||
-- @param openmw.core#GameObject actor
|
||||
-- @param equipment
|
||||
-- @usage local self = require('openmw.self')
|
||||
-- local Actor = require('openmw.types').Actor
|
||||
-- Actor.setEquipment(self, {}) -- unequip all
|
||||
|
||||
|
||||
|
||||
--- @{#Item} functions (all pickable items that can be placed to an inventory or container)
|
||||
-- @field [parent=#types] #Item Item
|
||||
|
||||
--- Functions for pickable items that can be placed to an inventory or container
|
||||
-- @type Item
|
||||
|
||||
---
|
||||
-- Whether the object is an item.
|
||||
-- @function [parent=#Item] objectIsInstance
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return #boolean
|
||||
|
||||
|
||||
|
||||
--- @{#Creature} functions
|
||||
-- @field [parent=#types] #Creature Creature
|
||||
|
||||
---
|
||||
-- @type Creature
|
||||
-- @extends #Actor
|
||||
-- @field #Actor baseType @{#Actor}
|
||||
|
||||
---
|
||||
-- Whether the object is a creature.
|
||||
-- @function [parent=#Creature] objectIsInstance
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return #boolean
|
||||
|
||||
|
||||
|
||||
--- @{#NPC} functions
|
||||
-- @field [parent=#types] #NPC NPC
|
||||
|
||||
---
|
||||
-- @type NPC
|
||||
-- @extends #Actor
|
||||
-- @field #Actor baseType @{#Actor}
|
||||
|
||||
---
|
||||
-- Whether the object is an NPC or a Player.
|
||||
-- @function [parent=#NPC] objectIsInstance
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return #boolean
|
||||
|
||||
|
||||
|
||||
--- @{#Player} functions
|
||||
-- @field [parent=#types] #Player Player
|
||||
|
||||
---
|
||||
-- @type Player
|
||||
-- @extends #NPC
|
||||
-- @field #NPC baseType @{#NPC}
|
||||
|
||||
---
|
||||
-- Whether the object is a player.
|
||||
-- @function [parent=#Player] objectIsInstance
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return #boolean
|
||||
|
||||
|
||||
|
||||
--- @{#Armor} functions
|
||||
-- @field [parent=#types] #Armor Armor
|
||||
|
||||
---
|
||||
-- @type Armor
|
||||
-- @extends #Item
|
||||
-- @field #Item baseType @{#Item}
|
||||
|
||||
---
|
||||
-- Whether the object is an Armor.
|
||||
-- @function [parent=#Armor] objectIsInstance
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return #boolean
|
||||
|
||||
|
||||
|
||||
--- @{#Book} functions
|
||||
-- @field [parent=#types] #Book Book
|
||||
|
||||
---
|
||||
-- @type Book
|
||||
-- @extends #Item
|
||||
-- @field #Item baseType @{#Item}
|
||||
|
||||
---
|
||||
-- Whether the object is a Book.
|
||||
-- @function [parent=#Book] objectIsInstance
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return #boolean
|
||||
|
||||
--- @{#Clothing} functions
|
||||
|
||||
|
||||
-- @field [parent=#types] #Clothing Clothing
|
||||
|
||||
---
|
||||
-- @type Clothing
|
||||
-- @extends #Item
|
||||
-- @field #Item baseType @{#Item}
|
||||
|
||||
---
|
||||
-- Whether the object is a Clothing.
|
||||
-- @function [parent=#Clothing] objectIsInstance
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return #boolean
|
||||
|
||||
|
||||
|
||||
--- @{#Ingredient} functions
|
||||
-- @field [parent=#types] #Ingredient Ingredient
|
||||
|
||||
---
|
||||
-- @type Ingredient
|
||||
-- @extends #Item
|
||||
-- @field #Item baseType @{#Item}
|
||||
|
||||
---
|
||||
-- Whether the object is an Ingredient.
|
||||
-- @function [parent=#Ingredient] objectIsInstance
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return #boolean
|
||||
|
||||
|
||||
|
||||
--- @{#Light} functions
|
||||
-- @field [parent=#types] #Light Light
|
||||
|
||||
---
|
||||
-- @type Light
|
||||
-- @extends #Item
|
||||
-- @field #Item baseType @{#Item}
|
||||
|
||||
---
|
||||
-- Whether the object is a Light.
|
||||
-- @function [parent=#Light] objectIsInstance
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return #boolean
|
||||
|
||||
|
||||
|
||||
--- Functions for @{#Miscellaneous} objects
|
||||
-- @field [parent=#types] #Miscellaneous Miscellaneous
|
||||
|
||||
---
|
||||
-- @type Miscellaneous
|
||||
-- @extends #Item
|
||||
-- @field #Item baseType @{#Item}
|
||||
|
||||
---
|
||||
-- Whether the object is a Miscellaneous.
|
||||
-- @function [parent=#Miscellaneous] objectIsInstance
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return #boolean
|
||||
|
||||
|
||||
|
||||
--- @{#Potion} functions
|
||||
-- @field [parent=#types] #Potion Potion
|
||||
|
||||
---
|
||||
-- @type Potion
|
||||
-- @extends #Item
|
||||
-- @field #Item baseType @{#Item}
|
||||
|
||||
---
|
||||
-- Whether the object is a Potion.
|
||||
-- @function [parent=#Potion] objectIsInstance
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return #boolean
|
||||
|
||||
|
||||
|
||||
--- @{#Weapon} functions
|
||||
-- @field [parent=#types] #Weapon Weapon
|
||||
|
||||
---
|
||||
-- @type Weapon
|
||||
-- @extends #Item
|
||||
-- @field #Item baseType @{#Item}
|
||||
|
||||
---
|
||||
-- Whether the object is a Weapon.
|
||||
-- @function [parent=#Weapon] objectIsInstance
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return #boolean
|
||||
|
||||
--- @{#Apparatus} functions
|
||||
-- @field [parent=#types] #Apparatus Apparatus
|
||||
|
||||
---
|
||||
-- @type Apparatus
|
||||
-- @extends #Item
|
||||
-- @field #Item baseType @{#Item}
|
||||
|
||||
---
|
||||
-- Whether the object is an Apparatus.
|
||||
-- @function [parent=#Apparatus] objectIsInstance
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return #boolean
|
||||
|
||||
--- @{#Lockpick} functions
|
||||
-- @field [parent=#types] #Lockpick Lockpick
|
||||
|
||||
---
|
||||
-- @type Lockpick
|
||||
-- @extends #Item
|
||||
-- @field #Item baseType @{#Item}
|
||||
|
||||
---
|
||||
-- Whether the object is a Lockpick.
|
||||
-- @function [parent=#Lockpick] objectIsInstance
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return #boolean
|
||||
|
||||
--- @{#Probe} functions
|
||||
-- @field [parent=#types] #Probe Probe
|
||||
|
||||
---
|
||||
-- @type Probe
|
||||
-- @extends #Item
|
||||
-- @field #Item baseType @{#Item}
|
||||
|
||||
---
|
||||
-- Whether the object is a Probe.
|
||||
-- @function [parent=#Probe] objectIsInstance
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return #boolean
|
||||
|
||||
--- @{#Repair} functions
|
||||
-- @field [parent=#types] #Repair Repair
|
||||
|
||||
---
|
||||
-- @type Repair
|
||||
-- @extends #Item
|
||||
-- @field #Item baseType @{#Item}
|
||||
|
||||
---
|
||||
-- Whether the object is a Repair.
|
||||
-- @function [parent=#Repair] objectIsInstance
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return #boolean
|
||||
|
||||
--- @{#Activator} functions
|
||||
-- @field [parent=#types] #Activator Activator
|
||||
|
||||
---
|
||||
-- @type Activator
|
||||
|
||||
---
|
||||
-- Whether the object is an Activator.
|
||||
-- @function [parent=#Activator] objectIsInstance
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return #boolean
|
||||
|
||||
--- @{#Container} functions
|
||||
-- @field [parent=#types] #Container Container
|
||||
|
||||
---
|
||||
-- @type Container
|
||||
|
||||
---
|
||||
-- Container content.
|
||||
-- @function [parent=#Container] content
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return openmw.core#Inventory
|
||||
|
||||
---
|
||||
-- Whether the object is a Container.
|
||||
-- @function [parent=#Container] objectIsInstance
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return #boolean
|
||||
|
||||
--- @{#Door} functions
|
||||
-- @field [parent=#types] #Door Door
|
||||
|
||||
---
|
||||
-- @type Door
|
||||
|
||||
---
|
||||
-- Whether the object is a Door.
|
||||
-- @function [parent=#Door] objectIsInstance
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return #boolean
|
||||
|
||||
---
|
||||
-- Whether the door is a teleport.
|
||||
-- @function [parent=#Door] isTeleport
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return #boolean
|
||||
|
||||
---
|
||||
-- Destination (only if a teleport door).
|
||||
-- @function [parent=#Door] destPosition
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return openmw.util#Vector3
|
||||
|
||||
---
|
||||
-- Destination rotation (only if a teleport door).
|
||||
-- @function [parent=#Door] destRotation
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return openmw.util#Vector3
|
||||
|
||||
---
|
||||
-- Destination cell (only if a teleport door).
|
||||
-- @function [parent=#Door] destCell
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return openmw.core#Cell
|
||||
|
||||
--- Functions for @{#Static} objects
|
||||
-- @field [parent=#types] #Static Static
|
||||
|
||||
---
|
||||
-- @type Static
|
||||
|
||||
---
|
||||
-- Whether the object is a Static.
|
||||
-- @function [parent=#Static] objectIsInstance
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return #boolean
|
||||
|
||||
return nil
|
||||
|
Loading…
Reference in New Issue