mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-22 15:23:58 +00:00
20da0892ef
Slowly moving through the open-cs errors Good progress in openCS Very good progress on openCS Getting closer with openCS OpenCS compiles and runs! Didn't have time to test it all though ix openMW everything compiles on windows?? Fix gcc Fix Clang
505 lines
13 KiB
C++
505 lines
13 KiB
C++
#include "regionmap.hpp"
|
|
|
|
#include <QBrush>
|
|
#include <QModelIndex>
|
|
#include <QSize>
|
|
#include <QVariant>
|
|
|
|
#include <algorithm>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
#include <type_traits>
|
|
|
|
#include <apps/opencs/model/world/cell.hpp>
|
|
#include <apps/opencs/model/world/cellcoordinates.hpp>
|
|
#include <apps/opencs/model/world/idcollection.hpp>
|
|
#include <apps/opencs/model/world/record.hpp>
|
|
|
|
#include <components/esm3/loadregn.hpp>
|
|
#include <components/misc/strings/lower.hpp>
|
|
|
|
#include "data.hpp"
|
|
#include "universalid.hpp"
|
|
|
|
CSMWorld::RegionMap::CellDescription::CellDescription()
|
|
: mDeleted(false)
|
|
{
|
|
}
|
|
|
|
CSMWorld::RegionMap::CellDescription::CellDescription(const Record<Cell>& cell)
|
|
{
|
|
const Cell& cell2 = cell.get();
|
|
|
|
if (!cell2.isExterior())
|
|
throw std::logic_error("Interior cell in region map");
|
|
|
|
mDeleted = cell.isDeleted();
|
|
|
|
mRegion = cell2.mRegion;
|
|
mName = cell2.mName;
|
|
}
|
|
|
|
CSMWorld::CellCoordinates CSMWorld::RegionMap::getIndex(const QModelIndex& index) const
|
|
{
|
|
return mMin.move(index.column(), mMax.getY() - mMin.getY() - index.row() - 1);
|
|
}
|
|
|
|
QModelIndex CSMWorld::RegionMap::getIndex(const CellCoordinates& index) const
|
|
{
|
|
// I hate you, Qt API naming scheme!
|
|
return QAbstractTableModel::index(
|
|
mMax.getY() - mMin.getY() - (index.getY() - mMin.getY()) - 1, index.getX() - mMin.getX());
|
|
}
|
|
|
|
CSMWorld::CellCoordinates CSMWorld::RegionMap::getIndex(const Cell& cell) const
|
|
{
|
|
std::istringstream stream(cell.mId.getRefIdString());
|
|
|
|
char ignore;
|
|
int x = 0;
|
|
int y = 0;
|
|
stream >> ignore >> x >> y;
|
|
|
|
return CellCoordinates(x, y);
|
|
}
|
|
|
|
void CSMWorld::RegionMap::buildRegions()
|
|
{
|
|
const IdCollection<ESM::Region>& regions = mData.getRegions();
|
|
|
|
int size = regions.getSize();
|
|
|
|
for (int i = 0; i < size; ++i)
|
|
{
|
|
const Record<ESM::Region>& region = regions.getRecord(i);
|
|
|
|
if (!region.isDeleted())
|
|
mColours.insert(std::make_pair(region.get().mId, region.get().mMapColor));
|
|
}
|
|
}
|
|
|
|
void CSMWorld::RegionMap::buildMap()
|
|
{
|
|
const IdCollection<Cell>& cells = mData.getCells();
|
|
|
|
int size = cells.getSize();
|
|
|
|
for (int i = 0; i < size; ++i)
|
|
{
|
|
const Record<Cell>& cell = cells.getRecord(i);
|
|
|
|
const Cell& cell2 = cell.get();
|
|
|
|
if (cell2.isExterior())
|
|
{
|
|
CellDescription description(cell);
|
|
|
|
CellCoordinates index = getIndex(cell2);
|
|
|
|
mMap.insert(std::make_pair(index, description));
|
|
}
|
|
}
|
|
|
|
std::pair<CellCoordinates, CellCoordinates> mapSize = getSize();
|
|
|
|
mMin = mapSize.first;
|
|
mMax = mapSize.second;
|
|
}
|
|
|
|
void CSMWorld::RegionMap::addCell(const CellCoordinates& index, const CellDescription& description)
|
|
{
|
|
std::map<CellCoordinates, CellDescription>::iterator cell = mMap.find(index);
|
|
|
|
if (cell != mMap.end())
|
|
{
|
|
cell->second = description;
|
|
}
|
|
else
|
|
{
|
|
updateSize();
|
|
|
|
mMap.insert(std::make_pair(index, description));
|
|
}
|
|
|
|
QModelIndex index2 = getIndex(index);
|
|
|
|
dataChanged(index2, index2);
|
|
}
|
|
|
|
void CSMWorld::RegionMap::addCells(int start, int end)
|
|
{
|
|
const IdCollection<Cell>& cells = mData.getCells();
|
|
|
|
for (int i = start; i <= end; ++i)
|
|
{
|
|
const Record<Cell>& cell = cells.getRecord(i);
|
|
|
|
const Cell& cell2 = cell.get();
|
|
|
|
if (cell2.isExterior())
|
|
{
|
|
CellCoordinates index = getIndex(cell2);
|
|
|
|
CellDescription description(cell);
|
|
|
|
addCell(index, description);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CSMWorld::RegionMap::removeCell(const CellCoordinates& index)
|
|
{
|
|
std::map<CellCoordinates, CellDescription>::iterator cell = mMap.find(index);
|
|
|
|
if (cell != mMap.end())
|
|
{
|
|
mMap.erase(cell);
|
|
|
|
QModelIndex index2 = getIndex(index);
|
|
|
|
dataChanged(index2, index2);
|
|
|
|
updateSize();
|
|
}
|
|
}
|
|
|
|
void CSMWorld::RegionMap::addRegion(const ESM::RefId& region, unsigned int colour)
|
|
{
|
|
mColours[region] = colour;
|
|
}
|
|
|
|
void CSMWorld::RegionMap::removeRegion(const ESM::RefId& region)
|
|
{
|
|
auto iter(mColours.find(region));
|
|
|
|
if (iter != mColours.end())
|
|
mColours.erase(iter);
|
|
}
|
|
|
|
void CSMWorld::RegionMap::updateRegions(const std::vector<ESM::RefId>& regions)
|
|
{
|
|
std::vector<ESM::RefId> regions2(regions);
|
|
|
|
std::sort(regions2.begin(), regions2.end());
|
|
|
|
for (std::map<CellCoordinates, CellDescription>::const_iterator iter(mMap.begin()); iter != mMap.end(); ++iter)
|
|
{
|
|
if (!iter->second.mRegion.empty()
|
|
&& std::find(regions2.begin(), regions2.end(),iter->second.mRegion)
|
|
!= regions2.end())
|
|
{
|
|
QModelIndex index = getIndex(iter->first);
|
|
|
|
dataChanged(index, index);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CSMWorld::RegionMap::updateSize()
|
|
{
|
|
std::pair<CellCoordinates, CellCoordinates> size = getSize();
|
|
|
|
if (int diff = size.first.getX() - mMin.getX())
|
|
{
|
|
beginInsertColumns(QModelIndex(), 0, std::abs(diff) - 1);
|
|
mMin = CellCoordinates(size.first.getX(), mMin.getY());
|
|
endInsertColumns();
|
|
}
|
|
|
|
if (int diff = size.first.getY() - mMin.getY())
|
|
{
|
|
beginInsertRows(QModelIndex(), 0, std::abs(diff) - 1);
|
|
mMin = CellCoordinates(mMin.getX(), size.first.getY());
|
|
endInsertRows();
|
|
}
|
|
|
|
if (int diff = size.second.getX() - mMax.getX())
|
|
{
|
|
int columns = columnCount();
|
|
|
|
if (diff > 0)
|
|
beginInsertColumns(QModelIndex(), columns, columns + diff - 1);
|
|
else
|
|
beginRemoveColumns(QModelIndex(), columns + diff, columns - 1);
|
|
|
|
mMax = CellCoordinates(size.second.getX(), mMax.getY());
|
|
endInsertColumns();
|
|
}
|
|
|
|
if (int diff = size.second.getY() - mMax.getY())
|
|
{
|
|
int rows = rowCount();
|
|
|
|
if (diff > 0)
|
|
beginInsertRows(QModelIndex(), rows, rows + diff - 1);
|
|
else
|
|
beginRemoveRows(QModelIndex(), rows + diff, rows - 1);
|
|
|
|
mMax = CellCoordinates(mMax.getX(), size.second.getY());
|
|
endInsertRows();
|
|
}
|
|
}
|
|
|
|
std::pair<CSMWorld::CellCoordinates, CSMWorld::CellCoordinates> CSMWorld::RegionMap::getSize() const
|
|
{
|
|
const IdCollection<Cell>& cells = mData.getCells();
|
|
|
|
int size = cells.getSize();
|
|
|
|
CellCoordinates min(0, 0);
|
|
CellCoordinates max(0, 0);
|
|
|
|
for (int i = 0; i < size; ++i)
|
|
{
|
|
const Record<Cell>& cell = cells.getRecord(i);
|
|
|
|
const Cell& cell2 = cell.get();
|
|
|
|
if (cell2.isExterior())
|
|
{
|
|
CellCoordinates index = getIndex(cell2);
|
|
|
|
if (min == max)
|
|
{
|
|
min = index;
|
|
max = min.move(1, 1);
|
|
}
|
|
else
|
|
{
|
|
if (index.getX() < min.getX())
|
|
min = CellCoordinates(index.getX(), min.getY());
|
|
else if (index.getX() >= max.getX())
|
|
max = CellCoordinates(index.getX() + 1, max.getY());
|
|
|
|
if (index.getY() < min.getY())
|
|
min = CellCoordinates(min.getX(), index.getY());
|
|
else if (index.getY() >= max.getY())
|
|
max = CellCoordinates(max.getX(), index.getY() + 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
return std::make_pair(min, max);
|
|
}
|
|
|
|
CSMWorld::RegionMap::RegionMap(Data& data)
|
|
: mData(data)
|
|
{
|
|
buildRegions();
|
|
buildMap();
|
|
|
|
QAbstractItemModel* regions = data.getTableModel(UniversalId(UniversalId::Type_Regions));
|
|
|
|
connect(regions, &QAbstractItemModel::rowsAboutToBeRemoved, this, &RegionMap::regionsAboutToBeRemoved);
|
|
connect(regions, &QAbstractItemModel::rowsInserted, this, &RegionMap::regionsInserted);
|
|
connect(regions, &QAbstractItemModel::dataChanged, this, &RegionMap::regionsChanged);
|
|
|
|
QAbstractItemModel* cells = data.getTableModel(UniversalId(UniversalId::Type_Cells));
|
|
|
|
connect(cells, &QAbstractItemModel::rowsAboutToBeRemoved, this, &RegionMap::cellsAboutToBeRemoved);
|
|
connect(cells, &QAbstractItemModel::rowsInserted, this, &RegionMap::cellsInserted);
|
|
connect(cells, &QAbstractItemModel::dataChanged, this, &RegionMap::cellsChanged);
|
|
}
|
|
|
|
int CSMWorld::RegionMap::rowCount(const QModelIndex& parent) const
|
|
{
|
|
if (parent.isValid())
|
|
return 0;
|
|
|
|
return mMax.getY() - mMin.getY();
|
|
}
|
|
|
|
int CSMWorld::RegionMap::columnCount(const QModelIndex& parent) const
|
|
{
|
|
if (parent.isValid())
|
|
return 0;
|
|
|
|
return mMax.getX() - mMin.getX();
|
|
}
|
|
|
|
QVariant CSMWorld::RegionMap::data(const QModelIndex& index, int role) const
|
|
{
|
|
if (role == Qt::SizeHintRole)
|
|
return QSize(16, 16);
|
|
|
|
if (role == Qt::BackgroundRole)
|
|
{
|
|
/// \todo GUI class in non-GUI code. Needs to be addressed eventually.
|
|
|
|
std::map<CellCoordinates, CellDescription>::const_iterator cell = mMap.find(getIndex(index));
|
|
|
|
if (cell != mMap.end())
|
|
{
|
|
if (cell->second.mDeleted)
|
|
return QBrush(Qt::red, Qt::DiagCrossPattern);
|
|
|
|
auto iter
|
|
= mColours.find(cell->second.mRegion);
|
|
|
|
if (iter != mColours.end())
|
|
return QBrush(QColor(iter->second & 0xff, (iter->second >> 8) & 0xff, (iter->second >> 16) & 0xff));
|
|
|
|
if (cell->second.mRegion.empty())
|
|
return QBrush(Qt::Dense6Pattern); // no region
|
|
|
|
return QBrush(Qt::red, Qt::Dense6Pattern); // invalid region
|
|
}
|
|
|
|
return QBrush(Qt::DiagCrossPattern);
|
|
}
|
|
|
|
if (role == Qt::ToolTipRole)
|
|
{
|
|
CellCoordinates cellIndex = getIndex(index);
|
|
|
|
std::ostringstream stream;
|
|
|
|
stream << cellIndex;
|
|
|
|
std::map<CellCoordinates, CellDescription>::const_iterator cell = mMap.find(cellIndex);
|
|
|
|
if (cell != mMap.end())
|
|
{
|
|
if (!cell->second.mName.empty())
|
|
stream << " " << cell->second.mName;
|
|
|
|
if (cell->second.mDeleted)
|
|
stream << " (deleted)";
|
|
|
|
if (!cell->second.mRegion.empty())
|
|
{
|
|
stream << "<br>";
|
|
|
|
auto iter
|
|
= mColours.find(cell->second.mRegion);
|
|
|
|
if (iter != mColours.end())
|
|
stream << cell->second.mRegion;
|
|
else
|
|
stream << "<font color=red>" << cell->second.mRegion << "</font>";
|
|
}
|
|
}
|
|
else
|
|
stream << " (no cell)";
|
|
|
|
return QString::fromUtf8(stream.str().c_str());
|
|
}
|
|
|
|
if (role == Role_Region)
|
|
{
|
|
CellCoordinates cellIndex = getIndex(index);
|
|
|
|
std::map<CellCoordinates, CellDescription>::const_iterator cell = mMap.find(cellIndex);
|
|
|
|
if (cell != mMap.end() && !cell->second.mRegion.empty())
|
|
return QString::fromUtf8(cell->second.mRegion.getRefIdString().c_str());
|
|
}
|
|
|
|
if (role == Role_CellId)
|
|
{
|
|
CellCoordinates cellIndex = getIndex(index);
|
|
|
|
std::ostringstream stream;
|
|
stream << "#" << cellIndex.getX() << " " << cellIndex.getY();
|
|
|
|
return QString::fromUtf8(stream.str().c_str());
|
|
}
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
Qt::ItemFlags CSMWorld::RegionMap::flags(const QModelIndex& index) const
|
|
{
|
|
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
|
}
|
|
|
|
void CSMWorld::RegionMap::regionsAboutToBeRemoved(const QModelIndex& parent, int start, int end)
|
|
{
|
|
std::vector<ESM::RefId> update;
|
|
|
|
const IdCollection<ESM::Region>& regions = mData.getRegions();
|
|
|
|
for (int i = start; i <= end; ++i)
|
|
{
|
|
const Record<ESM::Region>& region = regions.getRecord(i);
|
|
|
|
update.push_back(region.get().mId);
|
|
|
|
removeRegion(region.get().mId);
|
|
}
|
|
|
|
updateRegions(update);
|
|
}
|
|
|
|
void CSMWorld::RegionMap::regionsInserted(const QModelIndex& parent, int start, int end)
|
|
{
|
|
std::vector<ESM::RefId> update;
|
|
|
|
const IdCollection<ESM::Region>& regions = mData.getRegions();
|
|
|
|
for (int i = start; i <= end; ++i)
|
|
{
|
|
const Record<ESM::Region>& region = regions.getRecord(i);
|
|
|
|
if (!region.isDeleted())
|
|
{
|
|
update.push_back(region.get().mId);
|
|
|
|
addRegion(region.get().mId, region.get().mMapColor);
|
|
}
|
|
}
|
|
|
|
updateRegions(update);
|
|
}
|
|
|
|
void CSMWorld::RegionMap::regionsChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
|
{
|
|
// Note: At this point an additional check could be inserted to see if there is any change to the
|
|
// columns we are interested in. If not we can exit the function here and avoid all updating.
|
|
|
|
std::vector<ESM::RefId> update;
|
|
|
|
const IdCollection<ESM::Region>& regions = mData.getRegions();
|
|
|
|
for (int i = topLeft.row(); i <= bottomRight.column(); ++i)
|
|
{
|
|
const Record<ESM::Region>& region = regions.getRecord(i);
|
|
|
|
update.push_back(region.get().mId);
|
|
|
|
if (!region.isDeleted())
|
|
addRegion(region.get().mId, region.get().mMapColor);
|
|
else
|
|
removeRegion(region.get().mId);
|
|
}
|
|
|
|
updateRegions(update);
|
|
}
|
|
|
|
void CSMWorld::RegionMap::cellsAboutToBeRemoved(const QModelIndex& parent, int start, int end)
|
|
{
|
|
const IdCollection<Cell>& cells = mData.getCells();
|
|
|
|
for (int i = start; i <= end; ++i)
|
|
{
|
|
const Record<Cell>& cell = cells.getRecord(i);
|
|
|
|
const Cell& cell2 = cell.get();
|
|
|
|
if (cell2.isExterior())
|
|
removeCell(getIndex(cell2));
|
|
}
|
|
}
|
|
|
|
void CSMWorld::RegionMap::cellsInserted(const QModelIndex& parent, int start, int end)
|
|
{
|
|
addCells(start, end);
|
|
}
|
|
|
|
void CSMWorld::RegionMap::cellsChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
|
{
|
|
// Note: At this point an additional check could be inserted to see if there is any change to the
|
|
// columns we are interested in. If not we can exit the function here and avoid all updating.
|
|
|
|
addCells(topLeft.row(), bottomRight.row());
|
|
}
|