1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-06-20 09:41:33 +00:00

Merge branch 'load-ESM4-Cell' into 'master'

Can load and coc into an interrior oblivion cell

See merge request OpenMW/openmw!2647
This commit is contained in:
psi29a 2023-02-06 08:56:23 +00:00
commit 4032c447e9
58 changed files with 1084 additions and 271 deletions

View file

@ -78,7 +78,7 @@ add_openmw_dir (mwworld
actionequip timestamp actionalchemy cellstore actionapply actioneat actionequip timestamp actionalchemy cellstore actionapply actioneat
store esmstore fallback actionrepair actionsoulgem livecellref actiondoor store esmstore fallback actionrepair actionsoulgem livecellref actiondoor
contentloader esmloader actiontrap cellreflist cellref weather projectilemanager contentloader esmloader actiontrap cellreflist cellref weather projectilemanager
cellpreloader datetimemanager groundcoverstore magiceffects cellpreloader datetimemanager groundcoverstore magiceffects cell
) )
add_openmw_dir (mwphysics add_openmw_dir (mwphysics

View file

@ -92,6 +92,7 @@ namespace MWWorld
class TimeStamp; class TimeStamp;
class ESMStore; class ESMStore;
class RefData; class RefData;
class Cell;
typedef std::vector<std::pair<MWWorld::Ptr, MWMechanics::Movement>> PtrMovementList; typedef std::vector<std::pair<MWWorld::Ptr, MWMechanics::Movement>> PtrMovementList;
} }
@ -179,6 +180,8 @@ namespace MWBase
/// ///
/// \note If cell==0, the cell the player is currently in will be used instead to /// \note If cell==0, the cell the player is currently in will be used instead to
/// generate a name. /// generate a name.
virtual std::string_view getCellName(const MWWorld::Cell& cell) const = 0;
virtual std::string_view getCellName(const ESM::Cell* cell) const = 0; virtual std::string_view getCellName(const ESM::Cell* cell) const = 0;
virtual void removeRefScript(MWWorld::RefData* ref) = 0; virtual void removeRefScript(MWWorld::RefData* ref) = 0;

View file

@ -47,5 +47,7 @@ namespace MWClass
Repair::registerSelf(); Repair::registerSelf();
Static::registerSelf(); Static::registerSelf();
BodyPart::registerSelf(); BodyPart::registerSelf();
ESM4Static::registerSelf();
} }
} }

View file

@ -1,6 +1,7 @@
#include "static.hpp" #include "static.hpp"
#include <components/esm3/loadstat.hpp> #include <components/esm3/loadstat.hpp>
#include <components/esm4/loadstat.hpp>
#include <components/sceneutil/positionattitudetransform.hpp> #include <components/sceneutil/positionattitudetransform.hpp>
#include "../mwphysics/physicssystem.hpp" #include "../mwphysics/physicssystem.hpp"
@ -63,4 +64,53 @@ namespace MWClass
return MWWorld::Ptr(cell.insert(ref), &cell); return MWWorld::Ptr(cell.insert(ref), &cell);
} }
ESM4Static::ESM4Static()
: MWWorld::RegisteredClass<ESM4Static>(ESM4::Static::sRecordId)
{
}
void ESM4Static ::insertObjectRendering(
const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const
{
if (!model.empty())
{
renderingInterface.getObjects().insertModel(ptr, model);
ptr.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Static);
}
}
void ESM4Static::insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation,
MWPhysics::PhysicsSystem& physics) const
{
insertObjectPhysics(ptr, model, rotation, physics);
}
void ESM4Static::insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation,
MWPhysics::PhysicsSystem& physics) const
{
physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_World);
}
std::string ESM4Static::getModel(const MWWorld::ConstPtr& ptr) const
{
return getClassModel<ESM4::Static>(ptr);
}
std::string_view ESM4Static ::getName(const MWWorld::ConstPtr& ptr) const
{
return {};
}
bool ESM4Static::hasToolTip(const MWWorld::ConstPtr& ptr) const
{
return false;
}
MWWorld::Ptr ESM4Static::copyToCellImpl(const MWWorld::ConstPtr& ptr, MWWorld::CellStore& cell) const
{
const MWWorld::LiveCellRef<ESM4::Static>* ref = ptr.get<ESM4::Static>();
return MWWorld::Ptr(cell.insert(ref), &cell);
}
} }

View file

@ -31,6 +31,33 @@ namespace MWClass
std::string getModel(const MWWorld::ConstPtr& ptr) const override; std::string getModel(const MWWorld::ConstPtr& ptr) const override;
}; };
class ESM4Static : public MWWorld::RegisteredClass<ESM4Static>
{
friend MWWorld::RegisteredClass<ESM4Static>;
ESM4Static();
MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr& ptr, MWWorld::CellStore& cell) const override;
public:
void insertObjectRendering(const MWWorld::Ptr& ptr, const std::string& model,
MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation,
MWPhysics::PhysicsSystem& physics) const override;
void insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation,
MWPhysics::PhysicsSystem& physics) const override;
std::string_view getName(const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string.
bool hasToolTip(const MWWorld::ConstPtr& ptr) const override;
///< @return true if this object has a tooltip when focused (default implementation: true)
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
};
} }
#endif #endif

View file

@ -705,7 +705,7 @@ namespace MWGui
ESM::Position markedPosition; ESM::Position markedPosition;
MWBase::Environment::get().getWorld()->getPlayer().getMarkedPosition(markedCell, markedPosition); MWBase::Environment::get().getWorld()->getPlayer().getMarkedPosition(markedCell, markedPosition);
if (markedCell && markedCell->isExterior() == !mInterior if (markedCell && markedCell->isExterior() == !mInterior
&& (!mInterior || Misc::StringUtils::ciEqual(markedCell->getCell()->mName, mPrefix))) && (!mInterior || Misc::StringUtils::ciEqual(markedCell->getCell()->getNameId(), mPrefix)))
{ {
MarkerUserData markerPos(mLocalMapRender); MarkerUserData markerPos(mLocalMapRender);
MyGUI::ImageBox* markerWidget = mLocalMap->createWidget<MyGUI::ImageBox>("ImageBox", MyGUI::ImageBox* markerWidget = mLocalMap->createWidget<MyGUI::ImageBox>("ImageBox",

View file

@ -200,7 +200,7 @@ namespace MWGui
MWWorld::Ptr player = world->getPlayerPtr(); MWWorld::Ptr player = world->getPlayerPtr();
if (mSleeping && player.getCell()->isExterior()) if (mSleeping && player.getCell()->isExterior())
{ {
const ESM::RefId& regionstr = player.getCell()->getCell()->mRegion; const ESM::RefId& regionstr = player.getCell()->getCell()->getRegion();
if (!regionstr.empty()) if (!regionstr.empty())
{ {
const ESM::Region* region = world->getStore().get<ESM::Region>().find(regionstr); const ESM::Region* region = world->getStore().get<ESM::Region>().find(regionstr);

View file

@ -954,20 +954,21 @@ namespace MWGui
mMap->setCellName(name); mMap->setCellName(name);
mHud->setCellName(name); mHud->setCellName(name);
auto cellCommon = cell->getCell();
if (cell->getCell()->isExterior()) if (cellCommon->isExterior())
{ {
if (!cell->getCell()->mName.empty()) if (!cellCommon->getNameId().empty())
mMap->addVisitedLocation(name, cell->getCell()->getGridX(), cell->getCell()->getGridY()); mMap->addVisitedLocation(name, cellCommon->getGridX(), cellCommon->getGridY());
mMap->cellExplored(cell->getCell()->getGridX(), cell->getCell()->getGridY()); mMap->cellExplored(cellCommon->getGridX(), cellCommon->getGridY());
setActiveMap(cell->getCell()->getGridX(), cell->getCell()->getGridY(), false); setActiveMap(cellCommon->getGridX(), cellCommon->getGridY(), false);
} }
else else
{ {
mMap->setCellPrefix(cell->getCell()->mName); mMap->setCellPrefix(std::string(cellCommon->getNameId()));
mHud->setCellPrefix(cell->getCell()->mName); mHud->setCellPrefix(std::string(cellCommon->getNameId()));
osg::Vec3f worldPos; osg::Vec3f worldPos;
if (!MWBase::Environment::get().getWorld()->findInteriorPositionInWorldSpace(cell, worldPos)) if (!MWBase::Environment::get().getWorld()->findInteriorPositionInWorldSpace(cell, worldPos))

View file

@ -1,5 +1,6 @@
#include "luabindings.hpp" #include "luabindings.hpp"
#include <components/esm/esmbridge.hpp>
#include <components/esm/records.hpp> #include <components/esm/records.hpp>
#include "../mwworld/cellstore.hpp" #include "../mwworld/cellstore.hpp"
@ -29,35 +30,35 @@ namespace MWLua
cellT[sol::meta_function::equal_to] = [](const CellT& a, const CellT& b) { return a.mStore == b.mStore; }; 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) { cellT[sol::meta_function::to_string] = [](const CellT& c) {
const ESM::Cell* cell = c.mStore->getCell(); auto cell = c.mStore->getCell();
std::stringstream res; std::stringstream res;
if (cell->isExterior()) if (cell->isExterior())
res << "exterior(" << cell->getGridX() << ", " << cell->getGridY() << ")"; res << "exterior(" << cell->getGridX() << ", " << cell->getGridY() << ")";
else else
res << "interior(" << cell->mName << ")"; res << "interior(" << cell->getNameId() << ")";
return res.str(); return res.str();
}; };
cellT["name"] = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->mName; }); cellT["name"] = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->getNameId(); });
cellT["region"] cellT["region"]
= sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->mRegion.getRefIdString(); }); = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->getRegion().getRefIdString(); });
cellT["gridX"] = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->getGridX(); }); 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["gridY"] = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->getGridY(); });
cellT["hasWater"] = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->hasWater(); }); cellT["hasWater"] = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->hasWater(); });
cellT["hasSky"] = sol::readonly_property([](const CellT& c) { cellT["hasSky"] = sol::readonly_property([](const CellT& c) {
return c.mStore->getCell()->isExterior() || (c.mStore->getCell()->mData.mFlags & ESM::Cell::QuasiEx) != 0; return c.mStore->getCell()->isExterior() || (c.mStore->getCell()->isQuasiExterior()) != 0;
}); });
cellT["isExterior"] = sol::readonly_property([](const CellT& c) { return c.mStore->isExterior(); }); cellT["isExterior"] = sol::readonly_property([](const CellT& c) { return c.mStore->isExterior(); });
// deprecated, use cell:hasTag("QuasiExterior") instead // deprecated, use cell:hasTag("QuasiExterior") instead
cellT["isQuasiExterior"] = sol::readonly_property( cellT["isQuasiExterior"]
[](const CellT& c) { return (c.mStore->getCell()->mData.mFlags & ESM::Cell::QuasiEx) != 0; }); = sol::readonly_property([](const CellT& c) { return (c.mStore->getCell()->isQuasiExterior()) != 0; });
cellT["hasTag"] = [](const CellT& c, std::string_view tag) -> bool { cellT["hasTag"] = [](const CellT& c, std::string_view tag) -> bool {
if (tag == "NoSleep") if (tag == "NoSleep")
return (c.mStore->getCell()->mData.mFlags & ESM::Cell::NoSleep) != 0; return (c.mStore->getCell()->noSleep()) != 0;
else if (tag == "QuasiExterior") else if (tag == "QuasiExterior")
return (c.mStore->getCell()->mData.mFlags & ESM::Cell::QuasiEx) != 0; return (c.mStore->getCell()->isQuasiExterior()) != 0;
return false; return false;
}; };

View file

@ -176,12 +176,12 @@ namespace MWLua
// TODO: change AiEscort implementation to accept ptr instead of a non-unique refId. // TODO: change AiEscort implementation to accept ptr instead of a non-unique refId.
const ESM::RefId& refId = target.ptr().getCellRef().getRefId(); const ESM::RefId& refId = target.ptr().getCellRef().getRefId();
int gameHoursDuration = static_cast<int>(std::ceil(duration / 3600.0)); int gameHoursDuration = static_cast<int>(std::ceil(duration / 3600.0));
const ESM::Cell* esmCell = cell.mStore->getCell(); auto* esmCell = cell.mStore->getCell();
if (esmCell->isExterior()) if (esmCell->isExterior())
ai.stack(MWMechanics::AiEscort(refId, gameHoursDuration, dest.x(), dest.y(), dest.z(), false), ptr); ai.stack(MWMechanics::AiEscort(refId, gameHoursDuration, dest.x(), dest.y(), dest.z(), false), ptr);
else else
ai.stack(MWMechanics::AiEscort( ai.stack(MWMechanics::AiEscort(
refId, esmCell->mName, gameHoursDuration, dest.x(), dest.y(), dest.z(), false), refId, esmCell->getNameId(), gameHoursDuration, dest.x(), dest.y(), dest.z(), false),
ptr); ptr);
}; };
selfAPI["_startAiWander"] = [](SelfObject& self, int distance, float duration) { selfAPI["_startAiWander"] = [](SelfObject& self, int distance, float duration) {

View file

@ -274,8 +274,8 @@ namespace MWMechanics
.find("fInteriorHeadTrackMult") .find("fInteriorHeadTrackMult")
->mValue.getFloat(); ->mValue.getFloat();
float maxDistance = fMaxHeadTrackDistance; float maxDistance = fMaxHeadTrackDistance;
const ESM::Cell* currentCell = actor.getCell()->getCell(); auto currentCell = actor.getCell()->getCell();
if (!currentCell->isExterior() && !(currentCell->mData.mFlags & ESM::Cell::QuasiEx)) if (!currentCell->isExterior() && !(currentCell->isQuasiExterior()))
maxDistance *= fInteriorHeadTrackMult; maxDistance *= fInteriorHeadTrackMult;
const osg::Vec3f actor1Pos(actorRefData.getPosition().asVec3()); const osg::Vec3f actor1Pos(actorRefData.getPosition().asVec3());

View file

@ -357,12 +357,11 @@ namespace MWMechanics
case AiCombatStorage::FleeState_Idle: case AiCombatStorage::FleeState_Idle:
{ {
float triggerDist = getMaxAttackDistance(target); float triggerDist = getMaxAttackDistance(target);
const MWWorld::Cell* cellVariant = storage.mCell->getCell();
if (storage.mLOS && (triggerDist >= 1000 || getDistanceMinusHalfExtents(actor, target) <= triggerDist)) if (storage.mLOS && (triggerDist >= 1000 || getDistanceMinusHalfExtents(actor, target) <= triggerDist))
{ {
const ESM::Pathgrid* pathgrid const ESM::Pathgrid* pathgrid
= MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search( = MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*cellVariant);
*storage.mCell->getCell());
bool runFallback = true; bool runFallback = true;
@ -370,7 +369,7 @@ namespace MWMechanics
&& !actor.getClass().isPureWaterCreature(actor)) && !actor.getClass().isPureWaterCreature(actor))
{ {
ESM::Pathgrid::PointList points; ESM::Pathgrid::PointList points;
Misc::CoordinateConverter coords(storage.mCell->getCell()); Misc::CoordinateConverter coords(*storage.mCell->getCell());
osg::Vec3f localPos = actor.getRefData().getPosition().asVec3(); osg::Vec3f localPos = actor.getRefData().getPosition().asVec3();
coords.toLocal(localPos); coords.toLocal(localPos);

View file

@ -174,7 +174,8 @@ namespace MWMechanics
return true; return true;
} }
} }
else if (Misc::StringUtils::ciEqual(mCellId, actor.getCell()->getCell()->mName)) // Cell to travel to else if (Misc::StringUtils::ciEqual(
mCellId, actor.getCell()->getCell()->getNameId())) // Cell to travel to
{ {
mRemainingDuration = mDuration; mRemainingDuration = mDuration;
return true; return true;

View file

@ -411,11 +411,11 @@ bool MWMechanics::AiPackage::doesPathNeedRecalc(const osg::Vec3f& newDest, const
bool MWMechanics::AiPackage::isNearInactiveCell(osg::Vec3f position) bool MWMechanics::AiPackage::isNearInactiveCell(osg::Vec3f position)
{ {
const ESM::Cell* playerCell(getPlayer().getCell()->getCell()); const MWWorld::Cell* playerCell = getPlayer().getCell()->getCell();
if (playerCell->isExterior()) if (playerCell->isExterior())
{ {
// get actor's distance from origin of center cell // get actor's distance from origin of center cell
Misc::CoordinateConverter(playerCell).toLocal(position); Misc::CoordinateConverter(*playerCell).toLocal(position);
// currently assumes 3 x 3 grid for exterior cells, with player at center cell. // currently assumes 3 x 3 grid for exterior cells, with player at center cell.
// AI shuts down actors before they reach edges of 3 x 3 grid. // AI shuts down actors before they reach edges of 3 x 3 grid.

View file

@ -730,7 +730,7 @@ namespace MWMechanics
auto& prng = MWBase::Environment::get().getWorld()->getPrng(); auto& prng = MWBase::Environment::get().getWorld()->getPrng();
int index = Misc::Rng::rollDice(storage.mAllowedNodes.size(), prng); int index = Misc::Rng::rollDice(storage.mAllowedNodes.size(), prng);
ESM::Pathgrid::Point worldDest = storage.mAllowedNodes[index]; ESM::Pathgrid::Point worldDest = storage.mAllowedNodes[index];
auto converter = Misc::CoordinateConverter(actor.getCell()->getCell()); auto converter = Misc::CoordinateConverter(*actor.getCell()->getCell());
ESM::Pathgrid::Point dest = converter.toLocalPoint(worldDest); ESM::Pathgrid::Point dest = converter.toLocalPoint(worldDest);
bool isPathGridOccupied = MWBase::Environment::get().getMechanicsManager()->isAnyActorInRange( bool isPathGridOccupied = MWBase::Environment::get().getMechanicsManager()->isAnyActorInRange(
@ -811,7 +811,7 @@ namespace MWMechanics
getPathGridGraph(currentCell).getNeighbouringPoints(index, points); getPathGridGraph(currentCell).getNeighbouringPoints(index, points);
} }
void AiWander::getAllowedNodes(const MWWorld::Ptr& actor, const ESM::Cell* cell, AiWanderStorage& storage) void AiWander::getAllowedNodes(const MWWorld::Ptr& actor, const MWWorld::Cell* cell, AiWanderStorage& storage)
{ {
// infrequently used, therefore no benefit in caching it as a member // infrequently used, therefore no benefit in caching it as a member
const ESM::Pathgrid* pathgrid const ESM::Pathgrid* pathgrid
@ -835,7 +835,7 @@ namespace MWMechanics
if (mDistance && storage.mCanWanderAlongPathGrid && !actor.getClass().isPureWaterCreature(actor)) if (mDistance && storage.mCanWanderAlongPathGrid && !actor.getClass().isPureWaterCreature(actor))
{ {
// get NPC's position in local (i.e. cell) coordinates // get NPC's position in local (i.e. cell) coordinates
auto converter = Misc::CoordinateConverter(cell); auto converter = Misc::CoordinateConverter(*cell);
const osg::Vec3f npcPos = converter.toLocalVec3(mInitialActorPosition); const osg::Vec3f npcPos = converter.toLocalVec3(mInitialActorPosition);
// Find closest pathgrid point // Find closest pathgrid point

View file

@ -23,6 +23,10 @@ namespace Misc
class CoordinateConverter; class CoordinateConverter;
} }
namespace MWWorld
{
class Cell;
}
namespace MWMechanics namespace MWMechanics
{ {
/// \brief This class holds the variables AiWander needs which are deleted if the package becomes inactive. /// \brief This class holds the variables AiWander needs which are deleted if the package becomes inactive.
@ -147,7 +151,7 @@ namespace MWMechanics
void getNeighbouringNodes( void getNeighbouringNodes(
ESM::Pathgrid::Point dest, const MWWorld::CellStore* currentCell, ESM::Pathgrid::PointList& points); ESM::Pathgrid::Point dest, const MWWorld::CellStore* currentCell, ESM::Pathgrid::PointList& points);
void getAllowedNodes(const MWWorld::Ptr& actor, const ESM::Cell* cell, AiWanderStorage& storage); void getAllowedNodes(const MWWorld::Ptr& actor, const MWWorld::Cell* cell, AiWanderStorage& storage);
void trimAllowedNodes(std::vector<ESM::Pathgrid::Point>& nodes, const PathFinder& pathfinder); void trimAllowedNodes(std::vector<ESM::Pathgrid::Point>& nodes, const PathFinder& pathfinder);

View file

@ -196,7 +196,7 @@ namespace MWMechanics
return; return;
// NOTE: getClosestPoint expects local coordinates // NOTE: getClosestPoint expects local coordinates
Misc::CoordinateConverter converter(mCell->getCell()); Misc::CoordinateConverter converter(*mCell->getCell());
// NOTE: It is possible that getClosestPoint returns a pathgrind point index // NOTE: It is possible that getClosestPoint returns a pathgrind point index
// that is unreachable in some situations. e.g. actor is standing // that is unreachable in some situations. e.g. actor is standing

View file

@ -105,7 +105,8 @@ namespace MWMechanics
return true; return true;
mCell = cell->getCell(); mCell = cell->getCell();
mPathgrid = MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*cell->getCell());
mPathgrid = MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*mCell);
if (!mPathgrid) if (!mPathgrid)
return false; return false;

View file

@ -13,6 +13,7 @@ namespace ESM
namespace MWWorld namespace MWWorld
{ {
class CellStore; class CellStore;
class Cell;
} }
namespace MWMechanics namespace MWMechanics
@ -41,7 +42,7 @@ namespace MWMechanics
std::deque<ESM::Pathgrid::Point> aStarSearch(const int start, const int end) const; std::deque<ESM::Pathgrid::Point> aStarSearch(const int start, const int end) const;
private: private:
const ESM::Cell* mCell; const MWWorld::Cell* mCell;
const ESM::Pathgrid* mPathgrid; const ESM::Pathgrid* mPathgrid;
struct ConnectedPoint // edge struct ConnectedPoint // edge

View file

@ -489,7 +489,7 @@ namespace MWMechanics
{ {
std::string_view dest; std::string_view dest;
if (!markedCell->isExterior()) if (!markedCell->isExterior())
dest = markedCell->getCell()->mName; dest = markedCell->getCell()->getNameId();
MWWorld::ActionTeleport action(dest, markedPosition, false); MWWorld::ActionTeleport action(dest, markedPosition, false);
action.execute(target); action.execute(target);
if (!caster.isEmpty()) if (!caster.isEmpty())

View file

@ -2,11 +2,15 @@
#include <algorithm> #include <algorithm>
#include <components/esm/esmbridge.hpp>
#include <components/esm3/loadcell.hpp> #include <components/esm3/loadcell.hpp>
#include <components/esm4/loadcell.hpp>
#include <components/fallback/fallback.hpp> #include <components/fallback/fallback.hpp>
#include <components/sceneutil/util.hpp> #include <components/sceneutil/util.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <apps/openmw/mwworld/cell.hpp>
namespace namespace
{ {
float DLLandFogStart; float DLLandFogStart;
@ -38,13 +42,14 @@ namespace MWRender
DLInteriorFogEnd = Settings::Manager::getFloat("distant interior fog end", "Fog"); DLInteriorFogEnd = Settings::Manager::getFloat("distant interior fog end", "Fog");
} }
void FogManager::configure(float viewDistance, const ESM::Cell* cell) void FogManager::configure(float viewDistance, const MWWorld::Cell& cell)
{ {
osg::Vec4f color = SceneUtil::colourFromRGB(cell->mAmbi.mFog); osg::Vec4f color = SceneUtil::colourFromRGB(cell.getMood().mFogColor);
const float fogDensity = cell.getMood().mFogDensity;
if (mDistantFog) if (mDistantFog)
{ {
float density = std::max(0.2f, cell->mAmbi.mFogDensity); float density = std::max(0.2f, fogDensity);
mLandFogStart = DLInteriorFogEnd * (1.0f - density) + DLInteriorFogStart * density; mLandFogStart = DLInteriorFogEnd * (1.0f - density) + DLInteriorFogStart * density;
mLandFogEnd = DLInteriorFogEnd; mLandFogEnd = DLInteriorFogEnd;
mUnderwaterFogStart = DLUnderwaterFogStart; mUnderwaterFogStart = DLUnderwaterFogStart;
@ -52,7 +57,7 @@ namespace MWRender
mFogColor = color; mFogColor = color;
} }
else else
configure(viewDistance, cell->mAmbi.mFogDensity, mUnderwaterIndoorFog, 1.0f, 0.0f, color); configure(viewDistance, fogDensity, mUnderwaterIndoorFog, 1.0f, 0.0f, color);
} }
void FogManager::configure(float viewDistance, float fogDepth, float underwaterFog, float dlFactor, float dlOffset, void FogManager::configure(float viewDistance, float fogDepth, float underwaterFog, float dlFactor, float dlOffset,

View file

@ -3,9 +3,9 @@
#include <osg/Vec4f> #include <osg/Vec4f>
namespace ESM namespace MWWorld
{ {
struct Cell; class Cell;
} }
namespace MWRender namespace MWRender
@ -15,7 +15,7 @@ namespace MWRender
public: public:
FogManager(); FogManager();
void configure(float viewDistance, const ESM::Cell* cell); void configure(float viewDistance, const MWWorld::Cell& cell);
void configure(float viewDistance, float fogDepth, float underwaterFog, float dlFactor, float dlOffset, void configure(float viewDistance, float fogDepth, float underwaterFog, float dlFactor, float dlOffset,
const osg::Vec4f& color); const osg::Vec4f& color);

View file

@ -102,12 +102,13 @@ namespace MWRender
void Pathgrid::enableCellPathgrid(const MWWorld::CellStore* store) void Pathgrid::enableCellPathgrid(const MWWorld::CellStore* store)
{ {
MWBase::World* world = MWBase::Environment::get().getWorld(); MWBase::World* world = MWBase::Environment::get().getWorld();
const ESM::Pathgrid* pathgrid = world->getStore().get<ESM::Pathgrid>().search(*store->getCell()); const ESM::Pathgrid* pathgrid = world->getStore().get<ESM::Pathgrid>().search(*store->getCell());
if (!pathgrid) if (!pathgrid)
return; return;
osg::Vec3f cellPathGridPos(0, 0, 0); osg::Vec3f cellPathGridPos(0, 0, 0);
Misc::CoordinateConverter(store->getCell()).toWorld(cellPathGridPos); Misc::CoordinateConverter(*store->getCell()).toWorld(cellPathGridPos);
osg::ref_ptr<osg::PositionAttitudeTransform> cellPathGrid = new osg::PositionAttitudeTransform; osg::ref_ptr<osg::PositionAttitudeTransform> cellPathGrid = new osg::PositionAttitudeTransform;
cellPathGrid->setPosition(cellPathGridPos); cellPathGrid->setPosition(cellPathGridPos);

View file

@ -49,6 +49,7 @@
#include <components/terrain/terraingrid.hpp> #include <components/terrain/terraingrid.hpp>
#include <components/esm3/loadcell.hpp> #include <components/esm3/loadcell.hpp>
#include <components/esm4/loadcell.hpp>
#include <components/debug/debugdraw.hpp> #include <components/debug/debugdraw.hpp>
#include <components/detournavigator/navigator.hpp> #include <components/detournavigator/navigator.hpp>
@ -720,14 +721,14 @@ namespace MWRender
mSky->setMoonColour(red); mSky->setMoonColour(red);
} }
void RenderingManager::configureAmbient(const ESM::Cell* cell) void RenderingManager::configureAmbient(const MWWorld::Cell& cell)
{ {
bool isInterior = !cell->isExterior() && !(cell->mData.mFlags & ESM::Cell::QuasiEx); bool isInterior = !cell.isExterior() && !cell.isQuasiExterior();
bool needsAdjusting = false; bool needsAdjusting = false;
if (mResourceSystem->getSceneManager()->getLightingMethod() != SceneUtil::LightingMethod::FFP) if (mResourceSystem->getSceneManager()->getLightingMethod() != SceneUtil::LightingMethod::FFP)
needsAdjusting = isInterior; needsAdjusting = isInterior;
auto ambient = SceneUtil::colourFromRGB(cell->mAmbi.mAmbient); osg::Vec4f ambient = SceneUtil::colourFromRGB(cell.getMood().mAmbiantColor);
if (needsAdjusting) if (needsAdjusting)
{ {
@ -751,7 +752,8 @@ namespace MWRender
setAmbientColour(ambient); setAmbientColour(ambient);
osg::Vec4f diffuse = SceneUtil::colourFromRGB(cell->mAmbi.mSunlight); osg::Vec4f diffuse = SceneUtil::colourFromRGB(cell.getMood().mDirectionalColor);
setSunColour(diffuse, diffuse, 1.f); setSunColour(diffuse, diffuse, 1.f);
const osg::Vec4f interiorSunPos = osg::Vec4f(-0.15f, 0.15f, 1.f, 0.f); const osg::Vec4f interiorSunPos = osg::Vec4f(-0.15f, 0.15f, 1.f, 0.f);
@ -871,7 +873,7 @@ namespace MWRender
return false; return false;
} }
void RenderingManager::configureFog(const ESM::Cell* cell) void RenderingManager::configureFog(const MWWorld::Cell& cell)
{ {
mFog->configure(mViewDistance, cell); mFog->configure(mViewDistance, cell);
} }
@ -1405,7 +1407,7 @@ namespace MWRender
mMinimumAmbientLuminance mMinimumAmbientLuminance
= std::clamp(Settings::Manager::getFloat("minimum interior brightness", "Shaders"), 0.f, 1.f); = std::clamp(Settings::Manager::getFloat("minimum interior brightness", "Shaders"), 0.f, 1.f);
if (MWMechanics::getPlayer().isInCell()) if (MWMechanics::getPlayer().isInCell())
configureAmbient(MWMechanics::getPlayer().getCell()->getCell()); configureAmbient(*MWMechanics::getPlayer().getCell()->getCell());
} }
else if (it->first == "Shaders" else if (it->first == "Shaders"
&& (it->second == "light bounds multiplier" || it->second == "maximum light distance" && (it->second == "light bounds multiplier" || it->second == "maximum light distance"

View file

@ -73,6 +73,7 @@ namespace DetourNavigator
namespace MWWorld namespace MWWorld
{ {
class GroundcoverStore; class GroundcoverStore;
class Cell;
} }
namespace Debug namespace Debug
@ -140,8 +141,8 @@ namespace MWRender
void setSunColour(const osg::Vec4f& diffuse, const osg::Vec4f& specular, float sunVis); void setSunColour(const osg::Vec4f& diffuse, const osg::Vec4f& specular, float sunVis);
void setNight(bool isNight) { mNight = isNight; } void setNight(bool isNight) { mNight = isNight; }
void configureAmbient(const ESM::Cell* cell); void configureAmbient(const MWWorld::Cell& cell);
void configureFog(const ESM::Cell* cell); void configureFog(const MWWorld::Cell& cell);
void configureFog( void configureFog(
float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f& colour); float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f& colour);

View file

@ -744,7 +744,8 @@ namespace MWRender
bool wasInterior = mInterior; bool wasInterior = mInterior;
if (!isInterior) if (!isInterior)
{ {
mWaterNode->setPosition(getSceneNodeCoordinates(store->getCell()->mData.mX, store->getCell()->mData.mY)); mWaterNode->setPosition(
getSceneNodeCoordinates(store->getCell()->getGridX(), store->getCell()->getGridY()));
mInterior = false; mInterior = false;
} }
else else

View file

@ -766,14 +766,14 @@ namespace MWSound
{ {
MWBase::World* world = MWBase::Environment::get().getWorld(); MWBase::World* world = MWBase::Environment::get().getWorld();
const MWWorld::ConstPtr player = world->getPlayerPtr(); const MWWorld::ConstPtr player = world->getPlayerPtr();
const ESM::Cell* cell = player.getCell()->getCell(); auto cell = player.getCell()->getCell();
if (!cell->isExterior()) if (!cell->isExterior())
return; return;
if (mCurrentRegionSound && mOutput->isSoundPlaying(mCurrentRegionSound)) if (mCurrentRegionSound && mOutput->isSoundPlaying(mCurrentRegionSound))
return; return;
if (const auto next = mRegionSoundSelector.getNextRandom(duration, cell->mRegion, *world)) if (const auto next = mRegionSoundSelector.getNextRandom(duration, cell->getRegion(), *world))
mCurrentRegionSound = playSound(*next, 1.0f, 1.0f); mCurrentRegionSound = playSound(*next, 1.0f, 1.0f);
} }
@ -781,7 +781,8 @@ namespace MWSound
{ {
MWBase::World* world = MWBase::Environment::get().getWorld(); MWBase::World* world = MWBase::Environment::get().getWorld();
const MWWorld::ConstPtr player = world->getPlayerPtr(); const MWWorld::ConstPtr player = world->getPlayerPtr();
const ESM::Cell* curcell = player.getCell()->getCell();
const MWWorld::Cell* curcell = player.getCell()->getCell();
const auto update = mWaterSoundUpdater.update(player, *world); const auto update = mWaterSoundUpdater.update(player, *world);
WaterSoundAction action; WaterSoundAction action;
@ -810,7 +811,7 @@ namespace MWSound
} }
std::pair<SoundManager::WaterSoundAction, Sound_Buffer*> SoundManager::getWaterSoundAction( std::pair<SoundManager::WaterSoundAction, Sound_Buffer*> SoundManager::getWaterSoundAction(
const WaterSoundUpdate& update, const ESM::Cell* cell) const const WaterSoundUpdate& update, const MWWorld::Cell* cell) const
{ {
if (mNearWaterSound) if (mNearWaterSound)
{ {

View file

@ -30,6 +30,11 @@ namespace ESM
struct Cell; struct Cell;
} }
namespace MWWorld
{
class Cell;
}
namespace MWSound namespace MWSound
{ {
class Sound_Output; class Sound_Output;
@ -107,7 +112,7 @@ namespace MWSound
float mTimePassed; float mTimePassed;
const ESM::Cell* mLastCell; const MWWorld::Cell* mLastCell;
Sound* mCurrentRegionSound; Sound* mCurrentRegionSound;
@ -143,7 +148,7 @@ namespace MWSound
}; };
std::pair<WaterSoundAction, Sound_Buffer*> getWaterSoundAction( std::pair<WaterSoundAction, Sound_Buffer*> getWaterSoundAction(
const WaterSoundUpdate& update, const ESM::Cell* cell) const; const WaterSoundUpdate& update, const MWWorld::Cell* cell) const;
SoundManager(const SoundManager& rhs); SoundManager(const SoundManager& rhs);
SoundManager& operator=(const SoundManager& rhs); SoundManager& operator=(const SoundManager& rhs);

View file

@ -0,0 +1,61 @@
#include "cell.hpp"
#include <components/esm3/loadcell.hpp>
#include <components/esm4/loadcell.hpp>
#include <components/misc/algorithm.hpp>
namespace MWWorld
{
Cell::Cell(const ESM4::Cell& cell)
: ESM::CellVariant(cell)
, mIsExterior(!(cell.mCellFlags & ESM4::CELL_Interior))
, mIsQuasiExterior(cell.mCellFlags & ESM4::CELL_QuasiExt)
, mHasWater(cell.mCellFlags & ESM4::CELL_HasWater)
, mNoSleep(false) // No such notion in ESM4
, mGridPos(cell.mX, cell.mY)
, mDisplayname(cell.mFullName)
, mNameID(cell.mEditorId)
, mRegion(ESM::RefId::sEmpty) // Unimplemented for now
, mCellId{
.mWorldspace{ Misc::StringUtils::lowerCase(cell.mEditorId) },
.mIndex{ cell.getGridX(), cell.getGridY() },
.mPaged = isExterior(),}
,mMood{
.mAmbiantColor = cell.mLighting.ambient,
.mDirectionalColor = cell.mLighting.directional,
.mFogColor = cell.mLighting.fogColor,
.mFogDensity = cell.mLighting.fogPower,}
,mWaterHeight(cell.mWaterHeight)
{
}
Cell::Cell(const ESM::Cell& cell)
: ESM::CellVariant(cell)
, mIsExterior(!(cell.mData.mFlags & ESM::Cell::Interior))
, mIsQuasiExterior(cell.mData.mFlags & ESM::Cell::QuasiEx)
, mHasWater(cell.mData.mFlags & ESM::Cell::HasWater)
, mNoSleep(cell.mData.mFlags & ESM::Cell::NoSleep)
, mGridPos(cell.getGridX(), cell.getGridY())
, mDisplayname(cell.mName)
, mNameID(cell.mName)
, mRegion(ESM::RefId::sEmpty) // Unimplemented for now
, mCellId(cell.getCellId())
, mMood{
.mAmbiantColor = cell.mAmbi.mAmbient,
.mDirectionalColor = cell.mAmbi.mSunlight,
.mFogColor = cell.mAmbi.mFog,
.mFogDensity = cell.mAmbi.mFogDensity,
}
,mWaterHeight(cell.mWater)
{
}
std::string Cell::getDescription() const
{
return ESM::visit(ESM::VisitOverload{
[&](const ESM::Cell& cell) { return cell.getDescription(); },
[&](const ESM4::Cell& cell) { return cell.mEditorId; },
},
*this);
}
}

View file

@ -0,0 +1,70 @@
#ifndef OPENW_MWORLD_CELL
#define OPENW_MWORLD_CELL
#include <osg/Vec2i>
#include <components/esm/esmbridge.hpp>
#include <components/esm/refid.hpp>
#include <components/esm3/cellid.hpp>
namespace ESM
{
struct Cell;
struct CellId;
}
namespace ESM4
{
struct Cell;
}
namespace MWWorld
{
class CellStore;
class Cell : public ESM::CellVariant
{
struct MoodData
{
uint32_t mAmbiantColor;
uint32_t mDirectionalColor;
uint32_t mFogColor;
float mFogDensity;
};
public:
explicit Cell(const ESM4::Cell& cell);
explicit Cell(const ESM::Cell& cell);
int getGridX() const { return mGridPos.x(); }
int getGridY() const { return mGridPos.y(); }
bool isExterior() const { return mIsExterior; }
bool isQuasiExterior() const { return mIsQuasiExterior; }
bool hasWater() const { return mHasWater; }
bool noSleep() const { return mNoSleep; }
const ESM::CellId& getCellId() const { return mCellId; }
const ESM::RefId& getRegion() const { return mRegion; }
std::string_view getNameId() const { return mNameID; }
std::string_view getDisplayName() const { return mDisplayname; }
std::string getDescription() const;
const MoodData& getMood() const { return mMood; }
float getWaterHeight() const { return mWaterHeight; }
private:
bool mIsExterior;
bool mIsQuasiExterior;
bool mHasWater;
bool mNoSleep;
osg::Vec2i mGridPos;
std::string mDisplayname; // How the game displays it
std::string mNameID; // The name that will be used by the script and console commands
ESM::RefId mRegion;
ESM::CellId mCellId;
MoodData mMood;
float mWaterHeight;
};
}
#endif

View file

@ -7,45 +7,97 @@
namespace MWWorld namespace MWWorld
{ {
CellRef::CellRef(const ESM::CellRef& ref)
: mCellRef(ESM::ReferenceVariant(ref))
{
}
CellRef::CellRef(const ESM4::Reference& ref)
: mCellRef(ESM::ReferenceVariant(ref))
{
}
static const ESM::RefNum emptyRefNum = {};
const ESM::RefNum& CellRef::getRefNum() const
{
return std::visit(ESM::VisitOverload{
[&](const ESM4::Reference& /*ref*/) -> const ESM::RefNum& { return emptyRefNum; },
[&](const ESM::CellRef& ref) -> const ESM::RefNum& { return ref.mRefNum; },
},
mCellRef.mVariant);
}
const ESM::RefNum& CellRef::getOrAssignRefNum(ESM::RefNum& lastAssignedRefNum) const ESM::RefNum& CellRef::getOrAssignRefNum(ESM::RefNum& lastAssignedRefNum)
{ {
if (!mCellRef.mRefNum.isSet()) auto esm3Visit = [&](ESM::CellRef& ref) -> const ESM::RefNum& {
{ if (!ref.mRefNum.isSet())
// Generated RefNums have negative mContentFile
assert(lastAssignedRefNum.mContentFile < 0);
lastAssignedRefNum.mIndex++;
if (lastAssignedRefNum.mIndex == 0) // mIndex overflow, so mContentFile should be changed
{ {
if (lastAssignedRefNum.mContentFile > std::numeric_limits<int32_t>::min()) // Generated RefNums have negative mContentFile
lastAssignedRefNum.mContentFile--; assert(lastAssignedRefNum.mContentFile < 0);
else lastAssignedRefNum.mIndex++;
Log(Debug::Error) << "RefNum counter overflow in CellRef::getOrAssignRefNum"; if (lastAssignedRefNum.mIndex == 0) // mIndex overflow, so mContentFile should be changed
{
if (lastAssignedRefNum.mContentFile > std::numeric_limits<int32_t>::min())
lastAssignedRefNum.mContentFile--;
else
Log(Debug::Error) << "RefNum counter overflow in CellRef::getOrAssignRefNum";
}
ref.mRefNum = lastAssignedRefNum;
mChanged = true;
} }
mCellRef.mRefNum = lastAssignedRefNum; return ref.mRefNum;
mChanged = true; };
} return std::visit(
return mCellRef.mRefNum; ESM::VisitOverload{
[&](ESM4::Reference& /*ref*/) -> const ESM::RefNum& { return emptyRefNum; },
esm3Visit,
},
mCellRef.mVariant);
} }
void CellRef::unsetRefNum() void CellRef::unsetRefNum()
{ {
mCellRef.mRefNum = ESM::RefNum{}; std::visit(ESM::VisitOverload{
[&](ESM4::Reference& /*ref*/) {},
[&](ESM::CellRef& ref) { ref.mRefNum = emptyRefNum; },
},
mCellRef.mVariant);
}
static const std::string emptyString = "";
const std::string& CellRef::getDestCell() const
{
return std::visit(ESM::VisitOverload{
[&](const ESM4::Reference& /*ref*/) -> const std::string& { return emptyString; },
[&](const ESM::CellRef& ref) -> const std::string& { return ref.mDestCell; },
},
mCellRef.mVariant);
} }
void CellRef::setScale(float scale) void CellRef::setScale(float scale)
{ {
if (scale != mCellRef.mScale) if (scale != getScale())
{ {
mChanged = true; mChanged = true;
mCellRef.mScale = scale; std::visit([scale](auto&& ref) { ref.mScale = scale; }, mCellRef.mVariant);
} }
} }
void CellRef::setPosition(const ESM::Position& position) void CellRef::setPosition(const ESM::Position& position)
{ {
mChanged = true; mChanged = true;
mCellRef.mPos = position; std::visit([&position](auto&& ref) { ref.mPos = position; }, mCellRef.mVariant);
}
float CellRef::getEnchantmentCharge() const
{
return std::visit(ESM::VisitOverload{
[&](const ESM4::Reference& /*ref*/) { return 0.f; },
[&](const ESM::CellRef& ref) { return ref.mEnchantmentCharge; },
},
mCellRef.mVariant);
} }
float CellRef::getNormalizedEnchantmentCharge(int maxCharge) const float CellRef::getNormalizedEnchantmentCharge(int maxCharge) const
@ -54,112 +106,149 @@ namespace MWWorld
{ {
return 0; return 0;
} }
else if (mCellRef.mEnchantmentCharge == -1) else if (getEnchantmentCharge() == -1)
{ {
return 1; return 1;
} }
else else
{ {
return mCellRef.mEnchantmentCharge / static_cast<float>(maxCharge); return getEnchantmentCharge() / static_cast<float>(maxCharge);
} }
} }
void CellRef::setEnchantmentCharge(float charge) void CellRef::setEnchantmentCharge(float charge)
{ {
if (charge != mCellRef.mEnchantmentCharge) if (charge != getEnchantmentCharge())
{ {
mChanged = true; mChanged = true;
mCellRef.mEnchantmentCharge = charge;
std::visit(ESM::VisitOverload{
[&](ESM4::Reference& /*ref*/) {},
[&](ESM::CellRef& ref) { ref.mEnchantmentCharge = charge; },
},
mCellRef.mVariant);
} }
} }
void CellRef::setCharge(int charge) void CellRef::setCharge(int charge)
{ {
if (charge != mCellRef.mChargeInt) std::visit(ESM::VisitOverload{
{ [&](ESM4::Reference& /*ref*/) {},
mChanged = true; [&](ESM::CellRef& ref) { ref.mChargeInt = charge; },
mCellRef.mChargeInt = charge; },
} mCellRef.mVariant);
} }
void CellRef::applyChargeRemainderToBeSubtracted(float chargeRemainder) void CellRef::applyChargeRemainderToBeSubtracted(float chargeRemainder)
{ {
mCellRef.mChargeIntRemainder += std::abs(chargeRemainder); auto esm3Visit = [&](ESM::CellRef& cellRef3) {
if (mCellRef.mChargeIntRemainder > 1.0f) cellRef3.mChargeIntRemainder += std::abs(chargeRemainder);
{ if (cellRef3.mChargeIntRemainder > 1.0f)
float newChargeRemainder = (mCellRef.mChargeIntRemainder - std::floor(mCellRef.mChargeIntRemainder));
if (mCellRef.mChargeInt <= static_cast<int>(mCellRef.mChargeIntRemainder))
{ {
mCellRef.mChargeInt = 0; float newChargeRemainder = (cellRef3.mChargeIntRemainder - std::floor(cellRef3.mChargeIntRemainder));
if (cellRef3.mChargeInt <= static_cast<int>(cellRef3.mChargeIntRemainder))
{
cellRef3.mChargeInt = 0;
}
else
{
cellRef3.mChargeInt -= static_cast<int>(cellRef3.mChargeIntRemainder);
}
cellRef3.mChargeIntRemainder = newChargeRemainder;
} }
else };
{ std::visit(
mCellRef.mChargeInt -= static_cast<int>(mCellRef.mChargeIntRemainder); ESM::VisitOverload{
} [&](ESM4::Reference& /*ref*/) {},
mCellRef.mChargeIntRemainder = newChargeRemainder; esm3Visit,
} },
mCellRef.mVariant);
} }
void CellRef::setChargeFloat(float charge) void CellRef::setChargeFloat(float charge)
{ {
if (charge != mCellRef.mChargeFloat) std::visit(ESM::VisitOverload{
{ [&](ESM4::Reference& /*ref*/) {},
mChanged = true; [&](ESM::CellRef& ref) { ref.mChargeFloat = charge; },
mCellRef.mChargeFloat = charge; },
} mCellRef.mVariant);
}
const std::string& CellRef::getGlobalVariable() const
{
return std::visit(ESM::VisitOverload{
[&](const ESM4::Reference& /*ref*/) -> const std::string& { return emptyString; },
[&](const ESM::CellRef& ref) -> const std::string& { return ref.mGlobalVariable; },
},
mCellRef.mVariant);
} }
void CellRef::resetGlobalVariable() void CellRef::resetGlobalVariable()
{ {
if (!mCellRef.mGlobalVariable.empty()) if (!getGlobalVariable().empty())
{ {
mChanged = true; mChanged = true;
mCellRef.mGlobalVariable.erase(); std::visit(ESM::VisitOverload{
[&](ESM4::Reference& /*ref*/) {},
[&](ESM::CellRef& ref) { ref.mGlobalVariable.erase(); },
},
mCellRef.mVariant);
} }
} }
void CellRef::setFactionRank(int factionRank) void CellRef::setFactionRank(int factionRank)
{ {
if (factionRank != mCellRef.mFactionRank) if (factionRank != getFactionRank())
{ {
mChanged = true; mChanged = true;
mCellRef.mFactionRank = factionRank; std::visit([&](auto&& ref) { ref.mFactionRank = factionRank; }, mCellRef.mVariant);
} }
} }
void CellRef::setOwner(const ESM::RefId& owner) void CellRef::setOwner(const ESM::RefId& owner)
{ {
if (owner != mCellRef.mOwner) if (owner != getOwner())
{ {
mChanged = true; std::visit(ESM::VisitOverload{
mCellRef.mOwner = owner; [&](ESM4::Reference& /*ref*/) {},
[&](ESM::CellRef& ref) { ref.mOwner = owner; },
},
mCellRef.mVariant);
} }
} }
void CellRef::setSoul(const ESM::RefId& soul) void CellRef::setSoul(const ESM::RefId& soul)
{ {
if (soul != mCellRef.mSoul) if (soul != getSoul())
{ {
mChanged = true; mChanged = true;
mCellRef.mSoul = soul; std::visit(ESM::VisitOverload{
[&](ESM4::Reference& /*ref*/) {},
[&](ESM::CellRef& ref) { ref.mSoul = soul; },
},
mCellRef.mVariant);
} }
} }
void CellRef::setFaction(const ESM::RefId& faction) void CellRef::setFaction(const ESM::RefId& faction)
{ {
if (faction != mCellRef.mFaction) if (faction != getFaction())
{ {
mChanged = true; mChanged = true;
mCellRef.mFaction = faction; std::visit(ESM::VisitOverload{
[&](ESM4::Reference& /*ref*/) {},
[&](ESM::CellRef& ref) { ref.mFaction = faction; },
},
mCellRef.mVariant);
} }
} }
void CellRef::setLockLevel(int lockLevel) void CellRef::setLockLevel(int lockLevel)
{ {
if (lockLevel != mCellRef.mLockLevel) if (lockLevel != getLockLevel())
{ {
mChanged = true; mChanged = true;
mCellRef.mLockLevel = lockLevel; std::visit([&](auto&& ref) { ref.mLockLevel = lockLevel; }, mCellRef.mVariant);
} }
} }
@ -173,30 +262,41 @@ namespace MWWorld
void CellRef::unlock() void CellRef::unlock()
{ {
setLockLevel(-abs(mCellRef.mLockLevel)); // Makes lockLevel negative setLockLevel(-abs(getLockLevel())); // Makes lockLevel negative
} }
void CellRef::setTrap(const ESM::RefId& trap) void CellRef::setTrap(const ESM::RefId& trap)
{ {
if (trap != mCellRef.mTrap) if (trap != getTrap())
{ {
mChanged = true; mChanged = true;
mCellRef.mTrap = trap; std::visit(ESM::VisitOverload{
[&](ESM4::Reference& /*ref*/) {},
[&](ESM::CellRef& ref) { ref.mTrap = trap; },
},
mCellRef.mVariant);
} }
} }
void CellRef::setGoldValue(int value) void CellRef::setGoldValue(int value)
{ {
if (value != mCellRef.mGoldValue) if (value != getGoldValue())
{ {
mChanged = true; mChanged = true;
mCellRef.mGoldValue = value; std::visit(ESM::VisitOverload{
[&](ESM4::Reference& /*ref*/) {},
[&](ESM::CellRef& ref) { ref.mGoldValue = value; },
},
mCellRef.mVariant);
} }
} }
void CellRef::writeState(ESM::ObjectState& state) const void CellRef::writeState(ESM::ObjectState& state) const
{ {
state.mRef = mCellRef; std::visit(ESM::VisitOverload{
[&](const ESM4::Reference& /*ref*/) {},
[&](const ESM::CellRef& ref) { state.mRef = ref; },
},
mCellRef.mVariant);
} }
} }

View file

@ -3,7 +3,9 @@
#include <string_view> #include <string_view>
#include <components/esm/esmbridge.hpp>
#include <components/esm3/cellref.hpp> #include <components/esm3/cellref.hpp>
#include <components/esm4/loadrefr.hpp>
namespace ESM namespace ESM
{ {
@ -16,15 +18,14 @@ namespace MWWorld
/// \brief Encapsulated variant of ESM::CellRef with change tracking /// \brief Encapsulated variant of ESM::CellRef with change tracking
class CellRef class CellRef
{ {
protected:
public: public:
CellRef(const ESM::CellRef& ref) explicit CellRef(const ESM::CellRef& ref);
: mCellRef(ref)
{ explicit CellRef(const ESM4::Reference& ref);
mChanged = false;
}
// Note: Currently unused for items in containers // Note: Currently unused for items in containers
const ESM::RefNum& getRefNum() const { return mCellRef.mRefNum; } const ESM::RefNum& getRefNum() const;
// Returns RefNum. // Returns RefNum.
// If RefNum is not set, assigns a generated one and changes the "lastAssignedRefNum" counter. // If RefNum is not set, assigns a generated one and changes the "lastAssignedRefNum" counter.
@ -34,32 +35,62 @@ namespace MWWorld
void unsetRefNum(); void unsetRefNum();
/// Does the RefNum have a content file? /// Does the RefNum have a content file?
bool hasContentFile() const { return mCellRef.mRefNum.hasContentFile(); } bool hasContentFile() const { return getRefNum().hasContentFile(); }
// Id of object being referenced // Id of object being referenced
const ESM::RefId& getRefId() const { return mCellRef.mRefID; } const ESM::RefId& getRefId() const
{
struct Visitor
{
const ESM::RefId& operator()(const ESM::CellRef& ref) { return ref.mRefID; }
const ESM::RefId& operator()(const ESM4::Reference& ref) { return ref.mBaseObj; }
};
return std::visit(Visitor(), mCellRef.mVariant);
}
// For doors - true if this door teleports to somewhere else, false // For doors - true if this door teleports to somewhere else, false
// if it should open through animation. // if it should open through animation.
bool getTeleport() const { return mCellRef.mTeleport; } bool getTeleport() const
{
struct Visitor
{
bool operator()(const ESM::CellRef& ref) { return ref.mTeleport; }
bool operator()(const ESM4::Reference& ref) { return 0; }
};
return std::visit(Visitor(), mCellRef.mVariant);
}
// Teleport location for the door, if this is a teleporting door. // Teleport location for the door, if this is a teleporting door.
const ESM::Position& getDoorDest() const { return mCellRef.mDoorDest; } const ESM::Position& getDoorDest() const
{
struct Visitor
{
const ESM::Position& operator()(const ESM::CellRef& ref) { return ref.mDoorDest; }
const ESM::Position& operator()(const ESM4::Reference& ref) { return ref.mDoor.destPos; }
};
return std::visit(Visitor(), mCellRef.mVariant);
}
// Destination cell for doors (optional) // Destination cell for doors (optional)
const std::string& getDestCell() const { return mCellRef.mDestCell; } const std::string& getDestCell() const;
// Scale applied to mesh // Scale applied to mesh
float getScale() const { return mCellRef.mScale; } float getScale() const
{
return std::visit([&](auto&& ref) { return ref.mScale; }, mCellRef.mVariant);
}
void setScale(float scale); void setScale(float scale);
// The *original* position and rotation as it was given in the Construction Set. // The *original* position and rotation as it was given in the Construction Set.
// Current position and rotation of the object is stored in RefData. // Current position and rotation of the object is stored in RefData.
const ESM::Position& getPosition() const { return mCellRef.mPos; } const ESM::Position& getPosition() const
{
return std::visit([](auto&& ref) -> const ESM::Position& { return ref.mPos; }, mCellRef.mVariant);
}
void setPosition(const ESM::Position& position); void setPosition(const ESM::Position& position);
// Remaining enchantment charge. This could be -1 if the charge was not touched yet (i.e. full). // Remaining enchantment charge. This could be -1 if the charge was not touched yet (i.e. full).
float getEnchantmentCharge() const { return mCellRef.mEnchantmentCharge; } float getEnchantmentCharge() const;
// Remaining enchantment charge rescaled to the supplied maximum charge (such as one of the enchantment). // Remaining enchantment charge rescaled to the supplied maximum charge (such as one of the enchantment).
float getNormalizedEnchantmentCharge(int maxCharge) const; float getNormalizedEnchantmentCharge(int maxCharge) const;
@ -69,50 +100,115 @@ namespace MWWorld
// For weapon or armor, this is the remaining item health. // For weapon or armor, this is the remaining item health.
// For tools (lockpicks, probes, repair hammer) it is the remaining uses. // For tools (lockpicks, probes, repair hammer) it is the remaining uses.
// If this returns int(-1) it means full health. // If this returns int(-1) it means full health.
int getCharge() const { return mCellRef.mChargeInt; } int getCharge() const
float getChargeFloat() const { return mCellRef.mChargeFloat; } // Implemented as union with int charge {
struct Visitor
{
int operator()(const ESM::CellRef& ref) { return ref.mChargeInt; }
int operator()(const ESM4::Reference& /*ref*/) { return 0; }
};
return std::visit(Visitor(), mCellRef.mVariant);
}
float getChargeFloat() const
{
struct Visitor
{
float operator()(const ESM::CellRef& ref) { return ref.mChargeFloat; }
float operator()(const ESM4::Reference& /*ref*/) { return 0; }
};
return std::visit(Visitor(), mCellRef.mVariant);
} // Implemented as union with int charge
void setCharge(int charge); void setCharge(int charge);
void setChargeFloat(float charge); void setChargeFloat(float charge);
void applyChargeRemainderToBeSubtracted(float chargeRemainder); // Stores remainders and applies if > 1 void applyChargeRemainderToBeSubtracted(float chargeRemainder); // Stores remainders and applies if > 1
// The NPC that owns this object (and will get angry if you steal it) // The NPC that owns this object (and will get angry if you steal it)
const ESM::RefId& getOwner() const { return mCellRef.mOwner; } const ESM::RefId& getOwner() const
{
struct Visitor
{
const ESM::RefId& operator()(const ESM::CellRef& ref) { return ref.mOwner; }
const ESM::RefId& operator()(const ESM4::Reference& /*ref*/) { return ESM::RefId::sEmpty; }
};
return std::visit(Visitor(), mCellRef.mVariant);
}
void setOwner(const ESM::RefId& owner); void setOwner(const ESM::RefId& owner);
// Name of a global variable. If the global variable is set to '1', using the object is temporarily allowed // Name of a global variable. If the global variable is set to '1', using the object is temporarily allowed
// even if it has an Owner field. // even if it has an Owner field.
// Used by bed rent scripts to allow the player to use the bed for the duration of the rent. // Used by bed rent scripts to allow the player to use the bed for the duration of the rent.
const std::string& getGlobalVariable() const { return mCellRef.mGlobalVariable; } const std::string& getGlobalVariable() const;
void resetGlobalVariable(); void resetGlobalVariable();
// ID of creature trapped in this soul gem // ID of creature trapped in this soul gem
const ESM::RefId& getSoul() const { return mCellRef.mSoul; } const ESM::RefId& getSoul() const
{
struct Visitor
{
const ESM::RefId& operator()(const ESM::CellRef& ref) { return ref.mSoul; }
const ESM::RefId& operator()(const ESM4::Reference& /*ref*/) { return ESM::RefId::sEmpty; }
};
return std::visit(Visitor(), mCellRef.mVariant);
}
void setSoul(const ESM::RefId& soul); void setSoul(const ESM::RefId& soul);
// The faction that owns this object (and will get angry if // The faction that owns this object (and will get angry if
// you take it and are not a faction member) // you take it and are not a faction member)
const ESM::RefId& getFaction() const { return mCellRef.mFaction; } const ESM::RefId& getFaction() const
{
struct Visitor
{
const ESM::RefId& operator()(const ESM::CellRef& ref) { return ref.mFaction; }
const ESM::RefId& operator()(const ESM4::Reference& /*ref*/) { return ESM::RefId::sEmpty; }
};
return std::visit(Visitor(), mCellRef.mVariant);
}
void setFaction(const ESM::RefId& faction); void setFaction(const ESM::RefId& faction);
// PC faction rank required to use the item. Sometimes is -1, which means "any rank". // PC faction rank required to use the item. Sometimes is -1, which means "any rank".
void setFactionRank(int factionRank); void setFactionRank(int factionRank);
int getFactionRank() const { return mCellRef.mFactionRank; } int getFactionRank() const
{
return std::visit([&](auto&& ref) { return ref.mFactionRank; }, mCellRef.mVariant);
}
// Lock level for doors and containers // Lock level for doors and containers
// Positive for a locked door. 0 for a door that was never locked. // Positive for a locked door. 0 for a door that was never locked.
// For an unlocked door, it is set to -(previous locklevel) // For an unlocked door, it is set to -(previous locklevel)
int getLockLevel() const { return mCellRef.mLockLevel; } int getLockLevel() const
{
return std::visit([](auto&& ref) { return static_cast<int>(ref.mLockLevel); }, mCellRef.mVariant);
}
void setLockLevel(int lockLevel); void setLockLevel(int lockLevel);
void lock(int lockLevel); void lock(int lockLevel);
void unlock(); void unlock();
// Key and trap ID names, if any // Key and trap ID names, if any
const ESM::RefId& getKey() const { return mCellRef.mKey; } const ESM::RefId& getKey() const
const ESM::RefId& getTrap() const { return mCellRef.mTrap; } {
return std::visit([](auto&& ref) -> const ESM::RefId& { return ref.mKey; }, mCellRef.mVariant);
}
const ESM::RefId& getTrap() const
{
struct Visitor
{
const ESM::RefId& operator()(const ESM::CellRef& ref) { return ref.mTrap; }
const ESM::RefId& operator()(const ESM4::Reference& /*ref*/) { return ESM::RefId::sEmpty; }
};
return std::visit(Visitor(), mCellRef.mVariant);
}
void setTrap(const ESM::RefId& trap); void setTrap(const ESM::RefId& trap);
// This is 5 for Gold_005 references, 100 for Gold_100 and so on. // This is 5 for Gold_005 references, 100 for Gold_100 and so on.
int getGoldValue() const { return mCellRef.mGoldValue; } int getGoldValue() const
{
struct Visitor
{
int operator()(const ESM::CellRef& ref) { return ref.mGoldValue; }
int operator()(const ESM4::Reference& /*ref*/) { return 0; }
};
return std::visit(Visitor(), mCellRef.mVariant);
}
void setGoldValue(int value); void setGoldValue(int value);
// Write the content of this CellRef into the given ObjectState // Write the content of this CellRef into the given ObjectState
@ -122,8 +218,8 @@ namespace MWWorld
bool hasChanged() const { return mChanged; } bool hasChanged() const { return mChanged; }
private: private:
bool mChanged; bool mChanged = false;
ESM::CellRef mCellRef; ESM::ReferenceVariant mCellRef;
}; };
} }

View file

@ -28,6 +28,8 @@ namespace MWWorld
/// all methods are known. /// all methods are known.
void load(ESM::CellRef& ref, bool deleted, const MWWorld::ESMStore& esmStore); void load(ESM::CellRef& ref, bool deleted, const MWWorld::ESMStore& esmStore);
void load(const ESM4::Reference& ref, bool deleted, const MWWorld::ESMStore& esmStore);
LiveRef& insert(const LiveRef& item) LiveRef& insert(const LiveRef& item)
{ {
mList.push_back(item); mList.push_back(item);

View file

@ -39,6 +39,8 @@
#include <components/esm3/npcstate.hpp> #include <components/esm3/npcstate.hpp>
#include <components/esm3/objectstate.hpp> #include <components/esm3/objectstate.hpp>
#include <components/esm3/readerscache.hpp> #include <components/esm3/readerscache.hpp>
#include <components/esm4/loadrefr.hpp>
#include <components/esm4/loadstat.hpp>
#include <components/misc/tuplehelpers.hpp> #include <components/misc/tuplehelpers.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -369,6 +371,29 @@ namespace MWWorld
} }
} }
template <typename X>
void CellRefList<X>::load(const ESM4::Reference& ref, bool deleted, const MWWorld::ESMStore& esmStore)
{
if constexpr (!ESM::isESM4Rec(X::sRecordId))
return;
const MWWorld::Store<X>& store = esmStore.get<X>();
if (const X* ptr = store.search(ref.mBaseObj))
{
LiveRef liveCellRef(ref, ptr);
if (deleted)
liveCellRef.mData.setDeletedByContentFile(true);
mList.push_back(liveCellRef);
}
else
{
Log(Debug::Warning) << "Warning: could not resolve cell reference '" << ref.mId << "'"
<< " (dropping reference)";
}
}
template <typename X> template <typename X>
bool operator==(const LiveCellRef<X>& ref, int pRefnum) bool operator==(const LiveCellRef<X>& ref, int pRefnum)
{ {
@ -503,26 +528,27 @@ namespace MWWorld
return false; return false;
} }
CellStore::CellStore(const ESM::Cell* cell, const MWWorld::ESMStore& esmStore, ESM::ReadersCache& readers) CellStore::CellStore(MWWorld::Cell cell, const MWWorld::ESMStore& esmStore, ESM::ReadersCache& readers)
: mStore(esmStore) : mStore(esmStore)
, mReaders(readers) , mReaders(readers)
, mCell(cell) , mCellVariant(std::move(cell))
, mState(State_Unloaded) , mState(State_Unloaded)
, mHasState(false) , mHasState(false)
, mLastRespawn(0, 0) , mLastRespawn(0, 0)
, mCellStoreImp(std::make_unique<CellStoreImp>()) , mCellStoreImp(std::make_unique<CellStoreImp>())
, mRechargingItemsUpToDate(false) , mRechargingItemsUpToDate(false)
{ {
std::apply([this](auto&... x) { (CellStoreImp::assignStoreToIndex(*this, x), ...); }, mCellStoreImp->mRefLists); std::apply([this](auto&... x) { (CellStoreImp::assignStoreToIndex(*this, x), ...); }, mCellStoreImp->mRefLists);
mWaterLevel = cell->mWater; mWaterLevel = mCellVariant.getWaterHeight();
} }
CellStore::~CellStore() = default; CellStore::~CellStore() = default;
CellStore::CellStore(CellStore&&) = default; CellStore::CellStore(CellStore&&) = default;
const ESM::Cell* CellStore::getCell() const const MWWorld::Cell* CellStore::getCell() const
{ {
return mCell; return &mCellVariant;
} }
CellStore::State CellStore::getState() const CellStore::State CellStore::getState() const
@ -676,22 +702,20 @@ namespace MWWorld
} }
} }
void CellStore::listRefs() void CellStore::listRefs(const ESM::Cell& cell)
{ {
assert(mCell); if (cell.mContextList.empty())
if (mCell->mContextList.empty())
return; // this is a dynamically generated cell -> skipping. return; // this is a dynamically generated cell -> skipping.
// Load references from all plugins that do something with this cell. // Load references from all plugins that do something with this cell.
for (size_t i = 0; i < mCell->mContextList.size(); i++) for (size_t i = 0; i < cell.mContextList.size(); i++)
{ {
try try
{ {
// Reopen the ESM reader and seek to the right position. // Reopen the ESM reader and seek to the right position.
const std::size_t index = static_cast<std::size_t>(mCell->mContextList[i].index); const std::size_t index = static_cast<std::size_t>(cell.mContextList[i].index);
const ESM::ReadersCache::BusyItem reader = mReaders.get(index); const ESM::ReadersCache::BusyItem reader = mReaders.get(index);
mCell->restore(*reader, i); cell.restore(*reader, i);
ESM::CellRef ref; ESM::CellRef ref;
@ -707,8 +731,8 @@ namespace MWWorld
// Don't list reference if it was moved to a different cell. // Don't list reference if it was moved to a different cell.
ESM::MovedCellRefTracker::const_iterator iter ESM::MovedCellRefTracker::const_iterator iter
= std::find(mCell->mMovedRefs.begin(), mCell->mMovedRefs.end(), ref.mRefNum); = std::find(cell.mMovedRefs.begin(), cell.mMovedRefs.end(), ref.mRefNum);
if (iter != mCell->mMovedRefs.end()) if (iter != cell.mMovedRefs.end())
{ {
continue; continue;
} }
@ -724,33 +748,46 @@ namespace MWWorld
} }
// List moved references, from separately tracked list. // List moved references, from separately tracked list.
for (const auto& [ref, deleted] : mCell->mLeasedRefs) for (const auto& [ref, deleted] : cell.mLeasedRefs)
{ {
if (!deleted) if (!deleted)
mIds.push_back(ref.mRefID); mIds.push_back(ref.mRefID);
} }
}
void CellStore::listRefs(const ESM4::Cell& cell)
{
auto& refs = MWBase::Environment::get().getWorld()->getStore().get<ESM4::Reference>();
for (const auto& ref : refs)
{
if (ref.mParent == cell.mId)
{
mIds.push_back(ref.mBaseObj);
}
}
}
void CellStore::listRefs()
{
ESM::visit([&](auto&& cell) { listRefs(cell); }, mCellVariant);
std::sort(mIds.begin(), mIds.end()); std::sort(mIds.begin(), mIds.end());
} }
void CellStore::loadRefs() void CellStore::loadRefs(const ESM::Cell& cell, std::map<ESM::RefNum, ESM::RefId>& refNumToID)
{ {
assert(mCell); if (cell.mContextList.empty())
if (mCell->mContextList.empty())
return; // this is a dynamically generated cell -> skipping. return; // this is a dynamically generated cell -> skipping.
std::map<ESM::RefNum, ESM::RefId> refNumToID; // used to detect refID modifications
// Load references from all plugins that do something with this cell. // Load references from all plugins that do something with this cell.
for (size_t i = 0; i < mCell->mContextList.size(); i++) for (size_t i = 0; i < cell.mContextList.size(); i++)
{ {
try try
{ {
// Reopen the ESM reader and seek to the right position. // Reopen the ESM reader and seek to the right position.
const std::size_t index = static_cast<std::size_t>(mCell->mContextList[i].index); const std::size_t index = static_cast<std::size_t>(cell.mContextList[i].index);
const ESM::ReadersCache::BusyItem reader = mReaders.get(index); const ESM::ReadersCache::BusyItem reader = mReaders.get(index);
mCell->restore(*reader, i); cell.restore(*reader, i);
ESM::CellRef ref; ESM::CellRef ref;
// Get each reference in turn // Get each reference in turn
@ -765,8 +802,8 @@ namespace MWWorld
// Don't load reference if it was moved to a different cell. // Don't load reference if it was moved to a different cell.
ESM::MovedCellRefTracker::const_iterator iter ESM::MovedCellRefTracker::const_iterator iter
= std::find(mCell->mMovedRefs.begin(), mCell->mMovedRefs.end(), ref.mRefNum); = std::find(cell.mMovedRefs.begin(), cell.mMovedRefs.end(), ref.mRefNum);
if (iter != mCell->mMovedRefs.end()) if (iter != cell.mMovedRefs.end())
{ {
continue; continue;
} }
@ -780,27 +817,46 @@ namespace MWWorld
<< ": " << e.what(); << ": " << e.what();
} }
} }
// Load moved references, from separately tracked list. // Load moved references, from separately tracked list.
for (const auto& leasedRef : mCell->mLeasedRefs) for (const auto& leasedRef : cell.mLeasedRefs)
{ {
ESM::CellRef& ref = const_cast<ESM::CellRef&>(leasedRef.first); ESM::CellRef& ref = const_cast<ESM::CellRef&>(leasedRef.first);
bool deleted = leasedRef.second; bool deleted = leasedRef.second;
loadRef(ref, deleted, refNumToID); loadRef(ref, deleted, refNumToID);
} }
}
void CellStore::loadRefs(const ESM4::Cell& cell, std::map<ESM::RefNum, ESM::RefId>& refNumToID)
{
auto& refs = MWBase::Environment::get().getWorld()->getStore().get<ESM4::Reference>();
for (const auto& ref : refs)
{
if (ref.mParent == cell.mId)
{
loadRef(ref, false);
}
}
}
void CellStore::loadRefs()
{
std::map<ESM::RefNum, ESM::RefId> refNumToID; // used to detect refID modifications
ESM::visit([&](auto&& cell) { loadRefs(cell, refNumToID); }, mCellVariant);
updateMergedRefs(); updateMergedRefs();
} }
bool CellStore::isExterior() const bool CellStore::isExterior() const
{ {
return mCell->isExterior(); return mCellVariant.isExterior();
} }
bool CellStore::isQuasiExterior() const bool CellStore::isQuasiExterior() const
{ {
return (mCell->mData.mFlags & ESM::Cell::QuasiEx) != 0; return mCellVariant.isQuasiExterior();
} }
Ptr CellStore::searchInContainer(const ESM::RefId& id) Ptr CellStore::searchInContainer(const ESM::RefId& id)
@ -823,6 +879,18 @@ namespace MWWorld
return Ptr(); return Ptr();
} }
void CellStore::loadRef(const ESM4::Reference& ref, bool deleted)
{
const MWWorld::ESMStore& store = mStore;
ESM::RecNameInts foundType = static_cast<ESM::RecNameInts>(store.find(ref.mBaseObj));
Misc::tupleForEach(this->mCellStoreImp->mRefLists, [&ref, &deleted, &store, foundType](auto& x) {
recNameSwitcher(
x, foundType, [&ref, &deleted, &store](auto& storeIn) { storeIn.load(ref, deleted, store); });
});
}
void CellStore::loadRef(ESM::CellRef& ref, bool deleted, std::map<ESM::RefNum, ESM::RefId>& refNumToID) void CellStore::loadRef(ESM::CellRef& ref, bool deleted, std::map<ESM::RefNum, ESM::RefId>& refNumToID)
{ {
const MWWorld::ESMStore& store = mStore; const MWWorld::ESMStore& store = mStore;
@ -874,7 +942,7 @@ namespace MWWorld
{ {
mHasState = true; mHasState = true;
if (mCell->mData.mFlags & ESM::Cell::Interior && mCell->mData.mFlags & ESM::Cell::HasWater) if (!mCellVariant.isExterior() && mCellVariant.hasWater())
mWaterLevel = state.mWaterLevel; mWaterLevel = state.mWaterLevel;
mLastRespawn = MWWorld::TimeStamp(state.mLastRespawn); mLastRespawn = MWWorld::TimeStamp(state.mLastRespawn);
@ -882,9 +950,9 @@ namespace MWWorld
void CellStore::saveState(ESM::CellState& state) const void CellStore::saveState(ESM::CellState& state) const
{ {
state.mId = mCell->getCellId(); state.mId = mCellVariant.getCellId();
if (mCell->mData.mFlags & ESM::Cell::Interior && mCell->mData.mFlags & ESM::Cell::HasWater) if (!mCellVariant.isExterior() && mCellVariant.hasWater())
state.mWaterLevel = mWaterLevel; state.mWaterLevel = mWaterLevel;
state.mHasFogOfWar = (mFogState.get() ? 1 : 0); state.mHasFogOfWar = (mFogState.get() ? 1 : 0);
@ -895,7 +963,7 @@ namespace MWWorld
{ {
if (mFogState.get()) if (mFogState.get())
{ {
mFogState->save(writer, mCell->mData.mFlags & ESM::Cell::Interior); mFogState->save(writer, !mCellVariant.isExterior());
} }
} }
@ -999,8 +1067,8 @@ namespace MWWorld
Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << movedRef.getCellRef().getRefId() Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << movedRef.getCellRef().getRefId()
<< " (target cell " << movedTo.mWorldspace << " (target cell " << movedTo.mWorldspace
<< " no longer exists). Reference moved back to its original location."; << " no longer exists). Reference moved back to its original location.";
// Note by dropping tag the object will automatically re-appear in its original cell, though potentially // Note by dropping tag the object will automatically re-appear in its original cell, though
// at inapproriate coordinates. Restore original coordinates: // potentially at inapproriate coordinates. Restore original coordinates:
movedRef.getRefData().setPosition(movedRef.getCellRef().getPosition()); movedRef.getRefData().setPosition(movedRef.getCellRef().getPosition());
continue; continue;
} }
@ -1016,14 +1084,21 @@ namespace MWWorld
} }
} }
bool operator==(const CellStore& left, const CellStore& right) struct IsEqualVisitor
{ {
return left.getCell()->getCellId() == right.getCell()->getCellId(); bool operator()(const ESM::Cell& a, const ESM::Cell& b) const { return a.getCellId() == b.getCellId(); }
} bool operator()(const ESM4::Cell& a, const ESM4::Cell& b) const { return a.mId == b.mId; }
bool operator!=(const CellStore& left, const CellStore& right) template <class L, class R>
bool operator()(const L&, const R&) const
{
return false;
}
};
bool CellStore::operator==(const CellStore& right) const
{ {
return !(left == right); return ESM::visit(IsEqualVisitor(), this->mCellVariant, right.mCellVariant);
} }
void CellStore::setFog(std::unique_ptr<ESM::FogState>&& fog) void CellStore::setFog(std::unique_ptr<ESM::FogState>&& fog)
@ -1230,4 +1305,5 @@ namespace MWWorld
} }
return {}; return {};
} }
} }

View file

@ -11,6 +11,7 @@
#include <typeinfo> #include <typeinfo>
#include <vector> #include <vector>
#include "cell.hpp"
#include "cellreflist.hpp" #include "cellreflist.hpp"
#include "livecellref.hpp" #include "livecellref.hpp"
@ -49,6 +50,15 @@ namespace ESM
struct Static; struct Static;
struct Weapon; struct Weapon;
struct BodyPart; struct BodyPart;
struct CellCommon;
}
namespace ESM4
{
class Reader;
struct Cell;
struct Reference;
struct Static;
} }
namespace MWWorld namespace MWWorld
@ -61,7 +71,9 @@ namespace MWWorld
CellRefList<ESM::Container>, CellRefList<ESM::Creature>, CellRefList<ESM::Door>, CellRefList<ESM::Ingredient>, CellRefList<ESM::Container>, CellRefList<ESM::Creature>, CellRefList<ESM::Door>, CellRefList<ESM::Ingredient>,
CellRefList<ESM::CreatureLevList>, CellRefList<ESM::ItemLevList>, CellRefList<ESM::Light>, CellRefList<ESM::CreatureLevList>, CellRefList<ESM::ItemLevList>, CellRefList<ESM::Light>,
CellRefList<ESM::Lockpick>, CellRefList<ESM::Miscellaneous>, CellRefList<ESM::NPC>, CellRefList<ESM::Probe>, CellRefList<ESM::Lockpick>, CellRefList<ESM::Miscellaneous>, CellRefList<ESM::NPC>, CellRefList<ESM::Probe>,
CellRefList<ESM::Repair>, CellRefList<ESM::Static>, CellRefList<ESM::Weapon>, CellRefList<ESM::BodyPart>>; CellRefList<ESM::Repair>, CellRefList<ESM::Static>, CellRefList<ESM::Weapon>, CellRefList<ESM::BodyPart>,
CellRefList<ESM4::Static>>;
/// \brief Mutable state of a cell /// \brief Mutable state of a cell
class CellStore class CellStore
@ -85,7 +97,7 @@ namespace MWWorld
// Note this is nullptr until the cell is explored to save some memory // Note this is nullptr until the cell is explored to save some memory
std::unique_ptr<ESM::FogState> mFogState; std::unique_ptr<ESM::FogState> mFogState;
const ESM::Cell* mCell; MWWorld::Cell mCellVariant;
State mState; State mState;
bool mHasState; bool mHasState;
std::vector<ESM::RefId> mIds; std::vector<ESM::RefId> mIds;
@ -180,11 +192,11 @@ namespace MWWorld
} }
/// @param readerList The readers to use for loading of the cell on-demand. /// @param readerList The readers to use for loading of the cell on-demand.
CellStore(const ESM::Cell* cell, const MWWorld::ESMStore& store, ESM::ReadersCache& readers); CellStore(MWWorld::Cell cell, const MWWorld::ESMStore& store, ESM::ReadersCache& readers);
CellStore(CellStore&&); CellStore(CellStore&&);
~CellStore(); ~CellStore();
const ESM::Cell* getCell() const; const MWWorld::Cell* getCell() const;
State getState() const; State getState() const;
@ -331,6 +343,7 @@ namespace MWWorld
// Should be phased out when we have const version of forEach // Should be phased out when we have const version of forEach
inline const CellRefList<ESM::Door>& getReadOnlyDoors() const { return get<ESM::Door>(); } inline const CellRefList<ESM::Door>& getReadOnlyDoors() const { return get<ESM::Door>(); }
inline const CellRefList<ESM::Static>& getReadOnlyStatics() const { return get<ESM::Static>(); } inline const CellRefList<ESM::Static>& getReadOnlyStatics() const { return get<ESM::Static>(); }
inline const CellRefList<ESM4::Static>& getReadOnlyEsm4Statics() const { return get<ESM4::Static>(); }
bool isExterior() const; bool isExterior() const;
@ -366,20 +379,27 @@ namespace MWWorld
Ptr getMovedActor(int actorId) const; Ptr getMovedActor(int actorId) const;
bool operator==(const CellStore& right) const;
private: private:
/// Run through references and store IDs /// Run through references and store IDs
void listRefs(const ESM::Cell& cell);
void listRefs(const ESM4::Cell& cell);
void listRefs(); void listRefs();
void loadRefs(const ESM::Cell& cell, std::map<ESM::RefNum, ESM::RefId>& refNumToID);
void loadRefs(const ESM4::Cell& cell, std::map<ESM::RefNum, ESM::RefId>& refNumToID);
void loadRefs(); void loadRefs();
void loadRef(const ESM4::Reference& ref, bool deleted);
void loadRef(ESM::CellRef& ref, bool deleted, std::map<ESM::RefNum, ESM::RefId>& refNumToID); void loadRef(ESM::CellRef& ref, bool deleted, std::map<ESM::RefNum, ESM::RefId>& refNumToID);
///< Make case-adjustments to \a ref and insert it into the respective container. ///< Make case-adjustments to \a ref and insert it into the respective container.
/// ///
/// Invalid \a ref objects are silently dropped. /// Invalid \a ref objects are silently dropped.
///
}; };
bool operator==(const CellStore& left, const CellStore& right);
bool operator!=(const CellStore& left, const CellStore& right);
} }
#endif #endif

View file

@ -210,8 +210,8 @@ namespace MWWorld
static bool readRecord(ESM4::Reader& reader, ESMStore& store) static bool readRecord(ESM4::Reader& reader, ESMStore& store)
{ {
return std::apply([&reader](auto&... x) { return (ESMStoreImp::typedReadRecordESM4(reader, x) || ...); }, return std::apply(
store.mStoreImp->mStores); [&reader](auto&... x) { return (typedReadRecordESM4(reader, x) || ...); }, store.mStoreImp->mStores);
} }
}; };
@ -278,6 +278,7 @@ namespace MWWorld
case ESM::REC_STAT: case ESM::REC_STAT:
case ESM::REC_WEAP: case ESM::REC_WEAP:
case ESM::REC_BODY: case ESM::REC_BODY:
case ESM::REC_STAT4:
return true; return true;
break; break;
} }
@ -595,8 +596,8 @@ namespace MWWorld
removeMissingObjects(getWritable<ESM::ItemLevList>()); removeMissingObjects(getWritable<ESM::ItemLevList>());
} }
// Leveled lists can be modified by scripts. This removes items that no longer exist (presumably because the plugin // Leveled lists can be modified by scripts. This removes items that no longer exist (presumably because the
// was removed) from modified lists // plugin was removed) from modified lists
template <class T> template <class T>
void ESMStore::removeMissingObjects(Store<T>& store) void ESMStore::removeMissingObjects(Store<T>& store)
{ {

View file

@ -22,9 +22,16 @@ MWWorld::LiveCellRefBase::LiveCellRefBase(unsigned int type, const ESM::CellRef&
{ {
} }
MWWorld::LiveCellRefBase::LiveCellRefBase(unsigned int type, const ESM4::Reference& cref)
: mClass(&Class::get(type))
, mRef(cref)
, mData(cref)
{
}
void MWWorld::LiveCellRefBase::loadImp(const ESM::ObjectState& state) void MWWorld::LiveCellRefBase::loadImp(const ESM::ObjectState& state)
{ {
mRef = state.mRef; mRef = MWWorld::CellRef(state.mRef);
mData = RefData(state, mData.isDeletedByContentFile()); mData = RefData(state, mData.isDeletedByContentFile());
Ptr ptr(this); Ptr ptr(this);

View file

@ -35,6 +35,7 @@ namespace MWWorld
RefData mData; RefData mData;
LiveCellRefBase(unsigned int type, const ESM::CellRef& cref = ESM::CellRef()); LiveCellRefBase(unsigned int type, const ESM::CellRef& cref = ESM::CellRef());
LiveCellRefBase(unsigned int type, const ESM4::Reference& cref);
/* Need this for the class to be recognized as polymorphic */ /* Need this for the class to be recognized as polymorphic */
virtual ~LiveCellRefBase() {} virtual ~LiveCellRefBase() {}
@ -87,7 +88,8 @@ namespace MWWorld
{ {
if (const LiveCellRef<T>* ref = dynamic_cast<const LiveCellRef<T>*>(value)) if (const LiveCellRef<T>* ref = dynamic_cast<const LiveCellRef<T>*>(value))
return ref; return ref;
throw std::runtime_error(makeDynamicCastErrorMessage(value, T::getRecordType())); throw std::runtime_error(
makeDynamicCastErrorMessage(value, ESM::getRecNameString(T::sRecordId).toStringView()));
} }
template <class T> template <class T>
@ -95,7 +97,8 @@ namespace MWWorld
{ {
if (LiveCellRef<T>* ref = dynamic_cast<LiveCellRef<T>*>(value)) if (LiveCellRef<T>* ref = dynamic_cast<LiveCellRef<T>*>(value))
return ref; return ref;
throw std::runtime_error(makeDynamicCastErrorMessage(value, T::getRecordType())); throw std::runtime_error(
makeDynamicCastErrorMessage(value, ESM::getRecNameString(T::sRecordId).toStringView()));
} }
/// A reference to one object (of any type) in a cell. /// A reference to one object (of any type) in a cell.
@ -113,6 +116,12 @@ namespace MWWorld
{ {
} }
LiveCellRef(const ESM4::Reference& cref, const X* b = nullptr)
: LiveCellRefBase(X::sRecordId, cref)
, mBase(b)
{
}
LiveCellRef(const X* b = nullptr) LiveCellRef(const X* b = nullptr)
: LiveCellRefBase(X::sRecordId) : LiveCellRefBase(X::sRecordId)
, mBase(b) , mBase(b)
@ -130,7 +139,13 @@ namespace MWWorld
void save(ESM::ObjectState& state) const override; void save(ESM::ObjectState& state) const override;
///< Save LiveCellRef state into \a state. ///< Save LiveCellRef state into \a state.
std::string_view getTypeDescription() const override { return X::getRecordType(); } std::string_view getTypeDescription() const override
{
if constexpr (ESM::isESM4Rec(X::sRecordId))
return ESM::getRecNameString(X::sRecordId).toStringView();
else
return X::getRecordType();
}
static bool checkState(const ESM::ObjectState& state); static bool checkState(const ESM::ObjectState& state);
///< Check if state is valid and report errors. ///< Check if state is valid and report errors.

View file

@ -1,5 +1,6 @@
#include "manualref.hpp" #include "manualref.hpp"
#include <components/esm/records.hpp> #include <components/esm/records.hpp>
#include <components/esm4/loadstat.hpp>
#include "esmstore.hpp" #include "esmstore.hpp"
@ -89,7 +90,9 @@ MWWorld::ManualRef::ManualRef(const MWWorld::ESMStore& store, const ESM::RefId&
case ESM::REC_BODY: case ESM::REC_BODY:
create(store.get<ESM::BodyPart>(), name, mRef, mPtr); create(store.get<ESM::BodyPart>(), name, mRef, mPtr);
break; break;
case ESM::REC_STAT4:
create(store.get<ESM4::Static>(), name, mRef, mPtr);
break;
case 0: case 0:
throw std::logic_error("failed to create manual cell ref for " + name.getRefIdString() + " (unknown ID)"); throw std::logic_error("failed to create manual cell ref for " + name.getRefIdString() + " (unknown ID)");

View file

@ -85,6 +85,19 @@ namespace MWWorld
{ {
} }
RefData::RefData(const ESM4::Reference& cellRef)
: mBaseNode(nullptr)
, mDeletedByContentFile(false)
, mEnabled(true)
, mPhysicsPostponed(false)
, mCount(1)
, mPosition(cellRef.mPos)
, mCustomData(nullptr)
, mChanged(false)
, mFlags(0)
{
}
RefData::RefData(const ESM::ObjectState& objectState, bool deletedByContentFile) RefData::RefData(const ESM::ObjectState& objectState, bool deletedByContentFile)
: mBaseNode(nullptr) : mBaseNode(nullptr)
, mDeletedByContentFile(deletedByContentFile) , mDeletedByContentFile(deletedByContentFile)

View file

@ -25,6 +25,11 @@ namespace ESM
struct ObjectState; struct ObjectState;
} }
namespace ESM4
{
struct Reference;
}
namespace MWLua namespace MWLua
{ {
class LocalScripts; class LocalScripts;
@ -76,6 +81,7 @@ namespace MWWorld
/// be altered without affecting the original data. This makes it possible /// be altered without affecting the original data. This makes it possible
/// to reset the position as the original data is still held in the CellRef /// to reset the position as the original data is still held in the CellRef
RefData(const ESM::CellRef& cellRef); RefData(const ESM::CellRef& cellRef);
RefData(const ESM4::Reference& cellRef);
RefData(const ESM::ObjectState& objectState, bool deletedByContentFile); RefData(const ESM::ObjectState& objectState, bool deletedByContentFile);
///< Ignores local variables and custom data (not enough context available here to ///< Ignores local variables and custom data (not enough context available here to

View file

@ -317,7 +317,6 @@ namespace MWWorld
{ {
if (mActiveCells.find(cell) == mActiveCells.end()) if (mActiveCells.find(cell) == mActiveCells.end())
return; return;
Log(Debug::Info) << "Unloading cell " << cell->getCell()->getDescription(); Log(Debug::Info) << "Unloading cell " << cell->getCell()->getDescription();
ListAndResetObjectsVisitor visitor; ListAndResetObjectsVisitor visitor;
@ -355,8 +354,14 @@ namespace MWWorld
if (cell->getCell()->hasWater()) if (cell->getCell()->hasWater())
mNavigator.removeWater(osg::Vec2i(cellX, cellY), navigatorUpdateGuard); mNavigator.removeWater(osg::Vec2i(cellX, cellY), navigatorUpdateGuard);
if (const auto pathgrid = mWorld.getStore().get<ESM::Pathgrid>().search(*cell->getCell())) ESM::visit(ESM::VisitOverload{
mNavigator.removePathgrid(*pathgrid); [&](const ESM::Cell& cell) {
if (const auto pathgrid = mWorld.getStore().get<ESM::Pathgrid>().search(cell))
mNavigator.removePathgrid(*pathgrid);
},
[&](const ESM4::Cell& cell) {},
},
*cell->getCell());
MWBase::Environment::get().getMechanicsManager()->drop(cell); MWBase::Environment::get().getMechanicsManager()->drop(cell);
@ -384,8 +389,9 @@ namespace MWWorld
const int cellX = cell->getCell()->getGridX(); const int cellX = cell->getCell()->getGridX();
const int cellY = cell->getCell()->getGridY(); const int cellY = cell->getCell()->getGridY();
const MWWorld::Cell& cellVariant = *cell->getCell();
if (cell->getCell()->isExterior()) if (cellVariant.isExterior())
{ {
osg::ref_ptr<const ESMTerrain::LandObject> land = mRendering.getLandManager()->getLand(cellX, cellY); osg::ref_ptr<const ESMTerrain::LandObject> land = mRendering.getLandManager()->getLand(cellX, cellY);
const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr; const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr;
@ -427,8 +433,14 @@ namespace MWWorld
} }
} }
if (const auto pathgrid = mWorld.getStore().get<ESM::Pathgrid>().search(*cell->getCell())) ESM::visit(ESM::VisitOverload{
mNavigator.addPathgrid(*cell->getCell(), *pathgrid); [&](const ESM::Cell& cell) {
if (const auto pathgrid = mWorld.getStore().get<ESM::Pathgrid>().search(cell))
mNavigator.addPathgrid(cell, *pathgrid);
},
[&](const ESM4::Cell& cell) {},
},
*cell->getCell());
// register local scripts // register local scripts
// do this before insertCell, to make sure we don't add scripts from levelled creature spawning twice // do this before insertCell, to make sure we don't add scripts from levelled creature spawning twice
@ -442,7 +454,7 @@ namespace MWWorld
mRendering.addCell(cell); mRendering.addCell(cell);
MWBase::Environment::get().getWindowManager()->addCell(cell); MWBase::Environment::get().getWindowManager()->addCell(cell);
bool waterEnabled = cell->getCell()->hasWater() || cell->isExterior(); bool waterEnabled = cellVariant.hasWater() || cell->isExterior();
float waterLevel = cell->getWaterLevel(); float waterLevel = cell->getWaterLevel();
mRendering.setWaterEnabled(waterEnabled); mRendering.setWaterEnabled(waterEnabled);
if (waterEnabled) if (waterEnabled)
@ -450,7 +462,7 @@ namespace MWWorld
mPhysics->enableWater(waterLevel); mPhysics->enableWater(waterLevel);
mRendering.setWaterHeight(waterLevel); mRendering.setWaterHeight(waterLevel);
if (cell->getCell()->isExterior()) if (cellVariant.isExterior())
{ {
if (const auto heightField = mPhysics->getHeightField(cellX, cellY)) if (const auto heightField = mPhysics->getHeightField(cellX, cellY))
mNavigator.addWater( mNavigator.addWater(
@ -465,8 +477,8 @@ namespace MWWorld
else else
mPhysics->disableWater(); mPhysics->disableWater();
if (!cell->isExterior() && !(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx)) if (!cell->isExterior() && !cellVariant.isQuasiExterior())
mRendering.configureAmbient(cell->getCell()); mRendering.configureAmbient(cellVariant);
mPreloader->notifyLoaded(cell); mPreloader->notifyLoaded(cell);
} }
@ -542,7 +554,7 @@ namespace MWWorld
mNavigator.setWorldspace( mNavigator.setWorldspace(
Misc::StringUtils::lowerCase( Misc::StringUtils::lowerCase(
mWorld.getWorldModel().getExterior(playerCellX, playerCellY)->getCell()->mCellId.mWorldspace), mWorld.getWorldModel().getExterior(playerCellX, playerCellY)->getCell()->getCellId().mWorldspace),
navigatorUpdateGuard.get()); navigatorUpdateGuard.get());
mNavigator.updateBounds(pos, navigatorUpdateGuard.get()); mNavigator.updateBounds(pos, navigatorUpdateGuard.get());
@ -665,7 +677,7 @@ namespace MWWorld
CellStore* cell = mWorld.getWorldModel().getExterior(it->mData.mX, it->mData.mY); CellStore* cell = mWorld.getWorldModel().getExterior(it->mData.mX, it->mData.mY);
mNavigator.setWorldspace( mNavigator.setWorldspace(
Misc::StringUtils::lowerCase(cell->getCell()->mCellId.mWorldspace), navigatorUpdateGuard.get()); Misc::StringUtils::lowerCase(cell->getCell()->getCellId().mWorldspace), navigatorUpdateGuard.get());
const osg::Vec3f position const osg::Vec3f position
= osg::Vec3f(it->mData.mX + 0.5f, it->mData.mY + 0.5f, 0) * Constants::CellSizeInUnits; = osg::Vec3f(it->mData.mX + 0.5f, it->mData.mY + 0.5f, 0) * Constants::CellSizeInUnits;
mNavigator.updateBounds(position, navigatorUpdateGuard.get()); mNavigator.updateBounds(position, navigatorUpdateGuard.get());
@ -723,7 +735,7 @@ namespace MWWorld
CellStore* cell = mWorld.getWorldModel().getInterior(it->mName); CellStore* cell = mWorld.getWorldModel().getInterior(it->mName);
mNavigator.setWorldspace( mNavigator.setWorldspace(
Misc::StringUtils::lowerCase(cell->getCell()->mCellId.mWorldspace), navigatorUpdateGuard.get()); Misc::StringUtils::lowerCase(cell->getCell()->getCellId().mWorldspace), navigatorUpdateGuard.get());
ESM::Position position; ESM::Position position;
mWorld.findInteriorPosition(it->mName, position); mWorld.findInteriorPosition(it->mName, position);
mNavigator.updateBounds(position.asVec3(), navigatorUpdateGuard.get()); mNavigator.updateBounds(position.asVec3(), navigatorUpdateGuard.get());
@ -739,7 +751,7 @@ namespace MWWorld
{ {
assert(!(*iter)->getCell()->isExterior()); assert(!(*iter)->getCell()->isExterior());
if (it->mName == (*iter)->getCell()->mName) if (it->mName == (*iter)->getCell()->getNameId())
{ {
unloadCell(*iter, navigatorUpdateGuard.get()); unloadCell(*iter, navigatorUpdateGuard.get());
break; break;
@ -880,7 +892,7 @@ namespace MWWorld
loadingListener->setProgressRange(cell->count()); loadingListener->setProgressRange(cell->count());
mNavigator.setWorldspace( mNavigator.setWorldspace(
Misc::StringUtils::lowerCase(cell->getCell()->mCellId.mWorldspace), navigatorUpdateGuard.get()); Misc::StringUtils::lowerCase(cell->getCell()->getCellId().mWorldspace), navigatorUpdateGuard.get());
mNavigator.updateBounds(position.asVec3(), navigatorUpdateGuard.get()); mNavigator.updateBounds(position.asVec3(), navigatorUpdateGuard.get());
// Load cell. // Load cell.
@ -892,7 +904,7 @@ namespace MWWorld
changePlayerCell(cell, position, adjustPlayerPos); changePlayerCell(cell, position, adjustPlayerPos);
// adjust fog // adjust fog
mRendering.configureFog(mCurrentCell->getCell()); mRendering.configureFog(*mCurrentCell->getCell());
// Sky system // Sky system
mWorld.adjustSky(); mWorld.adjustSky();
@ -907,8 +919,7 @@ namespace MWWorld
MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell);
MWBase::Environment::get().getWorld()->getPostProcessor()->setExteriorFlag( MWBase::Environment::get().getWorld()->getPostProcessor()->setExteriorFlag(cell->getCell()->isQuasiExterior());
cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx);
} }
void Scene::changeToExteriorCell(const ESM::Position& position, bool adjustPlayerPos, bool changeEvent) void Scene::changeToExteriorCell(const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)

View file

@ -14,6 +14,8 @@
#include <components/loadinglistener/loadinglistener.hpp> #include <components/loadinglistener/loadinglistener.hpp>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <apps/openmw/mwworld/cell.hpp>
namespace namespace
{ {
// TODO: Switch to C++23 to get a working version of std::unordered_map::erase // TODO: Switch to C++23 to get a working version of std::unordered_map::erase
@ -961,6 +963,14 @@ namespace MWWorld
else else
return search(ESM::RefId::stringRefId(cell.mName)); return search(ESM::RefId::stringRefId(cell.mName));
} }
const ESM::Pathgrid* Store<ESM::Pathgrid>::search(const MWWorld::Cell& cellVariant) const
{
return ESM::visit(ESM::VisitOverload{
[&](const ESM::Cell& cell) { return search(cell); },
[&](const ESM4::Cell& cell) -> const ESM::Pathgrid* { return nullptr; },
},
cellVariant);
}
const ESM::Pathgrid* Store<ESM::Pathgrid>::find(const ESM::Cell& cell) const const ESM::Pathgrid* Store<ESM::Pathgrid>::find(const ESM::Cell& cell) const
{ {
if (!(cell.mData.mFlags & ESM::Cell::Interior)) if (!(cell.mData.mFlags & ESM::Cell::Interior))
@ -1170,18 +1180,29 @@ namespace MWWorld
return mKeywordSearch; return mKeywordSearch;
} }
ESM::FixedString<6> getRecNameString(ESM::RecNameInts recName) // ESM4 Cell
//=========================================================================
const ESM4::Cell* Store<ESM4::Cell>::searchCellName(std::string_view cellName) const
{ {
ESM::FixedString<6> name; const auto foundCell = mCellNameIndex.find(cellName);
name.assign(""); if (foundCell == mCellNameIndex.end())
ESM::NAME fourCCName(recName & ~ESM::sEsm4RecnameFlag); return nullptr;
for (int i = 0; i < 4; i++) return foundCell->second;
name.mData[i] = fourCCName.mData[i]; }
if (ESM::isESM4Rec(recName))
{ ESM4::Cell* Store<ESM4::Cell>::insert(const ESM4::Cell& item, bool overrideOnly)
name.mData[4] = '4'; {
} auto cellPtr = TypedDynamicStore<ESM4::Cell>::insert(item, overrideOnly);
return name; mCellNameIndex[cellPtr->mEditorId] = cellPtr;
return cellPtr;
}
ESM4::Cell* Store<ESM4::Cell>::insertStatic(const ESM4::Cell& item)
{
auto cellPtr = TypedDynamicStore<ESM4::Cell>::insertStatic(item);
mCellNameIndex[cellPtr->mEditorId] = cellPtr;
return cellPtr;
} }
} }

View file

@ -15,6 +15,7 @@
#include <components/esm3/loadgmst.hpp> #include <components/esm3/loadgmst.hpp>
#include <components/esm3/loadland.hpp> #include <components/esm3/loadland.hpp>
#include <components/esm3/loadpgrd.hpp> #include <components/esm3/loadpgrd.hpp>
#include <components/esm4/loadcell.hpp>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/misc/strings/algorithm.hpp> #include <components/misc/strings/algorithm.hpp>
@ -38,6 +39,7 @@ namespace Loading
namespace MWWorld namespace MWWorld
{ {
class Cell;
struct RecordId struct RecordId
{ {
ESM::RefId mId; ESM::RefId mId;
@ -268,6 +270,19 @@ namespace MWWorld
const ESM::GameSetting* find(const std::string_view id) const; const ESM::GameSetting* find(const std::string_view id) const;
const ESM::GameSetting* search(const std::string_view id) const; const ESM::GameSetting* search(const std::string_view id) const;
}; };
template <>
class Store<ESM4::Cell> : public TypedDynamicStore<ESM4::Cell>
{
std::unordered_map<std::string, ESM4::Cell*, Misc::StringUtils::CiHash, Misc::StringUtils::CiEqual>
mCellNameIndex;
public:
const ESM4::Cell* searchCellName(std::string_view) const;
ESM4::Cell* insert(const ESM4::Cell& item, bool overrideOnly = false);
ESM4::Cell* insertStatic(const ESM4::Cell& item);
};
template <> template <>
class Store<ESM::Land> : public DynamicStore class Store<ESM::Land> : public DynamicStore
{ {
@ -416,6 +431,7 @@ namespace MWWorld
const ESM::Pathgrid* find(int x, int y) const; const ESM::Pathgrid* find(int x, int y) const;
const ESM::Pathgrid* find(const ESM::RefId& name) const; const ESM::Pathgrid* find(const ESM::RefId& name) const;
const ESM::Pathgrid* search(const ESM::Cell& cell) const; const ESM::Pathgrid* search(const ESM::Cell& cell) const;
const ESM::Pathgrid* search(const MWWorld::Cell& cell) const;
const ESM::Pathgrid* find(const ESM::Cell& cell) const; const ESM::Pathgrid* find(const ESM::Cell& cell) const;
}; };
@ -519,8 +535,6 @@ namespace MWWorld
const MWDialogue::KeywordSearch<std::string, int>& getDialogIdKeywordSearch() const; const MWDialogue::KeywordSearch<std::string, int>& getDialogIdKeywordSearch() const;
}; };
ESM::FixedString<6> getRecNameString(ESM::RecNameInts recName);
} // end namespace } // end namespace
#endif #endif

View file

@ -704,7 +704,7 @@ namespace MWWorld
if (!paused || mFastForward) if (!paused || mFastForward)
{ {
// Add new transitions when either the player's current external region changes. // Add new transitions when either the player's current external region changes.
if (updateWeatherTime() || updateWeatherRegion(player.getCell()->getCell()->mRegion)) if (updateWeatherTime() || updateWeatherRegion(player.getCell()->getCell()->getRegion()))
{ {
auto it = mRegions.find(mCurrentRegion); auto it = mRegions.find(mCurrentRegion);
if (it != mRegions.end()) if (it != mRegions.end())

View file

@ -23,6 +23,8 @@
#include <components/esm3/loadmgef.hpp> #include <components/esm3/loadmgef.hpp>
#include <components/esm3/loadregn.hpp> #include <components/esm3/loadregn.hpp>
#include <components/esm3/loadstat.hpp> #include <components/esm3/loadstat.hpp>
#include <components/esm4/loadcell.hpp>
#include <components/esm4/loadstat.hpp>
#include <components/misc/constants.hpp> #include <components/misc/constants.hpp>
#include <components/misc/convert.hpp> #include <components/misc/convert.hpp>
@ -631,7 +633,21 @@ namespace MWWorld
{ {
if (!cell) if (!cell)
cell = mWorldScene->getCurrentCell(); cell = mWorldScene->getCurrentCell();
return getCellName(cell->getCell()); return getCellName(*cell->getCell());
}
std::string_view World::getCellName(const MWWorld::Cell& cell) const
{
if (!cell.isExterior() || !cell.getNameId().empty())
return cell.getNameId();
return ESM::visit(ESM::VisitOverload{
[&](const ESM::Cell& cellIn) -> std::string_view { return getCellName(&cellIn); },
[&](const ESM4::Cell& cellIn) -> std::string_view {
return mStore.get<ESM::GameSetting>().find("sDefaultCellname")->mValue.getString();
},
},
cell);
} }
std::string_view World::getCellName(const ESM::Cell* cell) const std::string_view World::getCellName(const ESM::Cell* cell) const
@ -644,7 +660,6 @@ namespace MWWorld
if (const ESM::Region* region = mStore.get<ESM::Region>().search(cell->mRegion)) if (const ESM::Region* region = mStore.get<ESM::Region>().search(cell->mRegion))
return region->mName; return region->mName;
} }
return mStore.get<ESM::GameSetting>().find("sDefaultCellname")->mValue.getString(); return mStore.get<ESM::GameSetting>().find("sDefaultCellname")->mValue.getString();
} }
@ -1136,7 +1151,7 @@ namespace MWWorld
{ {
if (!newCell->isExterior()) if (!newCell->isExterior())
{ {
changeToInteriorCell(newCell->getCell()->mName, pos, false); changeToInteriorCell(newCell->getCell()->getNameId(), pos, false);
removeContainerScripts(getPlayerPtr()); removeContainerScripts(getPlayerPtr());
} }
else else
@ -1397,7 +1412,7 @@ namespace MWWorld
esmPos.pos[2] = traced.z(); esmPos.pos[2] = traced.z();
std::string_view cell; std::string_view cell;
if (!actor.getCell()->isExterior()) if (!actor.getCell()->isExterior())
cell = actor.getCell()->getCell()->mName; cell = actor.getCell()->getCell()->getNameId();
MWWorld::ActionTeleport(cell, esmPos, false).execute(actor); MWWorld::ActionTeleport(cell, esmPos, false).execute(actor);
} }
} }
@ -1985,10 +2000,7 @@ namespace MWWorld
const CellStore* currentCell = mWorldScene->getCurrentCell(); const CellStore* currentCell = mWorldScene->getCurrentCell();
if (currentCell) if (currentCell)
{ {
if (!(currentCell->getCell()->mData.mFlags & ESM::Cell::QuasiEx)) return currentCell->getCell()->isQuasiExterior();
return false;
else
return true;
} }
return false; return false;
} }
@ -2465,8 +2477,7 @@ namespace MWWorld
|| isFlying(player)) || isFlying(player))
return Rest_PlayerIsInAir; return Rest_PlayerIsInAir;
if ((currentCell->getCell()->mData.mFlags & ESM::Cell::NoSleep) if (currentCell->getCell()->noSleep() || player.getClass().getNpcStats(player).isWerewolf())
|| player.getClass().getNpcStats(player).isWerewolf())
return Rest_OnlyWaiting; return Rest_OnlyWaiting;
return Rest_Allowed; return Rest_Allowed;
@ -2803,6 +2814,24 @@ namespace MWWorld
} }
} }
} }
for (const MWWorld::LiveCellRef<ESM4::Static>& stat4 : cellStore->getReadOnlyEsm4Statics().mList)
{
if (Misc::StringUtils::lowerCase(stat4.mBase->mEditorId) == "cocmarkerheading")
{
// found the COC position?
pos = stat4.mRef.getPosition();
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
return true;
}
}
// Fall back to the first static location.
const MWWorld::CellRefList<ESM4::Static>::List& statics4 = cellStore->getReadOnlyEsm4Statics().mList;
if (!statics4.empty())
{
pos = statics4.begin()->mRef.getPosition();
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
return true;
}
// Fall back to the first static location. // Fall back to the first static location.
const MWWorld::CellRefList<ESM::Static>::List& statics = cellStore->getReadOnlyStatics().mList; const MWWorld::CellRefList<ESM::Static>::List& statics = cellStore->getReadOnlyStatics().mList;
if (!statics.empty()) if (!statics.empty())
@ -2819,7 +2848,7 @@ namespace MWWorld
{ {
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
const ESM::Cell* ext = nullptr; const MWWorld::Cell* ext = nullptr;
try try
{ {
ext = mWorldModel.getCell(nameId)->getCell(); ext = mWorldModel.getCell(nameId)->getCell();
@ -3226,9 +3255,10 @@ namespace MWWorld
} }
else else
{ {
uint32_t ambient = cell->getCell()->mAmbi.mAmbient; const MWWorld::Cell& cellVariant = *cell->getCell();
uint32_t ambient = cellVariant.getMood().mAmbiantColor;
int ambientTotal = (ambient & 0xff) + ((ambient >> 8) & 0xff) + ((ambient >> 16) & 0xff); int ambientTotal = (ambient & 0xff) + ((ambient >> 8) & 0xff) + ((ambient >> 16) & 0xff);
return !(cell->getCell()->mData.mFlags & ESM::Cell::NoSleep) && ambientTotal <= 201; return !cell->getCell()->noSleep() && ambientTotal <= 201;
} }
} }
@ -3252,7 +3282,7 @@ namespace MWWorld
std::set<std::string_view> checkedCells; std::set<std::string_view> checkedCells;
std::set<std::string_view> currentCells; std::set<std::string_view> currentCells;
std::set<std::string_view> nextCells; std::set<std::string_view> nextCells;
nextCells.insert(cell->getCell()->mName); nextCells.insert(cell->getCell()->getNameId());
while (!nextCells.empty()) while (!nextCells.empty())
{ {
@ -3307,7 +3337,7 @@ namespace MWWorld
std::set<std::string_view> nextCells; std::set<std::string_view> nextCells;
MWWorld::ConstPtr closestMarker; MWWorld::ConstPtr closestMarker;
nextCells.insert(ptr.getCell()->getCell()->mName); nextCells.insert(ptr.getCell()->getCell()->getNameId());
while (!nextCells.empty()) while (!nextCells.empty())
{ {
currentCells = nextCells; currentCells = nextCells;
@ -3402,7 +3432,7 @@ namespace MWWorld
std::string_view cellName = ""; std::string_view cellName = "";
if (!closestMarker.mCell->isExterior()) if (!closestMarker.mCell->isExterior())
cellName = closestMarker.mCell->getCell()->mName; cellName = closestMarker.mCell->getCell()->getNameId();
MWWorld::ActionTeleport action(cellName, closestMarker.getRefData().getPosition(), false); MWWorld::ActionTeleport action(cellName, closestMarker.getRefData().getPosition(), false);
action.execute(ptr); action.execute(ptr);
@ -3415,7 +3445,7 @@ namespace MWWorld
{ {
mPlayer->setTeleported(false); mPlayer->setTeleported(false);
const ESM::RefId& playerRegion = getPlayerPtr().getCell()->getCell()->mRegion; const ESM::RefId& playerRegion = getPlayerPtr().getCell()->getCell()->getRegion();
mWeatherManager->playerTeleported(playerRegion, isExterior); mWeatherManager->playerTeleported(playerRegion, isExterior);
} }

View file

@ -269,6 +269,7 @@ namespace MWWorld
/// ///
/// \note If cell==0, the cell the player is currently in will be used instead to /// \note If cell==0, the cell the player is currently in will be used instead to
/// generate a name. /// generate a name.
std::string_view getCellName(const MWWorld::Cell& cell) const override;
std::string_view getCellName(const ESM::Cell* cell) const override; std::string_view getCellName(const ESM::Cell* cell) const override;
void removeRefScript(MWWorld::RefData* ref) override; void removeRefScript(MWWorld::RefData* ref) override;

View file

@ -67,7 +67,7 @@ MWWorld::CellStore* MWWorld::WorldModel::getCellStore(const ESM::Cell* cell)
auto result = mInteriors.find(cell->mName); auto result = mInteriors.find(cell->mName);
if (result == mInteriors.end()) if (result == mInteriors.end())
result = mInteriors.emplace(cell->mName, CellStore(cell, mStore, mReaders)).first; result = mInteriors.emplace(cell->mName, CellStore(MWWorld::Cell(*cell), mStore, mReaders)).first;
return &result->second; return &result->second;
} }
@ -78,7 +78,8 @@ MWWorld::CellStore* MWWorld::WorldModel::getCellStore(const ESM::Cell* cell)
if (result == mExteriors.end()) if (result == mExteriors.end())
result = mExteriors result = mExteriors
.emplace(std::make_pair(cell->getGridX(), cell->getGridY()), CellStore(cell, mStore, mReaders)) .emplace(std::make_pair(cell->getGridX(), cell->getGridY()),
CellStore(MWWorld::Cell(*cell), mStore, mReaders))
.first; .first;
return &result->second; return &result->second;
@ -185,7 +186,7 @@ MWWorld::CellStore* MWWorld::WorldModel::getExterior(int x, int y)
cell = MWBase::Environment::get().getWorld()->createRecord(record); cell = MWBase::Environment::get().getWorld()->createRecord(record);
} }
result = mExteriors.emplace(std::make_pair(x, y), CellStore(cell, mStore, mReaders)).first; result = mExteriors.emplace(std::make_pair(x, y), CellStore(MWWorld::Cell(*cell), mStore, mReaders)).first;
} }
if (result->second.getState() != CellStore::State_Loaded) if (result->second.getState() != CellStore::State_Loaded)
@ -202,9 +203,17 @@ MWWorld::CellStore* MWWorld::WorldModel::getInterior(std::string_view name)
if (result == mInteriors.end()) if (result == mInteriors.end())
{ {
const ESM::Cell* cell = mStore.get<ESM::Cell>().find(name); const ESM4::Cell* cell4 = mStore.get<ESM4::Cell>().searchCellName(name);
result = mInteriors.emplace(name, CellStore(cell, mStore, mReaders)).first; if (!cell4)
{
const ESM::Cell* cell = mStore.get<ESM::Cell>().find(name);
result = mInteriors.emplace(name, CellStore(MWWorld::Cell(*cell), mStore, mReaders)).first;
}
else
{
result = mInteriors.emplace(name, CellStore(MWWorld::Cell(*cell4), mStore, mReaders)).first;
}
} }
if (result->second.getState() != CellStore::State_Loaded) if (result->second.getState() != CellStore::State_Loaded)
@ -253,6 +262,17 @@ const ESM::Cell* MWWorld::WorldModel::getESMCellByName(std::string_view name)
return cell; 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) MWWorld::CellStore* MWWorld::WorldModel::getCell(std::string_view name)
{ {
const ESM::Cell* cell = getESMCellByName(name); const ESM::Cell* cell = getESMCellByName(name);

View file

@ -5,6 +5,7 @@
#include <map> #include <map>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <variant>
#include <components/misc/algorithm.hpp> #include <components/misc/algorithm.hpp>
@ -21,6 +22,11 @@ namespace ESM
struct RefNum; struct RefNum;
} }
namespace ESM4
{
struct Cell;
}
namespace Loading namespace Loading
{ {
class Listener; class Listener;
@ -45,8 +51,9 @@ namespace MWWorld
WorldModel& operator=(const WorldModel&); WorldModel& operator=(const WorldModel&);
const ESM::Cell* getESMCellByName(std::string_view name); const ESM::Cell* getESMCellByName(std::string_view name);
CellStore* getCellStore(const ESM::Cell* cell); ESM::CellVariant getCellByName(std::string_view name);
CellStore* getCellStore(const ESM::Cell* cell);
Ptr getPtrAndCache(const ESM::RefId& name, CellStore& cellStore); Ptr getPtrAndCache(const ESM::RefId& name, CellStore& cellStore);
Ptr getPtr(CellStore& cellStore, const ESM::RefId& id, const ESM::RefNum& refNum); Ptr getPtr(CellStore& cellStore, const ESM::RefId& id, const ESM::RefNum& refNum);

View file

@ -5,6 +5,7 @@
#include <boost/program_options/options_description.hpp> #include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp> #include <boost/program_options/variables_map.hpp>
#include <components/esm/defs.hpp>
#include <components/esm/records.hpp> #include <components/esm/records.hpp>
#include <components/esm3/esmreader.hpp> #include <components/esm3/esmreader.hpp>
#include <components/esm3/esmwriter.hpp> #include <components/esm3/esmwriter.hpp>
@ -316,7 +317,7 @@ static void testRecNameIntCount(const MWWorld::Store<T>& store, const MWWorld::E
const unsigned int recordIdCount const unsigned int recordIdCount
= std::apply([](auto&&... x) { return (hasSameRecordId(x, T::sRecordId) + ...); }, stores); = std::apply([](auto&&... x) { return (hasSameRecordId(x, T::sRecordId) + ...); }, stores);
ASSERT_EQ(recordIdCount, static_cast<unsigned int>(1)) ASSERT_EQ(recordIdCount, static_cast<unsigned int>(1))
<< "The same RecNameInt is used twice ESM::REC_" << MWWorld::getRecNameString(T::sRecordId).toStringView(); << "The same RecNameInt is used twice ESM::REC_" << ESM::getRecNameString(T::sRecordId).toStringView();
} }
} }

View file

@ -80,7 +80,7 @@ add_component_dir (to_utf8
to_utf8 to_utf8
) )
add_component_dir(esm attr common defs esmcommon records util luascripts format refid) add_component_dir(esm attr common defs esmcommon records util luascripts format refid esmbridge)
add_component_dir(fx pass technique lexer widgets stateupdater) add_component_dir(fx pass technique lexer widgets stateupdater)

View file

@ -9,6 +9,7 @@
#include <osg/Vec3f> #include <osg/Vec3f>
#include "components/esm/fourcc.hpp" #include "components/esm/fourcc.hpp"
#include <components/esm/esmcommon.hpp>
#include <components/esm4/common.hpp> #include <components/esm4/common.hpp>
namespace ESM namespace ESM
@ -338,6 +339,20 @@ namespace ESM
return RecName & sEsm4RecnameFlag; return RecName & sEsm4RecnameFlag;
} }
inline FixedString<6> getRecNameString(ESM::RecNameInts recName)
{
ESM::FixedString<6> name;
name.assign("");
ESM::NAME fourCCName(recName & ~ESM::sEsm4RecnameFlag);
for (int i = 0; i < 4; i++)
name.mData[i] = fourCCName.mData[i];
if (ESM::isESM4Rec(recName))
{
name.mData[4] = '4';
}
return name;
}
/// Common subrecords /// Common subrecords
enum SubRecNameInts enum SubRecNameInts
{ {

View file

@ -0,0 +1,18 @@
#include <components/esm/esmbridge.hpp>
#include <components/esm3/loadcell.hpp>
#include <components/esm4/loadcell.hpp>
namespace ESM
{
const ESM4::Cell& CellVariant::getEsm4() const
{
auto cell4 = std::get<const ESM4::Cell*>(mVariant);
return *cell4;
}
const ESM::Cell& CellVariant::getEsm3() const
{
auto cell = std::get<const ESM::Cell*>(mVariant);
return *cell;
}
}

View file

@ -0,0 +1,83 @@
#ifndef COMPONENTS_ESM_ESMBRIDGE
#define COMPONENTS_ESM_ESMBRIDGE
#include <string>
#include <string_view>
#include <variant>
#include <components/esm3/cellref.hpp>
#include <components/esm4/loadrefr.hpp>
namespace ESM4
{
struct Cell;
}
namespace ESM
{
struct Cell;
struct CellId;
struct RefId;
class CellVariant
{
protected:
std::variant<const ESM4::Cell*, const ESM::Cell*> mVariant;
public:
explicit CellVariant(const ESM4::Cell& cell)
: mVariant(&cell)
{
}
explicit CellVariant(const ESM::Cell& cell)
: mVariant(&cell)
{
}
bool isEsm4() const { return std::holds_alternative<const ESM4::Cell*>(mVariant); }
const ESM4::Cell& getEsm4() const;
const ESM::Cell& getEsm3() const;
template <class F, class... T>
friend auto visit(F&& f, T&&... v);
};
struct ReferenceVariant
{
std::variant<ESM::CellRef, ESM4::Reference> mVariant;
explicit ReferenceVariant(const ESM4::Reference& ref)
: mVariant(ref)
{
}
explicit ReferenceVariant(const ESM::CellRef& ref)
: mVariant(ref)
{
}
bool isESM4() const { return std::holds_alternative<ESM4::Reference>(mVariant); }
const ESM::CellRef& getEsm3() const { return std::get<ESM::CellRef>(mVariant); }
const ESM4::Reference& getEsm4() const { return std::get<ESM4::Reference>(mVariant); }
ESM::CellRef& getEsm3() { return std::get<ESM::CellRef>(mVariant); }
ESM4::Reference& getEsm4() { return std::get<ESM4::Reference>(mVariant); }
};
template <class F, class... T>
auto visit(F&& f, T&&... v)
{
return std::visit([&](auto*... ptr) { return std::forward<F>(f)(*ptr...); }, std::forward<T>(v).mVariant...);
}
template <class... Ts>
struct VisitOverload : Ts...
{
using Ts::operator()...;
};
template <class... Ts>
VisitOverload(Ts...) -> VisitOverload<Ts...>;
}
#endif

View file

@ -36,6 +36,7 @@
#include <components/esm/defs.hpp> #include <components/esm/defs.hpp>
#include <components/esm/refid.hpp> #include <components/esm/refid.hpp>
#include <components/esm3/cellid.hpp>
namespace ESM4 namespace ESM4
{ {
@ -101,6 +102,10 @@ namespace ESM4
void blank(); void blank();
static constexpr ESM::RecNameInts sRecordId = ESM::REC_CELL4; static constexpr ESM::RecNameInts sRecordId = ESM::REC_CELL4;
int getGridX() const { return mX; }
int getGridY() const { return mY; }
bool isExterior() const { return !(mCellFlags & CELL_Interior); }
}; };
} }

View file

@ -77,7 +77,7 @@ void ESM4::Reference::load(ESM4::Reader& reader)
break; break;
} }
case ESM4::SUB_DATA: case ESM4::SUB_DATA:
reader.get(mPlacement); reader.get(mPos);
break; break;
case ESM4::SUB_XSCL: case ESM4::SUB_XSCL:
reader.get(mScale); reader.get(mScale);
@ -236,7 +236,9 @@ void ESM4::Reference::load(ESM4::Reader& reader)
reader.get(dummy); reader.get(dummy);
reader.get(dummy); reader.get(dummy);
reader.get(dummy); reader.get(dummy);
reader.getFormId(mKey); FormId keyForm;
reader.getFormId(keyForm);
mKey = ESM::RefId::formIdRefId(keyForm);
reader.get(dummy); // flag? reader.get(dummy); // flag?
reader.get(dummy); reader.get(dummy);
reader.get(dummy); reader.get(dummy);

View file

@ -59,7 +59,7 @@ namespace ESM4
struct TeleportDest struct TeleportDest
{ {
FormId destDoor; FormId destDoor;
Placement destPos; ESM::Position destPos;
std::uint32_t flags; // 0x01 no alarm (only in TES5) std::uint32_t flags; // 0x01 no alarm (only in TES5)
}; };
@ -87,7 +87,7 @@ namespace ESM4
std::string mFullName; std::string mFullName;
ESM::RefId mBaseObj; ESM::RefId mBaseObj;
Placement mPlacement; ESM::Position mPos;
float mScale = 1.0f; float mScale = 1.0f;
FormId mOwner; FormId mOwner;
FormId mGlobal; FormId mGlobal;
@ -108,7 +108,7 @@ namespace ESM4
TeleportDest mDoor; TeleportDest mDoor;
bool mIsLocked; bool mIsLocked;
std::int8_t mLockLevel; std::int8_t mLockLevel;
FormId mKey; ESM::RefId mKey;
FormId mTargetRef; FormId mTargetRef;

View file

@ -1,9 +1,11 @@
#ifndef OPENMW_COMPONENTS_MISC_COORDINATECONVERTER_H #ifndef OPENMW_COMPONENTS_MISC_COORDINATECONVERTER_H
#define OPENMW_COMPONENTS_MISC_COORDINATECONVERTER_H #define OPENMW_COMPONENTS_MISC_COORDINATECONVERTER_H
#include <components/esm/esmbridge.hpp>
#include <components/esm3/loadcell.hpp> #include <components/esm3/loadcell.hpp>
#include <components/esm3/loadland.hpp> #include <components/esm3/loadland.hpp>
#include <components/esm3/loadpgrd.hpp> #include <components/esm3/loadpgrd.hpp>
#include <components/esm4/loadcell.hpp>
namespace Misc namespace Misc
{ {
@ -17,8 +19,15 @@ namespace Misc
{ {
} }
explicit CoordinateConverter(const ESM::CellVariant& cell)
: CoordinateConverter(cell.isEsm4() ? cell.getEsm4().isExterior() : cell.getEsm3().isExterior(),
cell.isEsm4() ? cell.getEsm4().getGridX() : cell.getEsm3().getGridX(),
cell.isEsm4() ? cell.getEsm4().getGridY() : cell.getEsm3().getGridY())
{
}
explicit CoordinateConverter(const ESM::Cell* cell) explicit CoordinateConverter(const ESM::Cell* cell)
: CoordinateConverter(cell->isExterior(), cell->mData.mX, cell->mData.mY) : CoordinateConverter(cell->isExterior(), cell->getGridX(), cell->getGridY())
{ {
} }