mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-16 17:59:56 +00:00
Add Cell Lua bindings
This commit is contained in:
parent
de349962d1
commit
c463f52390
12 changed files with 306 additions and 102 deletions
|
@ -57,7 +57,7 @@ add_openmw_dir (mwscript
|
||||||
|
|
||||||
add_openmw_dir (mwlua
|
add_openmw_dir (mwlua
|
||||||
luamanagerimp actions object worldview userdataserializer eventqueue query
|
luamanagerimp actions object worldview userdataserializer eventqueue query
|
||||||
luabindings localscripts objectbindings asyncbindings camerabindings uibindings
|
luabindings localscripts objectbindings cellbindings asyncbindings camerabindings uibindings
|
||||||
)
|
)
|
||||||
|
|
||||||
add_openmw_dir (mwsound
|
add_openmw_dir (mwsound
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
|
|
||||||
|
#include "../mwworld/cellstore.hpp"
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
#include "../mwworld/inventorystore.hpp"
|
#include "../mwworld/inventorystore.hpp"
|
||||||
#include "../mwworld/player.hpp"
|
#include "../mwworld/player.hpp"
|
||||||
|
@ -11,23 +12,14 @@ namespace MWLua
|
||||||
|
|
||||||
void TeleportAction::apply(WorldView& worldView) const
|
void TeleportAction::apply(WorldView& worldView) const
|
||||||
{
|
{
|
||||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
MWWorld::CellStore* cell = worldView.findCell(mCell, mPos);
|
||||||
bool exterior = mCell.empty() || world->getExterior(mCell);
|
|
||||||
MWWorld::CellStore* cell;
|
|
||||||
if (exterior)
|
|
||||||
{
|
|
||||||
int cellX, cellY;
|
|
||||||
world->positionToIndex(mPos.x(), mPos.y(), cellX, cellY);
|
|
||||||
cell = world->getExterior(cellX, cellY);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
cell = world->getInterior(mCell);
|
|
||||||
if (!cell)
|
if (!cell)
|
||||||
{
|
{
|
||||||
Log(Debug::Error) << "LuaManager::applyTeleport -> cell not found: '" << mCell << "'";
|
Log(Debug::Error) << "LuaManager::applyTeleport -> cell not found: '" << mCell << "'";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||||
MWWorld::Ptr obj = worldView.getObjectRegistry()->getPtr(mObject, false);
|
MWWorld::Ptr obj = worldView.getObjectRegistry()->getPtr(mObject, false);
|
||||||
const MWWorld::Class& cls = obj.getClass();
|
const MWWorld::Class& cls = obj.getClass();
|
||||||
bool isPlayer = obj == world->getPlayerPtr();
|
bool isPlayer = obj == world->getPlayerPtr();
|
||||||
|
@ -40,7 +32,7 @@ namespace MWLua
|
||||||
std::memcpy(esmPos.pos, &mPos, sizeof(osg::Vec3f));
|
std::memcpy(esmPos.pos, &mPos, sizeof(osg::Vec3f));
|
||||||
std::memcpy(esmPos.rot, &mRot, sizeof(osg::Vec3f));
|
std::memcpy(esmPos.rot, &mRot, sizeof(osg::Vec3f));
|
||||||
world->getPlayer().setTeleported(true);
|
world->getPlayer().setTeleported(true);
|
||||||
if (exterior)
|
if (cell->isExterior())
|
||||||
world->changeToExteriorCell(esmPos, true);
|
world->changeToExteriorCell(esmPos, true);
|
||||||
else
|
else
|
||||||
world->changeToInteriorCell(mCell, esmPos, true);
|
world->changeToInteriorCell(mCell, esmPos, true);
|
||||||
|
|
62
apps/openmw/mwlua/cellbindings.cpp
Normal file
62
apps/openmw/mwlua/cellbindings.cpp
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
#include "luabindings.hpp"
|
||||||
|
|
||||||
|
#include <components/esm/loadcell.hpp>
|
||||||
|
|
||||||
|
#include "../mwworld/cellstore.hpp"
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
|
||||||
|
template <class CellT, class ObjectT>
|
||||||
|
static void initCellBindings(const std::string& prefix, const Context& context)
|
||||||
|
{
|
||||||
|
sol::usertype<CellT> cellT = context.mLua->sol().new_usertype<CellT>(prefix + "Cell");
|
||||||
|
|
||||||
|
cellT[sol::meta_function::equal_to] = [](const CellT& a, const CellT& b) { return a.mStore == b.mStore; };
|
||||||
|
cellT[sol::meta_function::to_string] = [](const CellT& c)
|
||||||
|
{
|
||||||
|
const ESM::Cell* cell = c.mStore->getCell();
|
||||||
|
std::stringstream res;
|
||||||
|
if (cell->isExterior())
|
||||||
|
res << "exterior(" << cell->getGridX() << ", " << cell->getGridY() << ")";
|
||||||
|
else
|
||||||
|
res << "interior(" << cell->mName << ")";
|
||||||
|
return res.str();
|
||||||
|
};
|
||||||
|
|
||||||
|
cellT["name"] = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->mName; });
|
||||||
|
cellT["region"] = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->mRegion; });
|
||||||
|
cellT["gridX"] = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->getGridX(); });
|
||||||
|
cellT["gridY"] = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->getGridY(); });
|
||||||
|
cellT["isExterior"] = sol::readonly_property([](const CellT& c) { return c.mStore->isExterior(); });
|
||||||
|
cellT["hasWater"] = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->hasWater(); });
|
||||||
|
|
||||||
|
cellT["isInSameSpace"] = [](const CellT& c, const ObjectT& obj)
|
||||||
|
{
|
||||||
|
const MWWorld::Ptr& ptr = obj.ptr();
|
||||||
|
if (!ptr.isInCell())
|
||||||
|
return false;
|
||||||
|
MWWorld::CellStore* cell = ptr.getCell();
|
||||||
|
return cell == c.mStore || (cell->isExterior() && c.mStore->isExterior());
|
||||||
|
};
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<CellT, GCell>)
|
||||||
|
{ // only for global scripts
|
||||||
|
cellT["selectObjects"] = [context](const CellT& cell, const Queries::Query& query)
|
||||||
|
{
|
||||||
|
return GObjectList{selectObjectsFromCellStore(query, cell.mStore, context)};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void initCellBindingsForLocalScripts(const Context& context)
|
||||||
|
{
|
||||||
|
initCellBindings<LCell, LObject>("L", context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void initCellBindingsForGlobalScripts(const Context& context)
|
||||||
|
{
|
||||||
|
initCellBindings<GCell, GObject>("G", context);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -62,6 +62,22 @@ namespace MWLua
|
||||||
{
|
{
|
||||||
sol::table api(context.mLua->sol(), sol::create);
|
sol::table api(context.mLua->sol(), sol::create);
|
||||||
WorldView* worldView = context.mWorldView;
|
WorldView* worldView = context.mWorldView;
|
||||||
|
api["getCellByName"] = [worldView=context.mWorldView](const std::string& name) -> sol::optional<GCell>
|
||||||
|
{
|
||||||
|
MWWorld::CellStore* cell = worldView->findNamedCell(name);
|
||||||
|
if (cell)
|
||||||
|
return GCell{cell};
|
||||||
|
else
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
api["getExteriorCell"] = [worldView=context.mWorldView](int x, int y) -> sol::optional<GCell>
|
||||||
|
{
|
||||||
|
MWWorld::CellStore* cell = worldView->findExteriorCell(x, y);
|
||||||
|
if (cell)
|
||||||
|
return GCell{cell};
|
||||||
|
else
|
||||||
|
return {};
|
||||||
|
};
|
||||||
api["activeActors"] = GObjectList{worldView->getActorsInScene()};
|
api["activeActors"] = GObjectList{worldView->getActorsInScene()};
|
||||||
api["selectObjects"] = [context](const Queries::Query& query)
|
api["selectObjects"] = [context](const Queries::Query& query)
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,6 +11,11 @@
|
||||||
#include "query.hpp"
|
#include "query.hpp"
|
||||||
#include "worldview.hpp"
|
#include "worldview.hpp"
|
||||||
|
|
||||||
|
namespace MWWorld
|
||||||
|
{
|
||||||
|
class CellStore;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWLua
|
namespace MWLua
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -25,6 +30,18 @@ namespace MWLua
|
||||||
void initObjectBindingsForLocalScripts(const Context&);
|
void initObjectBindingsForLocalScripts(const Context&);
|
||||||
void initObjectBindingsForGlobalScripts(const Context&);
|
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&);
|
||||||
|
|
||||||
// Implemented in asyncbindings.cpp
|
// Implemented in asyncbindings.cpp
|
||||||
struct AsyncPackageId
|
struct AsyncPackageId
|
||||||
{
|
{
|
||||||
|
|
|
@ -49,12 +49,19 @@ namespace MWLua
|
||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isMarker(const MWWorld::Ptr& ptr)
|
||||||
|
{
|
||||||
|
std::string_view id = *ptr.getCellRef().getRefIdPtr();
|
||||||
|
return id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker";
|
||||||
|
}
|
||||||
|
|
||||||
std::string_view getMWClassName(const MWWorld::Ptr& ptr)
|
std::string_view getMWClassName(const MWWorld::Ptr& ptr)
|
||||||
{
|
{
|
||||||
if (*ptr.getCellRef().getRefIdPtr() == "player")
|
if (*ptr.getCellRef().getRefIdPtr() == "player")
|
||||||
return "Player";
|
return "Player";
|
||||||
else
|
if (isMarker(ptr))
|
||||||
return getMWClassName(typeid(ptr.getClass()), ptr.getTypeName());
|
return "Marker";
|
||||||
|
return getMWClassName(typeid(ptr.getClass()), ptr.getTypeName());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ptrToString(const MWWorld::Ptr& ptr)
|
std::string ptrToString(const MWWorld::Ptr& ptr)
|
||||||
|
|
|
@ -18,6 +18,7 @@ namespace MWLua
|
||||||
inline const ObjectId& getId(const MWWorld::Ptr& ptr) { return ptr.getCellRef().getRefNum(); }
|
inline const ObjectId& getId(const MWWorld::Ptr& ptr) { return ptr.getCellRef().getRefNum(); }
|
||||||
std::string idToString(const ObjectId& id);
|
std::string idToString(const ObjectId& id);
|
||||||
std::string ptrToString(const MWWorld::Ptr& ptr);
|
std::string ptrToString(const MWWorld::Ptr& ptr);
|
||||||
|
bool isMarker(const MWWorld::Ptr& ptr);
|
||||||
std::string_view getMWClassName(const std::type_index& cls_type, std::string_view fallback = "Unknown");
|
std::string_view getMWClassName(const std::type_index& cls_type, std::string_view fallback = "Unknown");
|
||||||
std::string_view getMWClassName(const MWWorld::Ptr& ptr);
|
std::string_view getMWClassName(const MWWorld::Ptr& ptr);
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,9 @@ namespace sol
|
||||||
namespace MWLua
|
namespace MWLua
|
||||||
{
|
{
|
||||||
|
|
||||||
|
template <typename ObjT>
|
||||||
|
using Cell = std::conditional_t<std::is_same_v<ObjT, LObject>, LCell, GCell>;
|
||||||
|
|
||||||
template <class Class>
|
template <class Class>
|
||||||
static const MWWorld::Ptr& requireClass(const MWWorld::Ptr& ptr)
|
static const MWWorld::Ptr& requireClass(const MWWorld::Ptr& ptr)
|
||||||
{
|
{
|
||||||
|
@ -95,10 +98,13 @@ namespace MWLua
|
||||||
{
|
{
|
||||||
return o.ptr().getCellRef().getRefId();
|
return o.ptr().getCellRef().getRefId();
|
||||||
});
|
});
|
||||||
objectT["cell"] = sol::readonly_property([](const ObjectT& o)
|
objectT["cell"] = sol::readonly_property([](const ObjectT& o) -> sol::optional<Cell<ObjectT>>
|
||||||
{
|
{
|
||||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
const MWWorld::Ptr& ptr = o.ptr();
|
||||||
return world->getCellName(o.ptr().getCell());
|
if (ptr.isInCell())
|
||||||
|
return Cell<ObjectT>{ptr.getCell()};
|
||||||
|
else
|
||||||
|
return {};
|
||||||
});
|
});
|
||||||
objectT["position"] = sol::readonly_property([](const ObjectT& o) -> osg::Vec3f
|
objectT["position"] = sol::readonly_property([](const ObjectT& o) -> osg::Vec3f
|
||||||
{
|
{
|
||||||
|
@ -117,6 +123,22 @@ namespace MWLua
|
||||||
context.mLocalEventQueue->push_back({dest.id(), std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer)});
|
context.mLocalEventQueue->push_back({dest.id(), std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer)});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
objectT["canMove"] = [context](const ObjectT& o)
|
||||||
|
{
|
||||||
|
const MWWorld::Class& cls = o.ptr().getClass();
|
||||||
|
return cls.getMaxSpeed(o.ptr()) > 0;
|
||||||
|
};
|
||||||
|
objectT["getRunSpeed"] = [context](const ObjectT& o)
|
||||||
|
{
|
||||||
|
const MWWorld::Class& cls = o.ptr().getClass();
|
||||||
|
return cls.getRunSpeed(o.ptr());
|
||||||
|
};
|
||||||
|
objectT["getWalkSpeed"] = [context](const ObjectT& o)
|
||||||
|
{
|
||||||
|
const MWWorld::Class& cls = o.ptr().getClass();
|
||||||
|
return cls.getWalkSpeed(o.ptr());
|
||||||
|
};
|
||||||
|
|
||||||
if constexpr (std::is_same_v<ObjectT, GObject>)
|
if constexpr (std::is_same_v<ObjectT, GObject>)
|
||||||
{ // Only for global scripts
|
{ // Only for global scripts
|
||||||
objectT["addScript"] = [luaManager=context.mLuaManager](const GObject& object, const std::string& path)
|
objectT["addScript"] = [luaManager=context.mLuaManager](const GObject& object, const std::string& path)
|
||||||
|
@ -155,9 +177,17 @@ namespace MWLua
|
||||||
{
|
{
|
||||||
return ptr(o).getCellRef().getDoorDest().asRotationVec3();
|
return ptr(o).getCellRef().getDoorDest().asRotationVec3();
|
||||||
});
|
});
|
||||||
objectT["destCell"] = sol::readonly_property([ptr](const ObjectT& o) -> std::string_view
|
objectT["destCell"] = sol::readonly_property(
|
||||||
|
[ptr, worldView=context.mWorldView](const ObjectT& o) -> sol::optional<Cell<ObjectT>>
|
||||||
{
|
{
|
||||||
return ptr(o).getCellRef().getDestCell();
|
const MWWorld::CellRef& cellRef = ptr(o).getCellRef();
|
||||||
|
if (!cellRef.getTeleport())
|
||||||
|
return {};
|
||||||
|
MWWorld::CellStore* cell = worldView->findCell(cellRef.getDestCell(), cellRef.getDoorDest().asVec3());
|
||||||
|
if (cell)
|
||||||
|
return Cell<ObjectT>{cell};
|
||||||
|
else
|
||||||
|
return {};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <components/lua/luastate.hpp>
|
#include <components/lua/luastate.hpp>
|
||||||
|
|
||||||
#include "../mwclass/container.hpp"
|
#include "../mwclass/container.hpp"
|
||||||
|
#include "../mwworld/cellstore.hpp"
|
||||||
|
|
||||||
#include "worldview.hpp"
|
#include "worldview.hpp"
|
||||||
|
|
||||||
|
@ -24,11 +25,16 @@ namespace MWLua
|
||||||
static std::array objectFields = {
|
static std::array objectFields = {
|
||||||
Queries::Field({"type"}, typeid(std::string)),
|
Queries::Field({"type"}, typeid(std::string)),
|
||||||
Queries::Field({"recordId"}, 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)),
|
Queries::Field({"count"}, typeid(int32_t)),
|
||||||
};
|
};
|
||||||
static std::array doorFields = {
|
static std::array doorFields = {
|
||||||
Queries::Field({"isTeleport"}, typeid(bool)),
|
Queries::Field({"isTeleport"}, typeid(bool)),
|
||||||
Queries::Field({"destCell"}, typeid(std::string)),
|
Queries::Field({"destCell", "name"}, typeid(std::string)),
|
||||||
|
Queries::Field({"destCell", "region"}, typeid(std::string)),
|
||||||
|
Queries::Field({"destCell", "isExterior"}, typeid(bool)),
|
||||||
};
|
};
|
||||||
return std::vector<QueryFieldGroup>{
|
return std::vector<QueryFieldGroup>{
|
||||||
createGroup("OBJECT", objectFields),
|
createGroup("OBJECT", objectFields),
|
||||||
|
@ -42,13 +48,8 @@ namespace MWLua
|
||||||
return fieldGroups;
|
return fieldGroups;
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectIdList selectObjectsFromList(const Queries::Query& query, const ObjectIdList& list, const Context& context)
|
bool checkQueryConditions(const Queries::Query& query, const ObjectId& id, 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>>();
|
|
||||||
std::vector<char> condStack;
|
|
||||||
auto compareFn = [](auto&& a, auto&& b, Queries::Condition::Type t)
|
auto compareFn = [](auto&& a, auto&& b, Queries::Condition::Type t)
|
||||||
{
|
{
|
||||||
switch (t)
|
switch (t)
|
||||||
|
@ -63,88 +64,125 @@ namespace MWLua
|
||||||
throw std::runtime_error("Unsupported condition type");
|
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 stupid, but "prisonmarker" has class "Door" despite that it is only an invisible marker. So we ignore all markers.
|
||||||
|
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)
|
for (const ObjectId& id : *list)
|
||||||
{
|
{
|
||||||
if (static_cast<int64_t>(res->size()) == query.mLimit)
|
if (static_cast<int64_t>(res->size()) == query.mLimit)
|
||||||
break;
|
break;
|
||||||
sol::object obj;
|
if (checkQueryConditions(query, id, context))
|
||||||
MWWorld::Ptr ptr;
|
|
||||||
if (context.mIsGlobal)
|
|
||||||
{
|
|
||||||
GObject g(id, context.mWorldView->getObjectRegistry());
|
|
||||||
if (!g.isValid()) continue;
|
|
||||||
ptr = g.ptr();
|
|
||||||
obj = sol::make_object(context.mLua->sol(), g);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LObject l(id, context.mWorldView->getObjectRegistry());
|
|
||||||
if (!l.isValid()) continue;
|
|
||||||
ptr = l.ptr();
|
|
||||||
obj = sol::make_object(context.mLua->sol(), l);
|
|
||||||
}
|
|
||||||
const MWWorld::Class& cls = ptr.getClass();
|
|
||||||
if (cls.isActivator() && query.mQueryType != ObjectQueryTypes::ACTIVATORS)
|
|
||||||
continue;
|
|
||||||
if (cls.isActor() && query.mQueryType != ObjectQueryTypes::ACTORS)
|
|
||||||
continue;
|
|
||||||
if (cls.isDoor() && query.mQueryType != ObjectQueryTypes::DOORS)
|
|
||||||
continue;
|
|
||||||
if (typeid(cls) == typeid(MWClass::Container) && query.mQueryType != ObjectQueryTypes::CONTAINERS)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
condStack.clear();
|
|
||||||
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 = sol::table(fieldObj)[field];
|
|
||||||
bool c;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (condStack.empty() || condStack.back() != 0)
|
|
||||||
res->push_back(id);
|
res->push_back(id);
|
||||||
}
|
}
|
||||||
return res;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ namespace MWLua
|
||||||
// TODO: Implement custom fields. QueryFieldGroup registerCustomFields(...);
|
// TODO: Implement custom fields. QueryFieldGroup registerCustomFields(...);
|
||||||
|
|
||||||
ObjectIdList selectObjectsFromList(const Queries::Query& query, const ObjectIdList& list, const Context&);
|
ObjectIdList selectObjectsFromList(const Queries::Query& query, const ObjectIdList& list, const Context&);
|
||||||
|
ObjectIdList selectObjectsFromCellStore(const Queries::Query& query, MWWorld::CellStore* store, const Context&);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include <components/esm/esmreader.hpp>
|
#include <components/esm/esmreader.hpp>
|
||||||
#include <components/esm/esmwriter.hpp>
|
#include <components/esm/esmwriter.hpp>
|
||||||
|
#include <components/esm/loadcell.hpp>
|
||||||
|
|
||||||
#include "../mwclass/container.hpp"
|
#include "../mwclass/container.hpp"
|
||||||
|
|
||||||
|
@ -33,6 +34,10 @@ namespace MWLua
|
||||||
|
|
||||||
WorldView::ObjectGroup* WorldView::chooseGroup(const MWWorld::Ptr& ptr)
|
WorldView::ObjectGroup* WorldView::chooseGroup(const MWWorld::Ptr& ptr)
|
||||||
{
|
{
|
||||||
|
// It is important to check `isMarker` first.
|
||||||
|
// For example "prisonmarker" has class "Door" despite that it is only an invisible marker.
|
||||||
|
if (isMarker(ptr))
|
||||||
|
return nullptr;
|
||||||
const MWWorld::Class& cls = ptr.getClass();
|
const MWWorld::Class& cls = ptr.getClass();
|
||||||
if (cls.isActivator())
|
if (cls.isActivator())
|
||||||
return &mActivatorsInScene;
|
return &mActivatorsInScene;
|
||||||
|
@ -113,4 +118,35 @@ namespace MWLua
|
||||||
group.mChanged = true;
|
group.mChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: If Lua scripts will use several threads at the same time, then `find*Cell` functions should have critical sections.
|
||||||
|
MWWorld::CellStore* WorldView::findCell(const std::string& name, osg::Vec3f position)
|
||||||
|
{
|
||||||
|
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||||
|
bool exterior = name.empty() || world->getExterior(name);
|
||||||
|
if (exterior)
|
||||||
|
{
|
||||||
|
int cellX, cellY;
|
||||||
|
world->positionToIndex(position.x(), position.y(), cellX, cellY);
|
||||||
|
return world->getExterior(cellX, cellY);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return world->getInterior(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
MWWorld::CellStore* WorldView::findNamedCell(const std::string& name)
|
||||||
|
{
|
||||||
|
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||||
|
const ESM::Cell* esmCell = world->getExterior(name);
|
||||||
|
if (esmCell)
|
||||||
|
return world->getExterior(esmCell->getGridX(), esmCell->getGridY());
|
||||||
|
else
|
||||||
|
return world->getInterior(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
MWWorld::CellStore* WorldView::findExteriorCell(int x, int y)
|
||||||
|
{
|
||||||
|
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||||
|
return world->getExterior(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,10 @@ namespace MWLua
|
||||||
// If onlyActive = true, then search only among the objects that are currently in the scene.
|
// If onlyActive = true, then search only among the objects that are currently in the scene.
|
||||||
// TODO: ObjectIdList selectObjects(const Queries::Query& query, bool onlyActive);
|
// TODO: ObjectIdList selectObjects(const Queries::Query& query, bool onlyActive);
|
||||||
|
|
||||||
|
MWWorld::CellStore* findCell(const std::string& name, osg::Vec3f position);
|
||||||
|
MWWorld::CellStore* findNamedCell(const std::string& name);
|
||||||
|
MWWorld::CellStore* findExteriorCell(int x, int y);
|
||||||
|
|
||||||
void load(ESM::ESMReader& esm);
|
void load(ESM::ESMReader& esm);
|
||||||
void save(ESM::ESMWriter& esm) const;
|
void save(ESM::ESMWriter& esm) const;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue