Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions Generals/Code/GameEngine/Include/GameLogic/AIPathfind.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@

class Bridge;
class Object;
class PathfindCell;
class PathfindZoneManager;
class Weapon;
class PathfindZoneManager;
class PathfindCell;

// How close is close enough when moving.

Expand Down Expand Up @@ -281,9 +281,9 @@ class PathfindCell
CELL_WATER = 0x01, ///< water area
CELL_CLIFF = 0x02, ///< steep altitude change
CELL_RUBBLE = 0x03, ///< Cell is occupied by rubble.
CELL_OBSTACLE = 0x04, ///< impassable area
CELL_unused = 0x08, ///< Unused.
CELL_IMPASSABLE = 0x0B ///< Just plain impassable except for aircraft.
CELL_OBSTACLE = 0x04, ///< Occupied by a structure
CELL_BRIDGE_IMPASSABLE = 0x05, ///< Piece of a bridge that is impassable.
CELL_IMPASSABLE = 0x06 ///< Just plain impassable except for aircraft.
};

enum CellFlags
Expand All @@ -301,8 +301,8 @@ class PathfindCell
PathfindCell();
~PathfindCell();

void setTypeAsObstacle( Object *obstacle, Bool isFence, const ICoord2D &pos ); ///< flag this cell as an obstacle, from the given one
void removeObstacle( Object *obstacle ); ///< flag this cell as an obstacle, from the given one
Bool setTypeAsObstacle( Object *obstacle, Bool isFence, const ICoord2D &pos ); ///< flag this cell as an obstacle, from the given one
Bool removeObstacle( Object *obstacle ); ///< unflag this cell as an obstacle, from the given one
void setType( CellType type ); ///< set the cell type
CellType getType() const { return (CellType)m_type; } ///< get the cell type
CellFlags getFlags() const { return (CellFlags)m_flags; } ///< get the cell type
Expand Down Expand Up @@ -356,13 +356,13 @@ class PathfindCell
inline UnsignedInt getCostSoFar() const {return m_info->m_costSoFar;}
inline UnsignedInt getTotalCost() const {return m_info->m_totalCost;}

inline void setCostSoFar(UnsignedInt cost) {m_info->m_costSoFar = cost;}
inline void setTotalCost(UnsignedInt cost) {m_info->m_totalCost = cost;}
inline void setCostSoFar(UnsignedInt cost) { if( m_info ) m_info->m_costSoFar = cost;}
inline void setTotalCost(UnsignedInt cost) { if( m_info ) m_info->m_totalCost = cost;}

void setParentCell(PathfindCell* parent);
void clearParentCell();
void setParentCellHierarchical(PathfindCell* parent);
inline PathfindCell* getParentCell() const {return m_info->m_pathParent?m_info->m_pathParent->m_cell: nullptr;}
inline PathfindCell* getParentCell() const {return m_info ? m_info->m_pathParent ? m_info->m_pathParent->m_cell : nullptr : nullptr;}

Bool startPathfind( PathfindCell *goalCell );
Bool getPinched() const {return m_pinched;}
Expand Down Expand Up @@ -590,7 +590,7 @@ class PathfindServicesInterface {
virtual Path *findPath( Object *obj, const LocomotorSet& locomotorSet, const Coord3D *from,
const Coord3D *to )=0; ///< Find a short, valid path between given locations
/** Find a short, valid path to a location NEAR the to location.
This succeds when the destination is unreachable (like inside a building).
This succeeds when the destination is unreachable (like inside a building).
If the destination is unreachable, it will adjust the to point. */
virtual Path *findClosestPath( Object *obj, const LocomotorSet& locomotorSet, const Coord3D *from,
Coord3D *to, Bool blocked, Real pathCostMultiplier, Bool moveAllies )=0;
Expand Down Expand Up @@ -619,7 +619,7 @@ class Pathfinder : PathfindServicesInterface, public Snapshot
private:
virtual Path *findPath( Object *obj, const LocomotorSet& locomotorSet, const Coord3D *from, const Coord3D *to); ///< Find a short, valid path between given locations
/** Find a short, valid path to a location NEAR the to location.
This succeds when the destination is unreachable (like inside a building).
This succeeds when the destination is unreachable (like inside a building).
If the destination is unreachable, it will adjust the to point. */
virtual Path *findClosestPath( Object *obj, const LocomotorSet& locomotorSet, const Coord3D *from,
Coord3D *to, Bool blocked, Real pathCostMultiplier, Bool moveAllies );
Expand Down
105 changes: 47 additions & 58 deletions Generals/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,20 @@
//-------------------------------------------------------------------------------------------------


static inline Bool IS_IMPASSABLE(PathfindCell::CellType type) {
// Return true if cell is impassable to ground units. jba. [8/18/2003]
if (type==PathfindCell::CELL_IMPASSABLE) {
return true;
}
if (type==PathfindCell::CELL_OBSTACLE) {
return true;
}
if (type==PathfindCell::CELL_BRIDGE_IMPASSABLE) {
return true;
}
return false;
}


struct TCheckMovementInfo
{
Expand Down Expand Up @@ -1545,11 +1559,12 @@ inline ObjectID PathfindCell::getObstacleID() const

/**
* Flag this cell as an obstacle, from the given one.
* Return true if cell was flagged.
*/
void PathfindCell::setTypeAsObstacle( Object *obstacle, Bool isFence, const ICoord2D &pos )
Bool PathfindCell::setTypeAsObstacle( Object *obstacle, Bool isFence, const ICoord2D &pos )
{
if (m_type!=PathfindCell::CELL_CLEAR && m_type != PathfindCell::CELL_IMPASSABLE) {
return;
return false;
}

Bool isRubble = false;
Expand All @@ -1565,15 +1580,15 @@ void PathfindCell::setTypeAsObstacle( Object *obstacle, Bool isFence, const ICoo
m_obstacleIsTransparent = false;
#if RETAIL_COMPATIBLE_PATHFINDING_ALLOCATION
if (s_useFixedPathfinding) {
return;
return true;
}

if (m_info) {
m_info->m_obstacleID = INVALID_ID;
releaseInfo();
}
#endif
return;
return true;
}

m_type = PathfindCell::CELL_OBSTACLE;
Expand All @@ -1584,21 +1599,21 @@ void PathfindCell::setTypeAsObstacle( Object *obstacle, Bool isFence, const ICoo
// TheSuperHackers @info In retail mode we need to track orphaned cells set as obstacles so we can cleanup and failover properly
// So we always make sure to set and clear the local obstacle data on the PathfindCell regardless of retail compat or not
if (s_useFixedPathfinding) {
return;
return true;
}

if (!m_info) {
m_info = PathfindCellInfo::getACellInfo(this, pos);
if (!m_info) {
DEBUG_CRASH(("Not enough PathFindCellInfos in pool."));
return;
return false;
}
}
m_info->m_obstacleID = obstacle->getID();
m_info->m_obstacleIsFence = isFence;
m_info->m_obstacleIsTransparent = obstacle->isKindOf(KINDOF_CAN_SEE_THROUGH_STRUCTURE);
#endif
return;
return true;
}

/**
Expand Down Expand Up @@ -1632,36 +1647,37 @@ void PathfindCell::setType( CellType type )

/**
* Unflag this cell as an obstacle, from the given one.
* Return true if this cell was previously flagged as an obstacle by this object.
*/
void PathfindCell::removeObstacle( Object *obstacle )
Bool PathfindCell::removeObstacle( Object *obstacle )
{
if (m_type == PathfindCell::CELL_RUBBLE) {
m_type = PathfindCell::CELL_CLEAR;
}
#if RETAIL_COMPATIBLE_PATHFINDING_ALLOCATION
if (s_useFixedPathfinding) {
if (m_obstacleID != obstacle->getID()) return;
if (m_obstacleID != obstacle->getID()) return false;
m_type = PathfindCell::CELL_CLEAR;
m_obstacleID = INVALID_ID;
m_obstacleIsFence = false;
m_obstacleIsTransparent = false;
return;
return true;
}

if (!m_info) return;
if (m_info->m_obstacleID != obstacle->getID()) return;
if (!m_info) return false;
if (m_info->m_obstacleID != obstacle->getID()) return false;
m_type = PathfindCell::CELL_CLEAR;
m_info->m_obstacleID = INVALID_ID;
releaseInfo();

#else
if (m_obstacleID != obstacle->getID()) return;
if (m_obstacleID != obstacle->getID()) return false;
m_type = PathfindCell::CELL_CLEAR;
#endif
m_obstacleID = INVALID_ID;
m_obstacleIsFence = false;
m_obstacleIsTransparent = false;
return;
return true;
}

/// put self on "open" list in ascending cost order, return new list
Expand Down Expand Up @@ -1941,7 +1957,11 @@ UnsignedInt PathfindCell::costToGoal( PathfindCell *goal )

UnsignedInt PathfindCell::costToHierGoal( PathfindCell *goal )
{
DEBUG_ASSERTCRASH(m_info, ("Has to have info."));
if( !m_info )
{
DEBUG_CRASH( ("Has to have info.") );

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wondering if the debug_crash is still needed with the patch hack.

Copy link
Author

@Mauller Mauller Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I honestly wouldn't be surprised if they were seeing pathfinding crashes due to all the problems we already cleaned up.

Can't wait to get rid of or at least conditionally remove code like this when i get the non allocating pathfinding going.

return 100000; //...patch hack 1.01
}
Int dx = m_info->m_pos.x - goal->getXIndex();
Int dy = m_info->m_pos.y - goal->getYIndex();
Int cost = REAL_TO_INT_FLOOR(COST_ORTHOGONAL*sqrt(dx*dx + dy*dy) + 0.5f);
Expand Down Expand Up @@ -2303,7 +2323,7 @@ void ZoneBlock::blockCalculateZones(PathfindCell **map, PathfindLayer layers[],
zoneStorageType ZoneBlock::getEffectiveZone( LocomotorSurfaceTypeMask acceptableSurfaces,
Bool crusher, zoneStorageType zone) const
{
DEBUG_ASSERTCRASH(zone, ("Zone not set"));

if (acceptableSurfaces&LOCOMOTORSURFACE_AIR) return 1; // air is all zone 1.

if ( (acceptableSurfaces&LOCOMOTORSURFACE_GROUND) &&
Expand Down Expand Up @@ -2581,9 +2601,6 @@ void PathfindZoneManager::calculateZones( PathfindCell **map, PathfindLayer laye
}

Int totalZones = m_maxZone;
if (totalZones>maxZones/2) {
DEBUG_LOG(("Max zones %d", m_maxZone));
}

// Collapse the zones into a 1,2,3... sequence, removing collapsed zones.
m_maxZone = 1;
Expand All @@ -2598,21 +2615,12 @@ void PathfindZoneManager::calculateZones( PathfindCell **map, PathfindLayer laye
collapsedZones[i] = collapsedZones[zone];
}
}
#ifdef DEBUG_QPF
#if defined(DEBUG_LOGGING)
QueryPerformanceCounter((LARGE_INTEGER *)&endTime64);
timeToUpdate = ((double)(endTime64-startTime64) / (double)(freq64));
DEBUG_LOG(("Time to calculate first %f", timeToUpdate));
#endif
#endif

// Now map the zones in the map back into the collapsed zones.
for( j=globalBounds.lo.y; j<=globalBounds.hi.y; j++ ) {
for( i=globalBounds.lo.x; i<=globalBounds.hi.x; i++ ) {
PathfindCell &cell = map[i][j];
cell.setZone(collapsedZones[cell.getZone()]);
if (map[i][j].getZone()==0) {
DEBUG_CRASH(("Zone not set cell %d, %d", i, j));
}
}
}
for (i=0; i<=LAYER_LAST; i++) {
Expand All @@ -2625,9 +2633,6 @@ void PathfindZoneManager::calculateZones( PathfindCell **map, PathfindLayer laye
}

r_thisLayer.setZone( zone );
if (!layers[i].isUnused() && !layers[i].isDestroyed() && layers[i].getZone()==0) {
DEBUG_CRASH(("Zone not set Layer %d", i));
}
r_thisLayer.applyZone();

if (!r_thisLayer.isUnused() && !r_thisLayer.isDestroyed()) {
Expand All @@ -2640,7 +2645,7 @@ void PathfindZoneManager::calculateZones( PathfindCell **map, PathfindLayer laye
}

allocateZones();
DEBUG_ASSERTCRASH(xBlock==m_zoneBlockExtent.x && yBlock==m_zoneBlockExtent.y, ("Inconsistent allocation - SERIOUS ERROR. jba"));

for (xBlock=0; xBlock<xCount; xBlock++) {
for (yBlock=0; yBlock<yCount; yBlock++) {
IRegion2D bounds;
Expand All @@ -2662,14 +2667,6 @@ void PathfindZoneManager::calculateZones( PathfindCell **map, PathfindLayer laye
}
}

#ifdef DEBUG_QPF
#if defined(DEBUG_LOGGING)
QueryPerformanceCounter((LARGE_INTEGER *)&endTime64);
timeToUpdate = ((double)(endTime64-startTime64) / (double)(freq64));
DEBUG_LOG(("Time to calculate second %f", timeToUpdate));
#endif
#endif

// Determine water/ground equivalent zones, and ground/cliff equivalent zones.
for (i=0; i<m_zonesAllocated; i++) {
m_groundCliffZones[i] = i;
Expand Down Expand Up @@ -2700,11 +2697,6 @@ void PathfindZoneManager::calculateZones( PathfindCell **map, PathfindLayer laye
applyZone(r_thisCell, r_leftCell, m_groundWaterZones, m_maxZone);
}
if (groundRubble(r_thisCell, r_leftCell)) {
Int zone1 = r_thisCell.getZone();
Int zone2 = r_leftCell.getZone();
if (m_terrainZones[zone1] != m_terrainZones[zone2]) {
//DEBUG_LOG(("Matching terrain zone %d to %d.", zone1, zone2));
}
applyZone(r_thisCell, r_leftCell, m_groundRubbleZones, m_maxZone);
}
if (groundCliff(r_thisCell, r_leftCell)) {
Expand All @@ -2728,11 +2720,6 @@ void PathfindZoneManager::calculateZones( PathfindCell **map, PathfindLayer laye
applyZone(r_thisCell, r_topCell, m_groundWaterZones, m_maxZone);
}
if (groundRubble(r_thisCell, r_topCell)) {
Int zone1 = r_thisCell.getZone();
Int zone2 = r_topCell.getZone();
if (m_terrainZones[zone1] != m_terrainZones[zone2]) {
//DEBUG_LOG(("Matching terrain zone %d to %d.", zone1, zone2));
}
applyZone(r_thisCell, r_topCell, m_groundRubbleZones, m_maxZone);
}
if (groundCliff(r_thisCell, r_topCell)) {
Expand All @@ -2749,9 +2736,6 @@ void PathfindZoneManager::calculateZones( PathfindCell **map, PathfindLayer laye
}
}

if (m_maxZone >= m_zonesAllocated) {
RELEASE_CRASH("Pathfind allocation error - fatal. see jba.");
}
for (i=1; i<m_maxZone; i++) {
// Flatten hierarchical zones.
Int zone = m_hierarchicalZones[i];
Expand Down Expand Up @@ -3122,6 +3106,9 @@ void PathfindLayer::doDebugIcons() {
} else if (cell->getType() == PathfindCell::CELL_IMPASSABLE) {
color.red = color.green = color.blue = 1;
empty = false;
} else if (cell->getType() == PathfindCell::CELL_BRIDGE_IMPASSABLE) {
color.blue = color.red = 1;
empty = false;
} else if (cell->getType() == PathfindCell::CELL_CLIFF) {
color.red = 1;
empty = false;
Expand Down Expand Up @@ -4422,6 +4409,7 @@ Locomotor* Pathfinder::chooseBestLocomotorForPosition(PathfindLayerEnum layer, L
return LOCOMOTORSURFACE_RUBBLE | LOCOMOTORSURFACE_AIR;

case PathfindCell::CELL_OBSTACLE:
case PathfindCell::CELL_BRIDGE_IMPASSABLE:
case PathfindCell::CELL_IMPASSABLE:
return LOCOMOTORSURFACE_AIR;

Expand Down Expand Up @@ -5345,6 +5333,10 @@ void Pathfinder::doDebugIcons() {
color.red = 1;
empty = false;
break;
case PathfindCell::CELL_BRIDGE_IMPASSABLE:
color.blue = color.red = 1;
empty = false;
break;
case PathfindCell::CELL_IMPASSABLE:
color.green = 1;
empty = false;
Expand Down Expand Up @@ -9182,7 +9174,6 @@ void Pathfinder::updateGoal( Object *obj, const Coord3D *newGoalPos, PathfindLay

PathfindLayerEnum originalLayer = obj->getDestinationLayer();

//DEBUG_LOG(("Object Goal layer is %d", layer));
Bool layerChanged = originalLayer != layer;

Bool doGround=false;
Expand Down Expand Up @@ -10257,9 +10248,7 @@ Path *Pathfinder::findAttackPath( const Object *obj, const LocomotorSet& locomot
#endif
if (show)
debugShowSearch(true);
#if defined(RTS_DEBUG)
//DEBUG_LOG(("Attack path took %d cells, %f sec", cellCount, (::GetTickCount()-startTimeMS)/1000.0f));
#endif

// construct and return path
Path *path = buildActualPath( obj, locomotorSet.getValidSurfaces(), obj->getPosition(), parentCell, centerInCell, false);
#if RETAIL_COMPATIBLE_PATHFINDING
Expand Down
2 changes: 1 addition & 1 deletion GeneralsMD/Code/GameEngine/Include/GameLogic/AIPathfind.h
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@ class PathfindZoneManager
void reset();

Bool needToCalculateZones() const {return m_nextFrameToCalculateZones <= TheGameLogic->getFrame() ;} ///< Returns true if the zones need to be recalculated.
void markZonesDirty( Bool insert ) ; ///< Called when the zones need to be recalculated.
void markZonesDirty() ; ///< Called when the zones need to be recalculated.
void updateZonesForModify( PathfindCell **map, PathfindLayer layers[], const IRegion2D &structureBounds, const IRegion2D &globalBounds ) ; ///< Called to recalculate an area when a structure has been removed.
void calculateZones( PathfindCell **map, PathfindLayer layers[], const IRegion2D &bounds); ///< Does zone calculations.
zoneStorageType getEffectiveZone(LocomotorSurfaceTypeMask acceptableSurfaces, Bool crusher, zoneStorageType zone) const;
Expand Down
Loading
Loading