mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-30 07:45:39 +00:00
Refactor teleporting in Lua; fix a bug in worldmodel.cpp
This commit is contained in:
parent
a82b7cb872
commit
60a8d08e66
6 changed files with 86 additions and 108 deletions
|
@ -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)
|
||||
MWWorld::WorldModel* wm = MWBase::Environment::get().getWorldModel();
|
||||
MWWorld::CellStore* cell;
|
||||
if (cellOrName.is<GCell>())
|
||||
cell = cellOrName.as<const GCell&>().mStore;
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
void apply() const override
|
||||
{
|
||||
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…
Reference in a new issue