Gets rid of most ESM::CellId

depth-refraction
florent.teppe 2 years ago
parent 6895a452ef
commit c39dd576f8

@ -220,7 +220,7 @@ namespace EsmTool
{
void CellState::load(ESM::ESMReader& reader, bool& deleted)
{
mCellState.mId.load(reader);
mCellState.mId = reader.getCellId();
mCellState.load(reader);
if (mCellState.mHasFogOfWar)
mFogState.load(reader);
@ -1358,11 +1358,8 @@ namespace EsmTool
void Record<CellState>::print()
{
std::cout << " Id:" << std::endl;
std::cout << " Worldspace: " << mData.mCellState.mId.mWorldspace << std::endl;
std::cout << " CellId: " << mData.mCellState.mId << std::endl;
std::cout << " Index:" << std::endl;
std::cout << " X: " << mData.mCellState.mId.mIndex.mX << std::endl;
std::cout << " Y: " << mData.mCellState.mId.mIndex.mY << std::endl;
std::cout << " Paged: " << mData.mCellState.mId.mPaged << std::endl;
std::cout << " WaterLevel: " << mData.mCellState.mWaterLevel << std::endl;
std::cout << " HasFogOfWar: " << mData.mCellState.mHasFogOfWar << std::endl;
std::cout << " LastRespawn:" << std::endl;
@ -1420,8 +1417,7 @@ namespace EsmTool
std::string Record<CellState>::getId() const
{
std::ostringstream stream;
stream << mData.mCellState.mId.mWorldspace << " " << mData.mCellState.mId.mIndex.mX << " "
<< mData.mCellState.mId.mIndex.mY << " " << mData.mCellState.mId.mPaged;
stream << mData.mCellState.mId;
return stream.str();
}

@ -204,7 +204,7 @@ namespace ESSImport
// note if the player is in a nameless exterior cell, we will assign the cellId later based on player position
if (Misc::StringUtils::ciEqual(cell.mName, mContext->mPlayerCellName))
{
mContext->mPlayer.mCellId = cell.getCellId();
mContext->mPlayer.mCellId = cell.mId;
}
Cell newcell;
@ -301,8 +301,7 @@ namespace ESSImport
marker.mWorldX = notepos[0];
marker.mWorldY = notepos[1];
marker.mNote = note;
marker.mCellId = cell.getCellId();
marker.mCell = cell.getCellId().getCellRefId();
marker.mCell = cell.mId;
mMarkers.push_back(marker);
}
@ -322,8 +321,9 @@ namespace ESSImport
csta.mHasFogOfWar = 0;
csta.mLastRespawn.mDay = 0;
csta.mLastRespawn.mHour = 0;
csta.mId = esmcell.getCellId();
csta.mId.save(esm);
csta.mId = esmcell.mId;
csta.mIsInterior = !esmcell.isExterior();
esm.writeCellId(csta.mId);
// TODO csta.mLastRespawn;
// shouldn't be needed if we respawn on global schedule like in original MW
csta.mWaterLevel = esmcell.mWater;

@ -61,7 +61,6 @@ namespace ESSImport
const PCDT::PNAM::MarkLocation& mark = pcdt.mPNAM.mMarkLocation;
ESM::CellId cell;
cell.mWorldspace = ESM::CellId::sDefaultWorldspace;
cell.mPaged = true;
cell.mIndex.mX = mark.mCellX;
@ -74,7 +73,7 @@ namespace ESSImport
cell.mPaged = false;
}
out.mMarkedCell = cell;
out.mMarkedCell = cell.getCellRefId();
out.mMarkedPosition.pos[0] = mark.mX;
out.mMarkedPosition.pos[1] = mark.mY;
out.mMarkedPosition.pos[2] = mark.mZ;

@ -409,16 +409,18 @@ namespace ESSImport
}
writer.startRecord(ESM::REC_PLAY);
if (context.mPlayer.mCellId.mPaged)
ESM::CellId cellId = ESM::CellId::extractFromRefId(context.mPlayer.mCellId);
if (cellId.mPaged)
{
// exterior cell -> determine cell coordinates based on position
int cellX
= static_cast<int>(std::floor(context.mPlayer.mObject.mPosition.pos[0] / Constants::CellSizeInUnits));
int cellY
= static_cast<int>(std::floor(context.mPlayer.mObject.mPosition.pos[1] / Constants::CellSizeInUnits));
context.mPlayer.mCellId.mIndex.mX = cellX;
context.mPlayer.mCellId.mIndex.mY = cellY;
cellId.mIndex.mX = cellX;
cellId.mIndex.mY = cellY;
}
context.mPlayer.mCellId = cellId.getCellRefId();
context.mPlayer.save(writer);
writer.endRecord(ESM::REC_PLAY);

@ -62,7 +62,7 @@ namespace ESSImport
ESM::CellId playerCellId;
playerCellId.mPaged = true;
playerCellId.mIndex.mX = playerCellId.mIndex.mY = 0;
mPlayer.mCellId = playerCellId;
mPlayer.mCellId = playerCellId.getCellRefId();
mPlayer.mLastKnownExteriorPosition[0] = mPlayer.mLastKnownExteriorPosition[1]
= mPlayer.mLastKnownExteriorPosition[2] = 0.0f;
mPlayer.mHasMark = 0;

@ -263,7 +263,8 @@ namespace NavMeshTool
const osg::Vec2i cellPosition(cell.mData.mX, cell.mData.mY);
const std::size_t cellObjectsBegin = data.mObjects.size();
const auto cellNameLowerCase = Misc::StringUtils::lowerCase(cell.mCellId.mWorldspace);
const auto cellNameLowerCase
= Misc::StringUtils::lowerCase(cell.isExterior() ? ESM::CellId::sDefaultWorldspace : cell.mName);
WorldspaceNavMeshInput& navMeshInput = [&]() -> WorldspaceNavMeshInput& {
auto it = navMeshInputs.find(cellNameLowerCase);
if (it == navMeshInputs.end())

@ -40,7 +40,6 @@ namespace ESM
{
class ESMReader;
class ESMWriter;
struct CellId;
}
namespace MWMechanics

@ -256,10 +256,6 @@ namespace MWBase
= 0;
///< Move to exterior cell.
///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes
virtual void changeToCell(
const ESM::CellId& cellId, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent = true)
= 0;
virtual void changeToCell(
const ESM::RefId& cellId, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent = true)
= 0;

@ -897,7 +897,6 @@ namespace MWGui
clickedId.mIndex.mY = y;
}
mEditingMarker.mCell = clickedId.getCellRefId();
mEditingMarker.mCellId = clickedId;
mEditNoteDialog.setVisible(true);
mEditNoteDialog.showDeleteButton(false);

@ -5,12 +5,10 @@
#include <components/esm/esmbridge.hpp>
#include <components/esm/refid.hpp>
#include <components/esm3/cellid.hpp>
namespace ESM
{
struct Cell;
struct CellId;
}
namespace ESM4

@ -90,11 +90,11 @@ namespace MWWorld
else
{
const osg::Vec2i index = positionToCellIndex(ref.mDoorDest.pos[0], ref.mDoorDest.pos[1]);
ESM::CellId CellId;
CellId.mPaged = true;
CellId.mIndex.mX = index.x();
CellId.mIndex.mY = index.y();
return CellId.getCellRefId();
ESM::CellId cellId;
cellId.mPaged = true;
cellId.mIndex.mX = index.x();
cellId.mIndex.mY = index.y();
return cellId.getCellRefId();
}
};

@ -988,11 +988,11 @@ namespace MWWorld
void CellStore::saveState(ESM::CellState& state) const
{
state.mId = ESM::CellId::extractFromRefId(mCellVariant.getId());
state.mId = mCellVariant.getId();
if (!mCellVariant.isExterior() && mCellVariant.hasWater())
state.mWaterLevel = mWaterLevel;
state.mIsInterior = !mCellVariant.isExterior();
state.mHasFogOfWar = (mFogState.get() ? 1 : 0);
state.mLastRespawn = mLastRespawn.toEsm();
}
@ -1019,10 +1019,10 @@ namespace MWWorld
for (const auto& [base, store] : mMovedToAnotherCell)
{
ESM::RefNum refNum = base->mRef.getRefNum();
ESM::CellId movedTo = ESM::CellId::extractFromRefId(store->getCell()->getId());
ESM::RefId movedTo = store->getCell()->getId();
refNum.save(writer, true, "MVRF");
movedTo.save(writer);
writer.writeCellId(movedTo);
}
}
@ -1078,10 +1078,8 @@ namespace MWWorld
{
reader.cacheSubName();
ESM::RefNum refnum;
ESM::CellId movedTo;
refnum.load(reader, true, "MVRF");
movedTo.load(reader);
ESM::RefId movedToId = movedTo.getCellRefId();
ESM::RefId movedToId = reader.getCellId();
if (refnum.hasContentFile())
{
auto iter = contentFileMap.find(refnum.mContentFile);
@ -1103,7 +1101,7 @@ namespace MWWorld
if (otherCell == nullptr)
{
Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << movedRef.getCellRef().getRefId()
<< " (target cell " << movedTo.mWorldspace
<< " (target cell " << movedToId
<< " 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 at inapproriate coordinates. Restore original coordinates:

@ -27,7 +27,6 @@ namespace ESM
class ReadersCache;
struct Cell;
struct CellState;
struct CellId;
struct RefNum;
struct Activator;
struct Potion;

@ -51,7 +51,7 @@ namespace MWWorld
{
if (!cell.isExterior())
continue;
auto cellIndex = std::make_pair(cell.getCellId().mIndex.mX, cell.getCellId().mIndex.mY);
auto cellIndex = std::make_pair(cell.getGridX(), cell.getGridY());
mCellContexts[cellIndex] = std::move(cell.mContextList);
}
}

@ -283,7 +283,7 @@ namespace MWWorld
ESM::Player player;
mPlayer.save(player.mObject);
player.mCellId = ESM::CellId::extractFromRefId(mCellStore->getCell()->getId());
player.mCellId = mCellStore->getCell()->getId();
player.mCurrentCrimeId = mCurrentCrimeId;
player.mPaidCrimeId = mPaidCrimeId;
@ -298,7 +298,7 @@ namespace MWWorld
{
player.mHasMark = true;
player.mMarkedPosition = mMarkedPosition;
player.mMarkedCell = ESM::CellId::extractFromRefId(mMarkedCell->getCell()->getId());
player.mMarkedCell = mMarkedCell->getCell()->getId();
}
else
player.mHasMark = false;
@ -367,11 +367,11 @@ namespace MWWorld
try
{
mCellStore = MWBase::Environment::get().getWorldModel()->getCellFromCellId(player.mCellId);
mCellStore = MWBase::Environment::get().getWorldModel()->getCell(player.mCellId);
}
catch (...)
{
Log(Debug::Warning) << "Warning: Player cell '" << player.mCellId.mWorldspace << "' no longer exists";
Log(Debug::Warning) << "Warning: Player cell '" << player.mCellId << "' no longer exists";
// Cell no longer exists. The loader will have to choose a default cell.
mCellStore = nullptr;
}
@ -392,19 +392,20 @@ namespace MWWorld
mLastKnownExteriorPosition.y() = player.mLastKnownExteriorPosition[1];
mLastKnownExteriorPosition.z() = player.mLastKnownExteriorPosition[2];
if (player.mHasMark && !player.mMarkedCell.mPaged)
bool exterior = ESM::CellId::extractFromRefId(player.mMarkedCell).mPaged;
if (player.mHasMark && !exterior)
{
// interior cell -> need to check if it exists (exterior cell will be
// generated on the fly)
if (!world.getStore().get<ESM::Cell>().search(player.mMarkedCell.mWorldspace))
if (!world.getStore().get<ESM::Cell>().search(player.mMarkedCell))
player.mHasMark = false; // drop mark silently
}
if (player.mHasMark)
{
mMarkedPosition = player.mMarkedPosition;
mMarkedCell = MWBase::Environment::get().getWorldModel()->getCellFromCellId(player.mMarkedCell);
mMarkedCell = MWBase::Environment::get().getWorldModel()->getCell(player.mMarkedCell);
}
else
{

@ -575,9 +575,6 @@ namespace MWWorld
newCell.mAmbi.mSunlight = 0;
newCell.mAmbi.mFog = 0;
newCell.mAmbi.mFogDensity = 0;
newCell.mCellId.mPaged = true;
newCell.mCellId.mIndex.mX = x;
newCell.mCellId.mIndex.mY = y;
newCell.updateId();
ESM::Cell* newCellInserted = &mCells.insert(std::make_pair(newCell.mId, newCell)).first->second;

@ -380,10 +380,10 @@ namespace MWWorld
pos.rot[2] = 0;
osg::Vec2i exteriorCellPos = positionToCellIndex(pos.pos[0], pos.pos[1]);
ESM::CellId CellId;
CellId.mPaged = true;
CellId.mIndex = { exteriorCellPos.x(), exteriorCellPos.y() };
mWorldScene->changeToExteriorCell(CellId.getCellRefId(), pos, true);
ESM::CellId cellId;
cellId.mPaged = true;
cellId.mIndex = { exteriorCellPos.x(), exteriorCellPos.y() };
mWorldScene->changeToExteriorCell(cellId.getCellRefId(), pos, true);
}
}
@ -987,28 +987,14 @@ namespace MWWorld
}
removeContainerScripts(getPlayerPtr());
osg::Vec2i exteriorCellPos = positionToCellIndex(position.pos[0], position.pos[1]);
ESM::CellId CellId;
CellId.mPaged = true;
CellId.mIndex = { exteriorCellPos.x(), exteriorCellPos.y() };
mWorldScene->changeToExteriorCell(CellId.getCellRefId(), position, adjustPlayerPos, changeEvent);
ESM::CellId cellId;
cellId.mPaged = true;
cellId.mIndex = { exteriorCellPos.x(), exteriorCellPos.y() };
mWorldScene->changeToExteriorCell(cellId.getCellRefId(), position, adjustPlayerPos, changeEvent);
addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
mRendering->getCamera()->instantTransition();
}
void World::changeToCell(
const ESM::CellId& cellId, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)
{
if (!changeEvent)
mCurrentWorldSpace = cellId.mWorldspace;
if (cellId.mPaged)
changeToExteriorCell(position, adjustPlayerPos, changeEvent);
else
changeToInteriorCell(cellId.mWorldspace, position, adjustPlayerPos, changeEvent);
mCurrentDate->setup(mGlobalVariables);
}
void World::changeToCell(
const ESM::RefId& cellId, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)
{

@ -349,9 +349,6 @@ namespace MWWorld
///< Move to exterior cell.
///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes
void changeToCell(const ESM::CellId& cellId, const ESM::Position& position, bool adjustPlayerPos,
bool changeEvent = true) override;
void changeToCell(const ESM::RefId& cellId, const ESM::Position& position, bool adjustPlayerPos,
bool changeEvent = true) override;
///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes

@ -143,7 +143,8 @@ void MWWorld::WorldModel::writeCell(ESM::ESMWriter& writer, CellStore& cell) con
cell.saveState(cellState);
writer.startRecord(ESM::REC_CSTA);
cellState.mId.save(writer);
writer.writeCellId(cellState.mId);
cellState.save(writer);
cell.writeFog(writer);
cell.writeReferences(writer);
@ -170,11 +171,6 @@ MWWorld::CellStore* MWWorld::WorldModel::getExterior(int x, int y)
{
// Cell isn't predefined. Make one on the fly.
ESM::Cell record;
record.mCellId.mWorldspace = ESM::CellId::sDefaultWorldspace;
record.mCellId.mPaged = true;
record.mCellId.mIndex.mX = x;
record.mCellId.mIndex.mY = y;
record.mData.mFlags = ESM::Cell::HasWater;
record.mData.mX = x;
record.mData.mY = y;
@ -227,14 +223,6 @@ MWWorld::CellStore* MWWorld::WorldModel::getInterior(std::string_view name)
return result->second;
}
MWWorld::CellStore* MWWorld::WorldModel::getCellFromCellId(const ESM::CellId& id)
{
if (id.mPaged)
return getExterior(id.mIndex.mX, id.mIndex.mY);
return getInterior(id.mWorldspace);
}
MWWorld::CellStore* MWWorld::WorldModel::getCell(const ESM::RefId& id)
{
auto result = mCells.find(id);
@ -489,19 +477,18 @@ bool MWWorld::WorldModel::readRecord(ESM::ESMReader& reader, uint32_t type, cons
if (type == ESM::REC_CSTA)
{
ESM::CellState state;
state.mId.load(reader);
state.mId = reader.getCellId();
CellStore* cellStore = nullptr;
try
{
cellStore = getCell(state.mId.getCellRefId());
cellStore = getCell(state.mId);
}
catch (...)
{
// silently drop cells that don't exist anymore
Log(Debug::Warning) << "Warning: Dropping state for cell " << state.mId.mWorldspace
<< " (cell no longer exists)";
Log(Debug::Warning) << "Warning: Dropping state for cell " << state.mId << " (cell no longer exists)";
reader.skipRecord();
return true;
}

@ -17,7 +17,6 @@ namespace ESM
class ESMReader;
class ESMWriter;
class ReadersCache;
struct CellId;
struct Cell;
struct RefNum;
}
@ -71,7 +70,6 @@ namespace MWWorld
CellStore* getInterior(std::string_view name);
CellStore* getCell(std::string_view name); // interior or named exterior
CellStore* getCell(const ESM::RefId& Id);
CellStore* getCellFromCellId(const ESM::CellId& Id);
// If cellNameInSameWorldSpace is an interior - returns this interior.
// Otherwise returns exterior cell for given position in the same world space.

@ -15,7 +15,6 @@ namespace ESM4
namespace ESM
{
struct Cell;
struct CellId;
class RefId;
class CellVariant;

@ -21,7 +21,7 @@ namespace ESM
void CellState::save(ESMWriter& esm) const
{
if (!mId.mPaged)
if (mIsInterior)
esm.writeHNT("WLVL", mWaterLevel);
esm.writeHNT("HFOW", mHasFogOfWar);

@ -15,8 +15,8 @@ namespace ESM
/// \note Does not include references
struct CellState
{
CellId mId;
RefId mId;
bool mIsInterior;
float mWaterLevel;
int mHasFogOfWar; // Do we have fog of war state (0 or 1)? (see fogstate.hpp)

@ -10,7 +10,7 @@ namespace ESM
{
esm.writeHNT("POSX", mWorldX);
esm.writeHNT("POSY", mWorldY);
mCellId.save(esm);
esm.writeCellId(mCell);
if (!mNote.empty())
esm.writeHNString("NOTE", mNote);
}
@ -19,8 +19,7 @@ namespace ESM
{
esm.getHNT(mWorldX, "POSX");
esm.getHNT(mWorldY, "POSY");
mCellId.load(esm);
mCell = mCellId.getCellRefId();
mCell = esm.getCellId();
mNote = esm.getHNOString("NOTE");
}

@ -13,7 +13,6 @@ namespace ESM
float mWorldY;
RefId mCell;
CellId mCellId; // The CellId representation for saving/loading
std::string mNote;

@ -3,6 +3,7 @@
#include "readerscache.hpp"
#include "savedgame.hpp"
#include <components/esm3/cellid.hpp>
#include <components/files/conversion.hpp>
#include <components/files/openfile.hpp>
#include <components/misc/strings/algorithm.hpp>
@ -86,6 +87,13 @@ namespace ESM
}
}
ESM::RefId ESMReader::getCellId()
{
ESM::CellId cellId;
cellId.load(*this);
return cellId.getCellRefId();
}
void ESMReader::openRaw(std::unique_ptr<std::istream>&& stream, const std::filesystem::path& name)
{
close();

@ -96,6 +96,9 @@ namespace ESM
*
*************************************************************************/
// Because we want to get rid of CellId, we isolate it's uses.
ESM::RefId getCellId();
// Read data of a given type, stored in a subrecord of a given name
template <typename X>
void getHNT(X& x, NAME name)

@ -4,6 +4,7 @@
#include <fstream>
#include <stdexcept>
#include <components/esm3/cellid.hpp>
#include <components/debug/debuglog.hpp>
#include <components/misc/notnullptr.hpp>
#include <components/to_utf8/to_utf8.hpp>
@ -236,6 +237,12 @@ namespace ESM
writeHNRefId(name, value);
}
void ESMWriter::writeCellId(const ESM::RefId& cellId)
{
ESM::CellId cell = ESM::CellId::extractFromRefId(cellId);
cell.save(*this);
}
void ESMWriter::writeMaybeFixedSizeString(const std::string& data, std::size_t size)
{
std::string string;

@ -103,6 +103,8 @@ namespace ESM
writeHNCRefId(name, value);
}
void writeCellId(const ESM::RefId& cellId);
template <typename T>
void writeHNT(NAME name, const T& data)
{

@ -59,7 +59,23 @@ namespace ESM
const ESM::RefId& Cell::updateId()
{
mId = mCellId.getCellRefId();
CellId cellid;
cellid.mPaged = !(mData.mFlags & Interior);
if (cellid.mPaged)
{
cellid.mWorldspace = CellId::sDefaultWorldspace;
cellid.mIndex.mX = mData.mX;
cellid.mIndex.mY = mData.mY;
}
else
{
cellid.mWorldspace = Misc::StringUtils::lowerCase(mName);
cellid.mIndex.mX = 0;
cellid.mIndex.mY = 0;
}
mId = cellid.getCellRefId();
return mId;
}
@ -97,20 +113,6 @@ namespace ESM
if (!hasData)
esm.fail("Missing DATA subrecord");
mCellId.mPaged = !(mData.mFlags & Interior);
if (mCellId.mPaged)
{
mCellId.mWorldspace = CellId::sDefaultWorldspace;
mCellId.mIndex.mX = mData.mX;
mCellId.mIndex.mY = mData.mY;
}
else
{
mCellId.mWorldspace = Misc::StringUtils::lowerCase(mName);
mCellId.mIndex.mX = 0;
mCellId.mIndex.mY = 0;
}
updateId();
}
@ -340,8 +342,4 @@ namespace ESM
mAmbi.mFogDensity = 0;
}
const CellId& Cell::getCellId() const
{
return mCellId;
}
}

@ -120,7 +120,6 @@ namespace ESM
std::vector<ESM_Context> mContextList; // File position; multiple positions for multiple plugin support
DATAstruct mData;
CellId mCellId;
AMBIstruct mAmbi;
bool mHasAmbi;
@ -191,7 +190,6 @@ namespace ESM
void blank();
///< Set record to default state (does not touch the ID/index).
const CellId& getCellId() const;
const ESM::RefId& updateId();
};
}

@ -11,7 +11,7 @@ namespace ESM
mObject.mRef.loadId(esm, true);
mObject.load(esm);
mCellId.load(esm);
mCellId = esm.getCellId();
esm.getHNTSized<12>(mLastKnownExteriorPosition, "LKEP");
@ -19,7 +19,7 @@ namespace ESM
{
mHasMark = true;
esm.getHTSized<24>(mMarkedPosition);
mMarkedCell.load(esm);
mMarkedCell = esm.getCellId();
}
else
mHasMark = false;
@ -92,14 +92,14 @@ namespace ESM
{
mObject.save(esm);
mCellId.save(esm);
esm.writeCellId(mCellId);
esm.writeHNT("LKEP", mLastKnownExteriorPosition);
if (mHasMark)
{
esm.writeHNT("MARK", mMarkedPosition, 24);
mMarkedCell.save(esm);
esm.writeCellId(mMarkedCell);
}
esm.writeHNRefId("SIGN", mBirthsign);

@ -20,12 +20,12 @@ namespace ESM
struct Player
{
NpcState mObject;
CellId mCellId;
RefId mCellId;
float mLastKnownExteriorPosition[3];
unsigned char mHasMark;
bool mSetWerewolfAcrobatics;
Position mMarkedPosition;
CellId mMarkedCell;
RefId mMarkedCell;
ESM::RefId mBirthsign;
int mCurrentCrimeId;

@ -45,7 +45,7 @@ namespace EsmLoader
return (v.mId);
}
const ESM::CellId& operator()(const ESM::Cell& v) const { return v.mCellId; }
const ESM::RefId& operator()(const ESM::Cell& v) const { return v.mId; }
std::pair<int, int> operator()(const ESM::Land& v) const { return std::pair(v.mX, v.mY); }

Loading…
Cancel
Save