Refactor teleporting in Lua; fix a bug in worldmodel.cpp

7344-support-launching-the-example-suite
Petr Mikheev 2 years ago
parent a82b7cb872
commit 60a8d08e66

@ -528,6 +528,11 @@ namespace MWLua
mActionQueue.push_back(std::make_unique<FunctionAction>(&mLua, std::move(action), name));
}
void LuaManager::addTeleportPlayerAction(std::function<void()> action)
{
mTeleportPlayerAction = std::make_unique<FunctionAction>(&mLua, std::move(action), "TeleportPlayer");
}
void LuaManager::reportStats(unsigned int frameNumber, osg::Stats& stats) const
{
stats.setAttribute(frameNumber, "Lua UsedMemory", mLua.getTotalMemoryUsage());

@ -106,7 +106,7 @@ namespace MWLua
void addAction(std::function<void()> action, std::string_view name = "");
void addAction(std::unique_ptr<Action>&& action) { mActionQueue.push_back(std::move(action)); }
void addTeleportPlayerAction(std::unique_ptr<Action>&& action) { mTeleportPlayerAction = std::move(action); }
void addTeleportPlayerAction(std::function<void()> action);
// Saving
void write(ESM::ESMWriter& writer, Loading::Listener& progress) override;

@ -48,59 +48,48 @@ namespace MWLua
namespace
{
class TeleportAction final : public LuaManager::Action
MWWorld::CellStore* findCell(const sol::object& cellOrName, const osg::Vec3f& pos)
{
public:
TeleportAction(LuaUtil::LuaState* state, ObjectId object, std::string_view cell, const osg::Vec3f& pos,
const osg::Vec3f& rot)
: Action(state)
, mObject(object)
, mCell(std::string(cell))
, mPos(pos)
, mRot(rot)
{
}
void apply() const override
MWWorld::WorldModel* wm = MWBase::Environment::get().getWorldModel();
MWWorld::CellStore* cell;
if (cellOrName.is<GCell>())
cell = cellOrName.as<const GCell&>().mStore;
else
{
MWWorld::WorldModel& wm = *MWBase::Environment::get().getWorldModel();
MWWorld::CellStore* cell = wm.getCellByPosition(mPos, mCell);
MWBase::World* world = MWBase::Environment::get().getWorld();
MWWorld::Ptr obj = wm.getPtr(mObject);
const MWWorld::Class& cls = obj.getClass();
bool isPlayer = obj == world->getPlayerPtr();
if (cls.isActor())
cls.getCreatureStats(obj).land(isPlayer);
if (isPlayer)
{
ESM::Position esmPos;
static_assert(sizeof(esmPos) == sizeof(osg::Vec3f) * 2);
std::memcpy(esmPos.pos, &mPos, sizeof(osg::Vec3f));
std::memcpy(esmPos.rot, &mRot, sizeof(osg::Vec3f));
world->getPlayer().setTeleported(true);
if (cell->isExterior())
world->changeToExteriorCell(esmPos, true);
else
world->changeToInteriorCell(mCell, esmPos, true);
}
std::string_view name = cellOrName.as<std::string_view>();
if (name.empty())
cell = nullptr; // default exterior worldspace
else
{
MWWorld::Ptr newObj = world->moveObject(obj, cell, mPos);
world->rotateObject(newObj, mRot);
if (!newObj.getRefData().isEnabled())
world->enable(newObj);
}
cell = wm->getCell(name);
}
return wm->getCellByPosition(pos, cell);
}
std::string toString() const override { return "TeleportAction"; }
void teleportPlayer(MWWorld::CellStore* destCell, const osg::Vec3f& pos, const osg::Vec3f& rot)
{
MWBase::World* world = MWBase::Environment::get().getWorld();
ESM::Position esmPos;
static_assert(sizeof(esmPos) == sizeof(osg::Vec3f) * 2);
std::memcpy(esmPos.pos, &pos, sizeof(osg::Vec3f));
std::memcpy(esmPos.rot, &rot, sizeof(osg::Vec3f));
MWWorld::Ptr ptr = world->getPlayerPtr();
ptr.getClass().getCreatureStats(ptr).land(false);
world->getPlayer().setTeleported(true);
world->changeToCell(destCell->getCell()->getId(), esmPos, true);
}
private:
ObjectId mObject;
std::string mCell;
osg::Vec3f mPos;
osg::Vec3f mRot;
};
void teleportNotPlayer(
const MWWorld::Ptr& ptr, MWWorld::CellStore* destCell, const osg::Vec3f& pos, const osg::Vec3f& rot)
{
MWBase::World* world = MWBase::Environment::get().getWorld();
const MWWorld::Class& cls = ptr.getClass();
if (cls.isActor())
cls.getCreatureStats(ptr).land(false);
MWWorld::Ptr newPtr = world->moveObject(ptr, destCell, pos);
world->rotateObject(newPtr, rot);
if (!newPtr.getRefData().isEnabled())
world->enable(newPtr);
}
class ActivateAction final : public LuaManager::Action
{
@ -323,8 +312,9 @@ namespace MWLua
refData.setCount(0);
});
};
objectT["teleport"] = [removeFn, context](const GObject& object, std::string_view cell,
objectT["teleport"] = [removeFn, context](const GObject& object, const sol::object& cellOrName,
const osg::Vec3f& pos, const sol::optional<osg::Vec3f>& optRot) {
MWWorld::CellStore* cell = findCell(cellOrName, pos);
MWWorld::Ptr ptr = object.ptr();
if (ptr.getRefData().isDeleted())
throw std::runtime_error("Object is removed");
@ -332,18 +322,19 @@ namespace MWLua
{
// Currently moving to or from containers makes a copy and removes the original.
// TODO(#6148): actually move rather than copy and preserve RefNum
auto* cellStore = MWBase::Environment::get().getWorldModel()->getCellByPosition(pos, cell);
MWWorld::Ptr newPtr = ptr.getClass().copyToCell(ptr, *cellStore, ptr.getRefData().getCount());
MWWorld::Ptr newPtr = ptr.getClass().copyToCell(ptr, *cell, ptr.getRefData().getCount());
newPtr.getRefData().disable();
removeFn(object, ptr.getRefData().getCount());
ptr = newPtr;
}
osg::Vec3f rot = optRot ? *optRot : ptr.getRefData().getPosition().asRotationVec3();
auto action = std::make_unique<TeleportAction>(context.mLua, getId(ptr), cell, pos, rot);
if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr())
context.mLuaManager->addTeleportPlayerAction(std::move(action));
context.mLuaManager->addTeleportPlayerAction(
[cell, pos, rot] { teleportPlayer(cell, pos, rot); });
else
context.mLuaManager->addAction(std::move(action));
context.mLuaManager->addAction(
[object, cell, pos, rot] { teleportNotPlayer(object.ptr(), cell, pos, rot); },
"TeleportAction");
};
}
}

@ -195,35 +195,36 @@ MWWorld::CellStore* MWWorld::WorldModel::getExterior(int x, int y)
return result->second;
}
MWWorld::CellStore* MWWorld::WorldModel::getInterior(std::string_view name)
MWWorld::CellStore* MWWorld::WorldModel::getInteriorOrNull(std::string_view name)
{
auto result = mInteriors.find(name);
if (result == mInteriors.end())
{
const ESM4::Cell* cell4 = mStore.get<ESM4::Cell>().searchCellName(name);
CellStore* newCellStore = nullptr;
if (!cell4)
{
const ESM::Cell* cell = mStore.get<ESM::Cell>().find(name);
if (const ESM::Cell* cell = mStore.get<ESM::Cell>().search(name))
newCellStore = &mCells.emplace(cell->mId, CellStore(MWWorld::Cell(*cell), mStore, mReaders)).first->second;
}
else
{
else if (const ESM4::Cell* cell4 = mStore.get<ESM4::Cell>().searchCellName(name))
newCellStore
= &mCells.emplace(cell4->mId, CellStore(MWWorld::Cell(*cell4), mStore, mReaders)).first->second;
}
if (!newCellStore)
return nullptr; // Cell not found
result = mInteriors.emplace(name, newCellStore).first;
}
if (result->second->getState() != CellStore::State_Loaded)
{
result->second->load();
}
return result->second;
}
MWWorld::CellStore* MWWorld::WorldModel::getInterior(std::string_view name)
{
CellStore* res = getInteriorOrNull(name);
if (res == nullptr)
throw std::runtime_error("Interior not found: '" + std::string(name) + "'");
return res;
}
MWWorld::CellStore* MWWorld::WorldModel::getCell(const ESM::RefId& id)
{
auto result = mCells.find(id);
@ -261,11 +262,14 @@ MWWorld::CellStore* MWWorld::WorldModel::getCell(const ESM::RefId& id)
return newCellStore;
}
const ESM::Cell* MWWorld::WorldModel::getESMCellByName(std::string_view name)
MWWorld::CellStore* MWWorld::WorldModel::getCell(std::string_view name)
{
const ESM::Cell* cell = mStore.get<ESM::Cell>().search(name); // first try interiors
if (!cell) // try named exteriors
cell = mStore.get<ESM::Cell>().searchExtByName(name);
if (CellStore* res = getInteriorOrNull(name)) // first try interiors
return res;
// try named exteriors
const ESM::Cell* cell = mStore.get<ESM::Cell>().searchExtByName(name);
if (!cell)
{
// treat "Wilderness" like an empty string
@ -288,39 +292,17 @@ const ESM::Cell* MWWorld::WorldModel::getESMCellByName(std::string_view name)
}
if (!cell)
throw std::runtime_error(std::string("Can't find cell with name ") + std::string(name));
return cell;
}
ESM::CellVariant MWWorld::WorldModel::getCellByName(std::string_view name)
{
const ESM::Cell* cellEsm3 = getESMCellByName(name);
if (!cellEsm3)
{
const ESM4::Cell* cellESM4 = mStore.get<ESM4::Cell>().searchCellName(name);
return ESM::CellVariant(*cellESM4);
}
return ESM::CellVariant(*cellEsm3);
}
MWWorld::CellStore* MWWorld::WorldModel::getCell(std::string_view name)
{
const ESM::Cell* cell = getESMCellByName(name);
if (cell->isExterior())
return getExterior(cell->getGridX(), cell->getGridY());
else
return getInterior(name);
return getExterior(cell->getGridX(), cell->getGridY());
}
MWWorld::CellStore* MWWorld::WorldModel::getCellByPosition(
const osg::Vec3f& pos, std::string_view cellNameInSameWorldSpace)
const osg::Vec3f& pos, MWWorld::CellStore* cellInSameWorldSpace)
{
if (cellNameInSameWorldSpace.empty() || getESMCellByName(cellNameInSameWorldSpace)->isExterior())
{
const osg::Vec2i cellIndex = positionToCellIndex(pos.x(), pos.y());
return getExterior(cellIndex.x(), cellIndex.y());
}
else
return getInterior(cellNameInSameWorldSpace);
if (cellInSameWorldSpace && !cellInSameWorldSpace->isExterior())
return cellInSameWorldSpace;
const osg::Vec2i cellIndex = positionToCellIndex(pos.x(), pos.y());
return getExterior(cellIndex.x(), cellIndex.y());
}
MWWorld::Ptr MWWorld::WorldModel::getPtr(const ESM::RefId& name, CellStore& cell)

@ -49,10 +49,8 @@ namespace MWWorld
std::size_t mPtrIndexUpdateCounter = 0;
ESM::RefNum mLastGeneratedRefnum;
const ESM::Cell* getESMCellByName(std::string_view name);
ESM::CellVariant getCellByName(std::string_view name);
CellStore* getCellStore(const ESM::Cell* cell);
CellStore* getInteriorOrNull(std::string_view name);
Ptr getPtrAndCache(const ESM::RefId& name, CellStore& cellStore);
void writeCell(ESM::ESMWriter& writer, CellStore& cell) const;
@ -70,10 +68,11 @@ namespace MWWorld
CellStore* getCell(std::string_view name); // interior or named exterior
CellStore* getCell(const ESM::RefId& Id);
// If cellNameInSameWorldSpace is an interior - returns this interior.
// Otherwise returns exterior cell for given position in the same world space.
// At the moment multiple world spaces are not supported, so all exteriors are in one world space.
CellStore* getCellByPosition(const osg::Vec3f& pos, std::string_view cellNameInSameWorldSpace);
// Returns the cell that is in the same worldspace as `cellInSameWorldSpace`
// (in case of nullptr - default exterior worldspace) and contains given position.
// Interiors are single-cell worldspaces, so in case of an interior it just returns
// the same cell.
CellStore* getCellByPosition(const osg::Vec3f& pos, CellStore* cellInSameWorldSpace = nullptr);
void registerPtr(const MWWorld::Ptr& ptr);
void deregisterPtr(const MWWorld::Ptr& ptr);

@ -170,9 +170,10 @@
-- Can be used to move objects from an inventory or a container to the world.
-- @function [parent=#GameObject] teleport
-- @param self
-- @param #string cellName Name of the cell to teleport into. For exteriors can be empty.
-- @param openmw.util#Vector3 position New position
-- @param openmw.util#Vector3 rotation New rotation. Optional argument. If missing, then the current rotation is used.
-- @param #any cellOrName A cell to define the destination worldspace; can be either #Cell, or cell name, or an empty string (empty string means the default exterior worldspace).
-- If the worldspace has multiple cells (i.e. an exterior), the destination cell is calculated using `position`.
-- @param openmw.util#Vector3 position New position.
-- @param openmw.util#Vector3 rotation (optional) New rotation. If missing, then the current rotation is used.
---
-- Moves object into a container or an inventory. Enables if was disabled.

Loading…
Cancel
Save