1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-22 00:53:52 +00:00
openmw/apps/opencs/model/world/refcollection.cpp
elsid cfdbd0d471
Indicate moved cell refs explicitly
This is less error prone approach than use of MovedCellRef fields.

Also make separate functions for skipping and reading moved cell refs to avoid
passing special flags  logic and null pointers for unused arguments.
2021-07-12 18:56:42 +02:00

137 lines
4.6 KiB
C++

#include "refcollection.hpp"
#include <components/esm/loadcell.hpp>
#include "ref.hpp"
#include "cell.hpp"
#include "universalid.hpp"
#include "record.hpp"
void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base,
std::map<ESM::RefNum, std::string>& cache, CSMDoc::Messages& messages)
{
Record<Cell> cell = mCells.getRecord (cellIndex);
Cell& cell2 = base ? cell.mBase : cell.mModified;
CellRef ref;
ref.mNew = false;
ESM::MovedCellRef mref;
mref.mRefNum.mIndex = 0;
bool isDeleted = false;
bool isMoved = false;
while (ESM::Cell::getNextRef(reader, ref, isDeleted, mref, isMoved))
{
// Keep mOriginalCell empty when in modified (as an indicator that the
// original cell will always be equal the current cell).
ref.mOriginalCell = base ? cell2.mId : "";
if (cell.get().isExterior())
{
// Autocalculate the cell index from coordinates first
std::pair<int, int> index = ref.getCellIndex();
ref.mCell = "#" + std::to_string(index.first) + " " + std::to_string(index.second);
// Handle non-base moved references
if (!base && !isMoved)
{
// Moved references must have a link back to their original cell
// See discussion: https://forum.openmw.org/viewtopic.php?f=6&t=577&start=30
ref.mOriginalCell = cell2.mId;
// Some mods may move references outside of the bounds, which often happens they are deleted.
// This results in nonsensical autocalculated cell IDs, so we must use the record target cell.
// Log a warning if the record target cell is different
if (index.first != mref.mTarget[0] || index.second != mref.mTarget[1])
{
std::string indexCell = ref.mCell;
ref.mCell = "#" + std::to_string(mref.mTarget[0]) + " " + std::to_string(mref.mTarget[1]);
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Cell, mCells.getId (cellIndex));
messages.add(id, "The position of the moved reference " + ref.mRefID + " (cell " + indexCell + ")"
" does not match the target cell (" + ref.mCell + ")",
std::string(), CSMDoc::Message::Severity_Warning);
}
}
}
else
ref.mCell = cell2.mId;
// ignore content file number
std::map<ESM::RefNum, std::string>::iterator iter = cache.begin();
unsigned int thisIndex = ref.mRefNum.mIndex & 0x00ffffff;
if (ref.mRefNum.mContentFile != -1 && !base) ref.mRefNum.mContentFile = ref.mRefNum.mIndex >> 24;
for (; iter != cache.end(); ++iter)
{
if (thisIndex == iter->first.mIndex)
break;
}
if (isDeleted)
{
if (iter==cache.end())
{
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Cell,
mCells.getId (cellIndex));
messages.add (id, "Attempt to delete a non-existent reference");
continue;
}
int index = getIndex (iter->second);
Record<CellRef> record = getRecord (index);
if (base)
{
removeRows (index, 1);
cache.erase (iter);
}
else
{
record.mState = RecordBase::State_Deleted;
setRecord (index, record);
}
continue;
}
if (iter==cache.end())
{
// new reference
ref.mId = getNewId();
Record<CellRef> record;
record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
const ESM::RefNum refNum = ref.mRefNum;
std::string refId = ref.mId;
(base ? record.mBase : record.mModified) = std::move(ref);
appendRecord (record);
cache.emplace(refNum, std::move(refId));
}
else
{
// old reference -> merge
ref.mId = iter->second;
int index = getIndex (ref.mId);
Record<CellRef> record = getRecord (index);
record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_Modified;
(base ? record.mBase : record.mModified) = std::move(ref);
setRecord (index, record);
}
}
}
std::string CSMWorld::RefCollection::getNewId()
{
return "ref#" + std::to_string(mNextId++);
}