Draw and recycle door markers consistently

This commit is contained in:
Evil Eye 2025-09-10 21:29:40 +02:00
parent 082b9a5461
commit 6e45e562a8
2 changed files with 53 additions and 31 deletions

View File

@ -319,11 +319,12 @@ namespace MWGui
mLocalMap->setViewOffset(viewOffset); mLocalMap->setViewOffset(viewOffset);
} }
MyGUI::IntCoord LocalMapBase::getMarkerCoordinates(MyGUI::Widget* widget, size_t markerSize) const void LocalMapBase::updateMarkerCoordinates(MyGUI::Widget* widget, size_t markerSize) const
{ {
MarkerUserData& markerPos(*widget->getUserData<MarkerUserData>()); MarkerUserData& markerPos(*widget->getUserData<MarkerUserData>());
auto position = getPosition(markerPos.cellX, markerPos.cellY, markerPos.nX, markerPos.nY); auto position = getPosition(markerPos.cellX, markerPos.cellY, markerPos.nX, markerPos.nY);
return MyGUI::IntCoord(position.left - markerSize / 2, position.top - markerSize / 2, markerSize, markerSize); MyGUI::IntCoord coord(position.left - markerSize / 2, position.top - markerSize / 2, markerSize, markerSize);
widget->setCoord(coord);
} }
std::vector<MarkerWidget*>& LocalMapBase::currentDoorMarkersWidgets() std::vector<MarkerWidget*>& LocalMapBase::currentDoorMarkersWidgets()
@ -379,6 +380,13 @@ namespace MWGui
if (&cell == mActiveCell) if (&cell == mActiveCell)
return; // don't do anything if we're still in the same cell return; // don't do anything if we're still in the same cell
// Remove all interior door markers
mDoorMarkersToRecycle.insert(
mDoorMarkersToRecycle.end(), mInteriorDoorMarkerWidgets.begin(), mInteriorDoorMarkerWidgets.end());
for (MarkerWidget* widget : mInteriorDoorMarkerWidgets)
widget->setVisible(false);
mInteriorDoorMarkerWidgets.clear();
const int x = cell.getGridX(); const int x = cell.getGridX();
const int y = cell.getGridY(); const int y = cell.getGridY();
@ -386,26 +394,47 @@ namespace MWGui
if (cell.isExterior()) if (cell.isExterior())
{ {
std::optional<MyGUI::IntRect> previousActiveGrid;
if (mActiveCell && mActiveCell->isExterior())
previousActiveGrid
= createRect({ mActiveCell->getGridX(), mActiveCell->getGridY() }, Constants::CellGridRadius);
mGrid = createRect({ x, y }, mExtCellDistance); mGrid = createRect({ x, y }, mExtCellDistance);
const MyGUI::IntRect activeGrid = createRect({ x, y }, Constants::CellGridRadius); const MyGUI::IntRect activeGrid = createRect({ x, y }, Constants::CellGridRadius);
mExteriorDoorMarkerWidgets.clear(); mExteriorDoorMarkerWidgets.clear();
for (auto& [coord, doors] : mExteriorDoorsByCell) for (auto it = mExteriorDoorsByCell.begin(); it != mExteriorDoorsByCell.end();)
{ {
if (!mGrid.inside({ coord.first, coord.second }) || activeGrid.inside({ coord.first, coord.second })) const auto& [coord, doors] = *it;
const MyGUI::IntPoint pos(coord.first, coord.second);
// Remove markers that fall outside the rendered map and ones that are new to the active grid.
// Scripts can enable/disable doors, requiring us to update the markers. Morrowind.exe only updates
// markers when a cell is added to the active grid.
if (!mGrid.inside(pos)
|| (previousActiveGrid && !previousActiveGrid->inside(pos) && activeGrid.inside(pos)))
{ {
mDoorMarkersToRecycle.insert(mDoorMarkersToRecycle.end(), doors.begin(), doors.end()); mDoorMarkersToRecycle.insert(mDoorMarkersToRecycle.end(), doors.begin(), doors.end());
doors.clear(); for (MarkerWidget* widget : doors)
}
else
mExteriorDoorMarkerWidgets.insert(mExteriorDoorMarkerWidgets.end(), doors.begin(), doors.end());
}
for (auto& widget : mDoorMarkersToRecycle)
widget->setVisible(false); widget->setVisible(false);
it = mExteriorDoorsByCell.erase(it);
} }
else else
{
mExteriorDoorMarkerWidgets.insert(mExteriorDoorMarkerWidgets.end(), doors.begin(), doors.end());
++it;
}
}
}
else
{
mGrid = mLocalMapRender->getInteriorGrid(); mGrid = mLocalMapRender->getInteriorGrid();
// Remove all exterior door markers
mDoorMarkersToRecycle.insert(
mDoorMarkersToRecycle.end(), mExteriorDoorMarkerWidgets.begin(), mExteriorDoorMarkerWidgets.end());
for (MarkerWidget* widget : mExteriorDoorMarkerWidgets)
widget->setVisible(false);
mExteriorDoorMarkerWidgets.clear();
mExteriorDoorsByCell.clear();
}
mActiveCell = &cell; mActiveCell = &cell;
@ -449,7 +478,7 @@ namespace MWGui
mNeedDoorMarkersUpdate = true; mNeedDoorMarkersUpdate = true;
for (MyGUI::Widget* widget : currentDoorMarkersWidgets()) for (MyGUI::Widget* widget : currentDoorMarkersWidgets())
widget->setCoord(getMarkerCoordinates(widget, 8)); updateMarkerCoordinates(widget, 8);
updateMagicMarkers(); updateMagicMarkers();
updateCustomMarkers(); updateCustomMarkers();
@ -609,6 +638,9 @@ namespace MWGui
entry.mFogTexture = std::make_unique<MyGUIPlatform::OSGTexture>(std::string(), nullptr); entry.mFogTexture = std::make_unique<MyGUIPlatform::OSGTexture>(std::string(), nullptr);
} }
needRedraw = true; needRedraw = true;
// Newly uncovered chunk, make sure to draw door markers right away instead of waiting for a cell
// transition
mNeedDoorMarkersUpdate = true;
} }
} }
if (needRedraw) if (needRedraw)
@ -621,16 +653,8 @@ namespace MWGui
MWBase::World* world = MWBase::Environment::get().getWorld(); MWBase::World* world = MWBase::Environment::get().getWorld();
MWWorld::WorldModel* worldModel = MWBase::Environment::get().getWorldModel(); MWWorld::WorldModel* worldModel = MWBase::Environment::get().getWorldModel();
const bool recycledMarkers = !mInteriorDoorMarkerWidgets.empty();
mDoorMarkersToRecycle.insert(
mDoorMarkersToRecycle.end(), mInteriorDoorMarkerWidgets.begin(), mInteriorDoorMarkerWidgets.end());
mInteriorDoorMarkerWidgets.clear();
if (!mActiveCell->isExterior()) if (!mActiveCell->isExterior())
{ {
for (MarkerWidget* widget : mExteriorDoorMarkerWidgets)
widget->setVisible(false);
MWWorld::CellStore& cell = worldModel->getInterior(mActiveCell->getNameId()); MWWorld::CellStore& cell = worldModel->getInterior(mActiveCell->getNameId());
world->getDoorMarkers(cell, doors); world->getDoorMarkers(cell, doors);
} }
@ -638,13 +662,13 @@ namespace MWGui
{ {
for (MapEntry& entry : mMaps) for (MapEntry& entry : mMaps)
{ {
if (!entry.mMapTexture && entry.mMapWidget->getVisible() && !widgetCropped(entry.mMapWidget, mLocalMap)) if (!entry.mMapWidget->getVisible() || widgetCropped(entry.mMapWidget, mLocalMap))
world->getDoorMarkers(worldModel->getExterior(ESM::ExteriorCellLocation( continue;
entry.mCellX, entry.mCellY, ESM::Cell::sDefaultWorldspaceId)), if (mExteriorDoorsByCell.contains({ entry.mCellX, entry.mCellY }))
doors); continue;
ESM::ExteriorCellLocation id(entry.mCellX, entry.mCellY, ESM::Cell::sDefaultWorldspaceId);
world->getDoorMarkers(worldModel->getExterior(id), doors);
} }
if (doors.empty() && !recycledMarkers)
return;
} }
// Create a widget for each marker // Create a widget for each marker
@ -652,8 +676,7 @@ namespace MWGui
{ {
std::vector<std::string> destNotes; std::vector<std::string> destNotes;
CustomMarkerCollection::RangeType markers = mCustomMarkers.getMarkers(marker.dest); CustomMarkerCollection::RangeType markers = mCustomMarkers.getMarkers(marker.dest);
for (CustomMarkerCollection::ContainerType::const_iterator iter = markers.first; iter != markers.second; for (auto iter = markers.first; iter != markers.second; ++iter)
++iter)
destNotes.push_back(iter->second.mNote); destNotes.push_back(iter->second.mNote);
MarkerWidget* markerWidget = nullptr; MarkerWidget* markerWidget = nullptr;
@ -682,8 +705,8 @@ namespace MWGui
mExteriorDoorsByCell[{ data->cellX, data->cellY }].push_back(markerWidget); mExteriorDoorsByCell[{ data->cellX, data->cellY }].push_back(markerWidget);
} }
for (auto& widget : mDoorMarkersToRecycle) for (MyGUI::Widget* widget : currentDoorMarkersWidgets())
widget->setVisible(false); updateMarkerCoordinates(widget, 8);
} }
void LocalMapBase::updateMagicMarkers() void LocalMapBase::updateMagicMarkers()
@ -732,7 +755,7 @@ namespace MWGui
MarkerUserData markerPos(mLocalMapRender); MarkerUserData markerPos(mLocalMapRender);
for (MyGUI::Widget* widget : currentDoorMarkersWidgets()) for (MyGUI::Widget* widget : currentDoorMarkersWidgets())
widget->setCoord(getMarkerCoordinates(widget, 8)); updateMarkerCoordinates(widget, 8);
for (MyGUI::Widget* widget : mCustomMarkerWidgets) for (MyGUI::Widget* widget : mCustomMarkerWidgets)
{ {
@ -741,7 +764,7 @@ namespace MWGui
} }
for (MyGUI::Widget* widget : mMagicMarkerWidgets) for (MyGUI::Widget* widget : mMagicMarkerWidgets)
widget->setCoord(getMarkerCoordinates(widget, 8)); updateMarkerCoordinates(widget, 8);
} }
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------

View File

@ -165,11 +165,10 @@ namespace MWGui
MyGUI::IntCoord getMarkerCoordinates( MyGUI::IntCoord getMarkerCoordinates(
float worldX, float worldY, MarkerUserData& markerPos, size_t markerSize) const; float worldX, float worldY, MarkerUserData& markerPos, size_t markerSize) const;
MarkerWidget* createDoorMarker(const std::string& name, float x, float y) const; MarkerWidget* createDoorMarker(const std::string& name, float x, float y) const;
MyGUI::IntCoord getMarkerCoordinates(MyGUI::Widget* widget, size_t markerSize) const; void updateMarkerCoordinates(MyGUI::Widget* widget, size_t markerSize) const;
virtual void notifyPlayerUpdate() {} virtual void notifyPlayerUpdate() {}
virtual void centerView(); virtual void centerView();
virtual void notifyMapChanged() {}
virtual void customMarkerCreated(MyGUI::Widget* marker) {} virtual void customMarkerCreated(MyGUI::Widget* marker) {}
virtual void doorMarkerCreated(MyGUI::Widget* marker) {} virtual void doorMarkerCreated(MyGUI::Widget* marker) {}