Skip to content

Commit

Permalink
Merge branch 'thewickedmarkerofthewest' into 'master'
Browse files Browse the repository at this point in the history
Emulate vanilla closest marker preference (#8187)

Closes #8187

See merge request OpenMW/openmw!4401
  • Loading branch information
psi29a committed Oct 15, 2024
2 parents 3e3ff00 + 77d2f18 commit 34e32b7
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@
Bug #8132: Actors without hello responses turn to face the player
Bug #8171: Items with more than 100% health can be repaired
Bug #8172: Openmw-cs crashes when viewing `Dantooine, Sea`
Bug #8187: Intervention effects should use Chebyshev distance to determine the closest marker
Feature #1415: Infinite fall failsafe
Feature #2566: Handle NAM9 records for manual cell references
Feature #3501: OpenMW-CS: Instance Editing - Shortcuts for axial locking
Expand Down
65 changes: 57 additions & 8 deletions apps/openmw/mwworld/worldimp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3257,22 +3257,71 @@ namespace MWWorld

MWWorld::ConstPtr World::getClosestMarkerFromExteriorPosition(const osg::Vec3f& worldPos, const ESM::RefId& id)
{
MWWorld::ConstPtr closestMarker;
float closestDistance = std::numeric_limits<float>::max();
const ESM::ExteriorCellLocation posIndex = ESM::positionToExteriorCellLocation(worldPos.x(), worldPos.y());

std::vector<MWWorld::Ptr> markers;
// Potential optimization: don't scan the entire world for markers and actually do the Todd spiral
std::vector<Ptr> markers;
mWorldModel.getExteriorPtrs(id, markers);

struct MarkerInfo
{
Ptr mPtr;
int mColumn, mRow; // Local coordinates in the valid marker grid
};
std::vector<MarkerInfo> validMarkers;
validMarkers.reserve(markers.size());

// The idea is to collect all markers that belong to the smallest possible square grid around worldPos
// They are grouped with their position on that grid's edge where the origin is the SW corner
int minGridSize = std::numeric_limits<int>::max();
for (const Ptr& marker : markers)
{
osg::Vec3f markerPos = marker.getRefData().getPosition().asVec3();
float distance = (worldPos - markerPos).length2();
if (distance < closestDistance)
const osg::Vec3f markerPos = marker.getRefData().getPosition().asVec3();
const ESM::ExteriorCellLocation index = ESM::positionToExteriorCellLocation(markerPos.x(), markerPos.y());

const int deltaX = index.mX - posIndex.mX;
const int deltaY = index.mY - posIndex.mY;
const int gridSize = std::max(std::abs(deltaX), std::abs(deltaY)) * 2;
if (gridSize == 0)
return marker;

if (gridSize <= minGridSize)
{
closestDistance = distance;
closestMarker = marker;
if (gridSize < minGridSize)
{
validMarkers.clear();
minGridSize = gridSize;
}
validMarkers.push_back({ marker, gridSize / 2 + deltaX, gridSize / 2 + deltaY });
}
}

ConstPtr closestMarker;
if (validMarkers.empty())
return closestMarker;
if (validMarkers.size() == 1)
return validMarkers[0].mPtr;

// All the markers are on the edge of the grid
// Break ties by picking the earliest marker on SW -> SE -> NE -> NW -> SW path
int earliestDistance = std::numeric_limits<int>::max();
for (const MarkerInfo& marker : validMarkers)
{
int distance = 0;
if (marker.mRow == 0) // South edge (plus SW and SE corners)
distance = marker.mColumn;
else if (marker.mColumn == minGridSize) // East edge and NE corner
distance = minGridSize + marker.mRow;
else if (marker.mRow == minGridSize) // North edge and NW corner
distance = minGridSize * 3 - marker.mColumn;
else // West edge
distance = minGridSize * 4 - marker.mRow;
if (distance < earliestDistance)
{
closestMarker = marker.mPtr;
earliestDistance = distance;
}
}
return closestMarker;
}

Expand Down

0 comments on commit 34e32b7

Please sign in to comment.