Skip to content

Commit 6287241

Browse files
authored
bugfix(partition): Fix inconsistent mine collision behaviour (TheSuperHackers#2208)
1 parent be927e0 commit 6287241

5 files changed

Lines changed: 104 additions & 0 deletions

File tree

Core/GameEngine/Include/Common/GameDefines.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@
4545
#define RETAIL_COMPATIBLE_PATHFINDING_ALLOCATION (1)
4646
#endif
4747

48+
#ifndef RETAIL_COMPATIBLE_CIRCLE_FILL_ALGORITHM
49+
#define RETAIL_COMPATIBLE_CIRCLE_FILL_ALGORITHM (1) // Use the original circle fill algorithm, which is more efficient but less accurate
50+
#endif
51+
4852
// Disable non retail fixes in the networking, such as putting more data per UDP packet
4953
#ifndef RETAIL_COMPATIBLE_NETWORKING
5054
#define RETAIL_COMPATIBLE_NETWORKING (1)

Generals/Code/GameEngine/Include/GameLogic/PartitionManager.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,11 @@ class PartitionData : public MemoryPoolObject
463463
Real radius
464464
);
465465

466+
/**
467+
A more advanced implementation of doCircleFill that is 100% accurate.
468+
*/
469+
void doCircleFillPrecise(Real centerX, Real centerY, Real radius);
470+
466471
/**
467472
fill in the pixels covered by the given rectangular shape with the given
468473
center, dimensions, and rotation.

Generals/Code/GameEngine/Source/GameLogic/Object/PartitionManager.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,8 @@ static Bool distCalcProc_BoundaryAndBoundary_2D(const Coord3D *posA, const Objec
371371
static Bool distCalcProc_CenterAndCenter_3D(const Coord3D *posA, const Object *objA, const Coord3D *posB, const Object *objB, Real& abDistSqr, Coord3D& abVec, Real maxDistSqr);
372372
static Bool distCalcProc_BoundaryAndBoundary_3D(const Coord3D *posA, const Object *objA, const Coord3D *posB, const Object *objB, Real& abDistSqr, Coord3D& abVec, Real maxDistSqr);
373373

374+
static Bool doesCircleOverlapCell(Real centerX, Real centerY, Real radius, Real cellX, Real cellY, Real cellSize);
375+
374376
//-----------------------------------------------------------------------------
375377
inline void projectCoord3D(Coord3D *coord, const Coord3D *unitDir, Real dist)
376378
{
@@ -1878,6 +1880,43 @@ void PartitionData::doCircleFill(
18781880
}
18791881
}
18801882

1883+
static Bool doesCircleOverlapCell(Real centerX, Real centerY, Real radius, Real cellX, Real cellY, Real cellSize)
1884+
{
1885+
Real closestX = std::max(cellX, std::min(centerX, cellX + cellSize));
1886+
Real closestY = std::max(cellY, std::min(centerY, cellY + cellSize));
1887+
Real distX = centerX - closestX;
1888+
Real distY = centerY - closestY;
1889+
1890+
return (sqr(distX) + sqr(distY)) < sqr(radius);
1891+
}
1892+
1893+
void PartitionData::doCircleFillPrecise(Real centerX, Real centerY, Real radius)
1894+
{
1895+
Int minCellX, minCellY, maxCellX, maxCellY;
1896+
ThePartitionManager->worldToCell(centerX - radius, centerY - radius, &minCellX, &minCellY);
1897+
ThePartitionManager->worldToCell(centerX + radius, centerY + radius, &maxCellX, &maxCellY);
1898+
1899+
Real cellSize = ThePartitionManager->getCellSize();
1900+
1901+
for (Int x = minCellX; x <= maxCellX; ++x)
1902+
{
1903+
for (Int y = minCellY; y <= maxCellY; ++y)
1904+
{
1905+
Real cellWorldX = x * cellSize;
1906+
Real cellWorldY = y * cellSize;
1907+
1908+
if (doesCircleOverlapCell(centerX, centerY, radius, cellWorldX, cellWorldY, cellSize))
1909+
{
1910+
PartitionCell* cell = ThePartitionManager->getCellAt(x, y);
1911+
if (cell)
1912+
{
1913+
addSubPixToCoverage(cell);
1914+
}
1915+
}
1916+
}
1917+
}
1918+
}
1919+
18811920
// -----------------------------------------------------------------------------
18821921
void PartitionData::doSmallFill(
18831922
Real centerX,
@@ -2079,7 +2118,13 @@ void PartitionData::updateCellsTouched()
20792118
case GEOMETRY_SPHERE:
20802119
case GEOMETRY_CYLINDER:
20812120
{
2121+
#if RETAIL_COMPATIBLE_CRC || RETAIL_COMPATIBLE_CIRCLE_FILL_ALGORITHM
20822122
doCircleFill(pos.x, pos.y, majorRadius);
2123+
#else
2124+
// TheSuperHackers @bugfix Stubbjax 29/01/2026 Use precise circle fill to improve
2125+
// collision accuracy, most notably for objects with geometry radii >= 20 and < 40.
2126+
doCircleFillPrecise(pos.x, pos.y, majorRadius);
2127+
#endif
20832128
break;
20842129
}
20852130

GeneralsMD/Code/GameEngine/Include/GameLogic/PartitionManager.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,11 @@ class PartitionData : public MemoryPoolObject
464464
Real radius
465465
);
466466

467+
/**
468+
A more advanced implementation of doCircleFill that is 100% accurate.
469+
*/
470+
void doCircleFillPrecise(Real centerX, Real centerY, Real radius);
471+
467472
/**
468473
fill in the pixels covered by the given rectangular shape with the given
469474
center, dimensions, and rotation.

GeneralsMD/Code/GameEngine/Source/GameLogic/Object/PartitionManager.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,8 @@ static Bool distCalcProc_BoundaryAndBoundary_2D(const Coord3D *posA, const Objec
375375
static Bool distCalcProc_CenterAndCenter_3D(const Coord3D *posA, const Object *objA, const Coord3D *posB, const Object *objB, Real& abDistSqr, Coord3D& abVec, Real maxDistSqr);
376376
static Bool distCalcProc_BoundaryAndBoundary_3D(const Coord3D *posA, const Object *objA, const Coord3D *posB, const Object *objB, Real& abDistSqr, Coord3D& abVec, Real maxDistSqr);
377377

378+
static Bool doesCircleOverlapCell(Real centerX, Real centerY, Real radius, Real cellX, Real cellY, Real cellSize);
379+
378380
//-----------------------------------------------------------------------------
379381
inline void projectCoord3D(Coord3D *coord, const Coord3D *unitDir, Real dist)
380382
{
@@ -1882,6 +1884,43 @@ void PartitionData::doCircleFill(
18821884
}
18831885
}
18841886

1887+
static Bool doesCircleOverlapCell(Real centerX, Real centerY, Real radius, Real cellX, Real cellY, Real cellSize)
1888+
{
1889+
Real closestX = std::max(cellX, std::min(centerX, cellX + cellSize));
1890+
Real closestY = std::max(cellY, std::min(centerY, cellY + cellSize));
1891+
Real distX = centerX - closestX;
1892+
Real distY = centerY - closestY;
1893+
1894+
return (sqr(distX) + sqr(distY)) < sqr(radius);
1895+
}
1896+
1897+
void PartitionData::doCircleFillPrecise(Real centerX, Real centerY, Real radius)
1898+
{
1899+
Int minCellX, minCellY, maxCellX, maxCellY;
1900+
ThePartitionManager->worldToCell(centerX - radius, centerY - radius, &minCellX, &minCellY);
1901+
ThePartitionManager->worldToCell(centerX + radius, centerY + radius, &maxCellX, &maxCellY);
1902+
1903+
Real cellSize = ThePartitionManager->getCellSize();
1904+
1905+
for (Int x = minCellX; x <= maxCellX; ++x)
1906+
{
1907+
for (Int y = minCellY; y <= maxCellY; ++y)
1908+
{
1909+
Real cellWorldX = x * cellSize;
1910+
Real cellWorldY = y * cellSize;
1911+
1912+
if (doesCircleOverlapCell(centerX, centerY, radius, cellWorldX, cellWorldY, cellSize))
1913+
{
1914+
PartitionCell* cell = ThePartitionManager->getCellAt(x, y);
1915+
if (cell)
1916+
{
1917+
addSubPixToCoverage(cell);
1918+
}
1919+
}
1920+
}
1921+
}
1922+
}
1923+
18851924
// -----------------------------------------------------------------------------
18861925
void PartitionData::doSmallFill(
18871926
Real centerX,
@@ -2083,7 +2122,13 @@ void PartitionData::updateCellsTouched()
20832122
case GEOMETRY_SPHERE:
20842123
case GEOMETRY_CYLINDER:
20852124
{
2125+
#if RETAIL_COMPATIBLE_CRC || RETAIL_COMPATIBLE_CIRCLE_FILL_ALGORITHM
20862126
doCircleFill(pos.x, pos.y, majorRadius);
2127+
#else
2128+
// TheSuperHackers @bugfix Stubbjax 29/01/2026 Use precise circle fill to improve
2129+
// collision accuracy, most notably for objects with geometry radii >= 20 and < 40.
2130+
doCircleFillPrecise(pos.x, pos.y, majorRadius);
2131+
#endif
20872132
break;
20882133
}
20892134

0 commit comments

Comments
 (0)