From 1dbd3e0cb3df43b7e66cfb397e86973b0028a47e Mon Sep 17 00:00:00 2001 From: Mauller <26652186+Mauller@users.noreply.github.com> Date: Mon, 16 Feb 2026 19:00:52 +0000 Subject: [PATCH 1/2] refactor(pathfinder): Implement PathfindCellList class for the pathfindcell openList (#2327) --- .../GameEngine/Include/GameLogic/AIPathfind.h | 28 +- .../Source/GameLogic/AI/AIPathfind.cpp | 272 ++++++++++++------ .../GameEngine/Include/GameLogic/AIPathfind.h | 28 +- .../Source/GameLogic/AI/AIPathfind.cpp | 272 ++++++++++++------ 4 files changed, 406 insertions(+), 194 deletions(-) diff --git a/Generals/Code/GameEngine/Include/GameLogic/AIPathfind.h b/Generals/Code/GameEngine/Include/GameLogic/AIPathfind.h index d147c9650c5..7a8805c2df6 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/AIPathfind.h +++ b/Generals/Code/GameEngine/Include/GameLogic/AIPathfind.h @@ -247,6 +247,24 @@ class PathfindCellInfo UnsignedInt m_closed:1; ///< place for marking this cell as on the closed list }; +// TheSuperHackers @info The PathfindCellList class acts as a new management class for the pathfindcell open and closed lists +class PathfindCellList +{ + friend class PathfindCell; + +public: + PathfindCellList() : m_head(nullptr) {} + + void reset(PathfindCell* newHead = nullptr) { m_head = newHead; } + + PathfindCell* getHead() const { return m_head; } + + Bool empty() const { return m_head == nullptr; } + +private: + PathfindCell* m_head; +}; + /** * This represents one cell in the pathfinding grid. * These cells categorize the world into idealized cellular states, @@ -307,11 +325,11 @@ class PathfindCell UnsignedInt costSoFar( PathfindCell *parent ); - /// put self on "open" list in ascending cost order, return new list - PathfindCell *putOnSortedOpenList( PathfindCell *list ); + /// put self on "open" list in ascending cost order + void putOnSortedOpenList( PathfindCellList &list ); /// remove self from "open" list - PathfindCell *removeFromOpenList( PathfindCell *list ); + void removeFromOpenList( PathfindCellList &list ); /// put self on "closed" list, return new list PathfindCell *putOnClosedList( PathfindCell *list ); @@ -323,7 +341,7 @@ class PathfindCell static Int releaseClosedList( PathfindCell *list ); /// remove all cells from closed list. - static Int releaseOpenList( PathfindCell *list ); + static Int releaseOpenList( PathfindCellList &list ); inline PathfindCell *getNextOpen(void) {return m_info->m_nextOpen?m_info->m_nextOpen->m_cell: nullptr;} @@ -848,7 +866,7 @@ class Pathfinder : PathfindServicesInterface, public Snapshot IRegion2D m_extent; ///< Grid extent limits IRegion2D m_logicalExtent; ///< Logical grid extent limits - PathfindCell *m_openList; ///< Cells ready to be explored + PathfindCellList m_openList; ///< Cells ready to be explored PathfindCell *m_closedList; ///< Cells already explored Bool m_isMapReady; ///< True if all cells of map have been classified diff --git a/Generals/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp b/Generals/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp index 2e2dc8822cd..98b32987867 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp @@ -1117,7 +1117,7 @@ void Pathfinder::forceCleanCells() TheAudio->addAudioEvent(&TheAudio->getMiscAudio()->m_allCheerSound); PathfindCellInfo::forceCleanPathFindCellInfos(); - m_openList = nullptr; + m_openList.reset(); m_closedList = nullptr; for (int j = 0; j <= m_extent.hi.y; ++j) { @@ -1665,13 +1665,13 @@ void PathfindCell::removeObstacle( Object *obstacle ) } /// put self on "open" list in ascending cost order, return new list -PathfindCell *PathfindCell::putOnSortedOpenList( PathfindCell *list ) +void PathfindCell::putOnSortedOpenList( PathfindCellList &list ) { DEBUG_ASSERTCRASH(m_info, ("Has to have info.")); DEBUG_ASSERTCRASH(m_info->m_closed==FALSE && m_info->m_open==FALSE, ("Serious error - Invalid flags. jba")); - if (list == nullptr) + if (list.m_head == nullptr) { - list = this; + list.m_head = this; m_info->m_prevOpen = nullptr; m_info->m_nextOpen = nullptr; } @@ -1684,11 +1684,11 @@ PathfindCell *PathfindCell::putOnSortedOpenList( PathfindCell *list ) // External code should pickup on the bad behaviour and cleanup properly, but we need to explicitly break out here // The fixed pathfinding does not have this issue due to the proper cleanup of pathfindCells and their pathfindCellInfos UnsignedInt cellCount = 0; - for (c = list; c && cellCount < PATHFIND_CELLS_PER_FRAME; c = c->getNextOpen()) + for (c = list.m_head; c && cellCount < PATHFIND_CELLS_PER_FRAME; c = c->getNextOpen()) { cellCount++; #else - for (c = list; c; c = c->getNextOpen()) + for (c = list.m_head; c; c = c->getNextOpen()) { #endif if (c->m_info->m_totalCost > m_info->m_totalCost) @@ -1703,7 +1703,7 @@ PathfindCell *PathfindCell::putOnSortedOpenList( PathfindCell *list ) if (c->m_info->m_prevOpen) c->m_info->m_prevOpen->m_nextOpen = this->m_info; else - list = this; + list.m_head = this; m_info->m_prevOpen = c->m_info->m_prevOpen; c->m_info->m_prevOpen = this->m_info; @@ -1724,11 +1724,10 @@ PathfindCell *PathfindCell::putOnSortedOpenList( PathfindCell *list ) m_info->m_open = true; m_info->m_closed = false; - return list; } /// remove self from "open" list -PathfindCell *PathfindCell::removeFromOpenList( PathfindCell *list ) +void PathfindCell::removeFromOpenList( PathfindCellList &list ) { DEBUG_ASSERTCRASH(m_info, ("Has to have info.")); DEBUG_ASSERTCRASH(m_info->m_closed==FALSE && m_info->m_open==TRUE, ("Serious error - Invalid flags. jba")); @@ -1738,25 +1737,24 @@ PathfindCell *PathfindCell::removeFromOpenList( PathfindCell *list ) if (m_info->m_prevOpen) m_info->m_prevOpen->m_nextOpen = m_info->m_nextOpen; else - list = getNextOpen(); + list.m_head = getNextOpen(); m_info->m_open = false; m_info->m_nextOpen = nullptr; m_info->m_prevOpen = nullptr; - return list; } /// remove all cells from "open" list -Int PathfindCell::releaseOpenList( PathfindCell *list ) +Int PathfindCell::releaseOpenList( PathfindCellList &list ) { Int count = 0; - while (list) { + while (list.m_head) { count++; - DEBUG_ASSERTCRASH(list->m_info, ("Has to have info.")); - DEBUG_ASSERTCRASH(list->m_info->m_closed==FALSE && list->m_info->m_open==TRUE, ("Serious error - Invalid flags. jba")); - PathfindCell *cur = list; - PathfindCellInfo *curInfo = list->m_info; + DEBUG_ASSERTCRASH(list.m_head->m_info, ("Has to have info.")); + DEBUG_ASSERTCRASH(list.m_head->m_info->m_closed==FALSE && list.m_head->m_info->m_open==TRUE, ("Serious error - Invalid flags. jba")); + PathfindCell *cur = list.m_head; + PathfindCellInfo *curInfo = list.m_head->m_info; #if RETAIL_COMPATIBLE_PATHFINDING // TheSuperHackers @info This is only here to catch a crash point in the retail compatible pathfinding @@ -1770,9 +1768,9 @@ Int PathfindCell::releaseOpenList( PathfindCell *list ) #endif if (curInfo->m_nextOpen) { - list = curInfo->m_nextOpen->m_cell; + list.m_head = curInfo->m_nextOpen->m_cell; } else { - list = nullptr; + list.m_head = nullptr; } DEBUG_ASSERTCRASH(cur == curInfo->m_cell, ("Bad backpointer in PathfindCellInfo")); curInfo->m_nextOpen = nullptr; @@ -3654,7 +3652,7 @@ void Pathfinder::reset( void ) // reset the pathfind grid m_extent.lo.x=m_extent.lo.y=m_extent.hi.x=m_extent.hi.y=0; m_logicalExtent.lo.x=m_logicalExtent.lo.y=m_logicalExtent.hi.x=m_logicalExtent.hi.y=0; - m_openList = nullptr; + m_openList.reset(); m_closedList = nullptr; m_ignoreObstacleID = INVALID_ID; @@ -4351,7 +4349,7 @@ void Pathfinder::debugShowSearch( Bool pathFound ) addIcon(nullptr, 0, 0, color); // erase. } - for( s = m_openList; s; s=s->getNextOpen() ) + for( s = m_openList.getHead(); s; s=s->getNextOpen() ) { // create objects to show path - they decay RGBColor color; @@ -4454,9 +4452,9 @@ Bool Pathfinder::validMovementTerrain( PathfindLayerEnum layer, const Locomotor* // void Pathfinder::cleanOpenAndClosedLists(void) { Int count = 0; - if (m_openList) { + if (!m_openList.empty()) { count += PathfindCell::releaseOpenList(m_openList); - m_openList = nullptr; + m_openList.reset(); } #if RETAIL_COMPATIBLE_PATHFINDING @@ -5579,7 +5577,7 @@ void Pathfinder::checkChangeLayers(PathfindCell *parentCell) newCell->setCostSoFar(parentCell->getCostSoFar()); // same as parent cost newCell->setTotalCost(parentCell->getTotalCost()); // insert newCell in open list such that open list is sorted, smallest total path cost first - m_openList = newCell->putOnSortedOpenList( m_openList ); + newCell->putOnSortedOpenList( m_openList ); } bool Pathfinder::checkCellOutsideExtents(ICoord2D& cell) { @@ -5682,10 +5680,10 @@ struct ExamineCellsStruct // if the to was already on the open list, remove it so it can be re-inserted in order if (to->getOpen()) - d->thePathfinder->m_openList = to->removeFromOpenList( d->thePathfinder->m_openList ); + to->removeFromOpenList( d->thePathfinder->m_openList ); // insert to in open list such that open list is sorted, smallest total path cost first - d->thePathfinder->m_openList = to->putOnSortedOpenList( d->thePathfinder->m_openList ); + to->putOnSortedOpenList( d->thePathfinder->m_openList ); } return 0; // keep going @@ -5916,10 +5914,10 @@ Int Pathfinder::examineNeighboringCells(PathfindCell *parentCell, PathfindCell * // if the newCell was already on the open list, remove it so it can be re-inserted in order if (newCell->getOpen()) - m_openList = newCell->removeFromOpenList( m_openList ); + newCell->removeFromOpenList( m_openList ); // insert newCell in open list such that open list is sorted, smallest total path cost first - m_openList = newCell->putOnSortedOpenList( m_openList ); + newCell->putOnSortedOpenList( m_openList ); } return cellCount; } @@ -5985,7 +5983,7 @@ Path *Pathfinder::internalFindPath( Object *obj, const LocomotorSet& locomotorSe DEBUG_LOG(("Attempting pathfind to 0,0, generally a bug.")); return nullptr; } - DEBUG_ASSERTCRASH(m_openList== nullptr && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); if (m_isMapReady == false) { return nullptr; } @@ -6097,7 +6095,16 @@ Path *Pathfinder::internalFindPath( Object *obj, const LocomotorSet& locomotorSe parentCell->startPathfind(goalCell); // initialize "open" list to contain start cell - m_openList = parentCell; +#if RETAIL_COMPATIBLE_PATHFINDING + if (!s_useFixedPathfinding) { + m_openList.reset(parentCell); + } + else +#endif + { + m_openList.reset(); + parentCell->putOnSortedOpenList(m_openList); + } // "closed" list is initially empty m_closedList = nullptr; @@ -6108,11 +6115,11 @@ Path *Pathfinder::internalFindPath( Object *obj, const LocomotorSet& locomotorSe // Continue search until "open" list is empty, or // until goal is found. // - while( m_openList != nullptr ) + while( !m_openList.empty() ) { // take head cell off of open list - it has lowest estimated total path cost - parentCell = m_openList; - m_openList = parentCell->removeFromOpenList(m_openList); + parentCell = m_openList.getHead(); + parentCell->removeFromOpenList(m_openList); if (parentCell == goalCell) { @@ -6491,7 +6498,7 @@ struct GroundCellsStruct to->setTotalCost(to->getCostSoFar() + costRemaining) ; // insert to in open list such that open list is sorted, smallest total path cost first - d->thePathfinder->m_openList = to->putOnSortedOpenList( d->thePathfinder->m_openList ); + to->putOnSortedOpenList( d->thePathfinder->m_openList ); } return 0; // keep going @@ -6527,7 +6534,7 @@ Path *Pathfinder::findGroundPath( const Coord3D *from, DEBUG_LOG(("Attempting pathfind to 0,0, generally a bug.")); return nullptr; } - DEBUG_ASSERTCRASH(m_openList== nullptr && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); if (m_isMapReady == false) { return nullptr; } @@ -6620,7 +6627,16 @@ Path *Pathfinder::findGroundPath( const Coord3D *from, parentCell->startPathfind(goalCell); // initialize "open" list to contain start cell - m_openList = parentCell; +#if RETAIL_COMPATIBLE_PATHFINDING + if (!s_useFixedPathfinding) { + m_openList.reset(parentCell); + } + else +#endif + { + m_openList.reset(); + parentCell->putOnSortedOpenList(m_openList); + } // "closed" list is initially empty m_closedList = nullptr; @@ -6635,11 +6651,11 @@ Path *Pathfinder::findGroundPath( const Coord3D *from, // until goal is found. // Int cellCount = 0; - while( m_openList != nullptr ) + while( !m_openList.empty() ) { // take head cell off of open list - it has lowest estimated total path cost - parentCell = m_openList; - m_openList = parentCell->removeFromOpenList(m_openList); + parentCell = m_openList.getHead(); + parentCell->removeFromOpenList(m_openList); if (parentCell == goalCell) { @@ -6811,10 +6827,10 @@ Path *Pathfinder::findGroundPath( const Coord3D *from, // if the newCell was already on the open list, remove it so it can be re-inserted in order if (newCell->getOpen()) - m_openList = newCell->removeFromOpenList( m_openList ); + newCell->removeFromOpenList( m_openList ); // insert newCell in open list such that open list is sorted, smallest total path cost first - m_openList = newCell->putOnSortedOpenList( m_openList ); + newCell->putOnSortedOpenList( m_openList ); } } // failure - goal cannot be reached @@ -6958,7 +6974,7 @@ void Pathfinder::processHierarchicalCell( const ICoord2D &scanCell, const ICoord adjNewCell->setTotalCost(adjNewCell->getCostSoFar()+remCost); adjNewCell->setParentCellHierarchical(parentCell); // insert newCell in open list such that open list is sorted, smallest total path cost first - m_openList = adjNewCell->putOnSortedOpenList( m_openList ); + adjNewCell->putOnSortedOpenList( m_openList ); } } @@ -7003,7 +7019,7 @@ Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSu DEBUG_LOG(("Attempting pathfind to 0,0, generally a bug.")); return nullptr; } - DEBUG_ASSERTCRASH(m_openList== nullptr && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); if (m_isMapReady == false) { return nullptr; } @@ -7079,7 +7095,16 @@ Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSu } // initialize "open" list to contain start cell - m_openList = parentCell; +#if RETAIL_COMPATIBLE_PATHFINDING + if (!s_useFixedPathfinding) { + m_openList.reset(parentCell); + } + else +#endif + { + m_openList.reset(); + parentCell->putOnSortedOpenList(m_openList); + } if (parentCell->getLayer()!=LAYER_GROUND) { PathfindLayerEnum layer = parentCell->getLayer(); @@ -7092,7 +7117,7 @@ Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSu PathfindCell *startCell = getCell(LAYER_GROUND, ndx.x, ndx.y); if (cell && startCell) { // Close parent cell; - m_openList = parentCell->removeFromOpenList(m_openList); + parentCell->removeFromOpenList(m_openList); m_closedList = parentCell->putOnClosedList(m_closedList); if (!startCell->allocateInfo(ndx)) { // TheSuperHackers @info We need to forcefully cleanup dangling pathfinding cells if this failure condition is hit in retail @@ -7118,7 +7143,7 @@ Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSu startCell->setTotalCost(remCost); startCell->setParentCellHierarchical(parentCell); // insert newCell in open list such that open list is sorted, smallest total path cost first - m_openList = startCell->putOnSortedOpenList( m_openList ); + startCell->putOnSortedOpenList( m_openList ); cellCount++; if(!cell->allocateInfo(toNdx)) { @@ -7143,7 +7168,7 @@ Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSu cell->setTotalCost(remCost); cell->setParentCellHierarchical(parentCell); // insert newCell in open list such that open list is sorted, smallest total path cost first - m_openList = cell->putOnSortedOpenList( m_openList ); + cell->putOnSortedOpenList( m_openList ); } } @@ -7154,11 +7179,11 @@ Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSu // Continue search until "open" list is empty, or // until goal is found. // - while( m_openList != nullptr ) + while( !m_openList.empty() ) { // take head cell off of open list - it has lowest estimated total path cost - parentCell = m_openList; - m_openList = parentCell->removeFromOpenList(m_openList); + parentCell = m_openList.getHead(); + parentCell->removeFromOpenList(m_openList); zoneStorageType parentZone; if (parentCell->getLayer()==LAYER_GROUND) { @@ -7271,7 +7296,7 @@ Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSu cell->setTotalCost(cell->getCostSoFar()+remCost); cell->setParentCellHierarchical(startCell); // insert newCell in open list such that open list is sorted, smallest total path cost first - m_openList = cell->putOnSortedOpenList( m_openList ); + cell->putOnSortedOpenList( m_openList ); } } @@ -7711,7 +7736,7 @@ Bool Pathfinder::pathDestination( Object *obj, const LocomotorSet& locomotorSet Coord3D adjustTo = *groupDest; Coord3D *to = &adjustTo; - DEBUG_ASSERTCRASH(m_openList== nullptr && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); // create unique "mark" values for open and closed cells for this pathfind invocation Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false; @@ -7774,7 +7799,16 @@ Bool Pathfinder::pathDestination( Object *obj, const LocomotorSet& locomotorSet parentCell->startPathfind(goalCell); // initialize "open" list to contain start cell - m_openList = parentCell; +#if RETAIL_COMPATIBLE_PATHFINDING + if (!s_useFixedPathfinding) { + m_openList.reset(parentCell); + } + else +#endif + { + m_openList.reset(); + parentCell->putOnSortedOpenList(m_openList); + } // "closed" list is initially empty m_closedList = nullptr; @@ -7783,11 +7817,11 @@ Bool Pathfinder::pathDestination( Object *obj, const LocomotorSet& locomotorSet // Continue search until "open" list is empty, or // until goal is found. // - while( m_openList != nullptr ) + while( !m_openList.empty() ) { // take head cell off of open list - it has lowest estimated total path cost - parentCell = m_openList; - m_openList = parentCell->removeFromOpenList(m_openList); + parentCell = m_openList.getHead(); + parentCell->removeFromOpenList(m_openList); Coord3D pos; // put parent cell onto closed list - its evaluation is finished @@ -7912,10 +7946,10 @@ Bool Pathfinder::pathDestination( Object *obj, const LocomotorSet& locomotorSet // if the newCell was already on the open list, remove it so it can be re-inserted in order if (newCell->getOpen()) - m_openList = newCell->removeFromOpenList( m_openList ); + newCell->removeFromOpenList( m_openList ); // insert newCell in open list such that open list is sorted, smallest total path cost first - m_openList = newCell->putOnSortedOpenList( m_openList ); + newCell->putOnSortedOpenList( m_openList ); } } @@ -8005,7 +8039,7 @@ Int Pathfinder::checkPathCost(Object *obj, const LocomotorSet& locomotorSet, con Coord3D adjustTo = *rawTo; Coord3D *to = &adjustTo; - DEBUG_ASSERTCRASH(m_openList== nullptr && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); // create unique "mark" values for open and closed cells for this pathfind invocation Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false; @@ -8050,7 +8084,16 @@ Int Pathfinder::checkPathCost(Object *obj, const LocomotorSet& locomotorSet, con parentCell->startPathfind(goalCell); // initialize "open" list to contain start cell - m_openList = parentCell; +#if RETAIL_COMPATIBLE_PATHFINDING + if (!s_useFixedPathfinding) { + m_openList.reset(parentCell); + } + else +#endif + { + m_openList.reset(); + parentCell->putOnSortedOpenList(m_openList); + } // "closed" list is initially empty m_closedList = nullptr; @@ -8059,11 +8102,11 @@ Int Pathfinder::checkPathCost(Object *obj, const LocomotorSet& locomotorSet, con // Continue search until "open" list is empty, or // until goal is found. // - while( m_openList != nullptr ) + while( !m_openList.empty() ) { // take head cell off of open list - it has lowest estimated total path cost - parentCell = m_openList; - m_openList = parentCell->removeFromOpenList(m_openList); + parentCell = m_openList.getHead(); + parentCell->removeFromOpenList(m_openList); // put parent cell onto closed list - its evaluation is finished - Retail compatible behaviour #if RETAIL_COMPATIBLE_PATHFINDING @@ -8197,12 +8240,12 @@ Int Pathfinder::checkPathCost(Object *obj, const LocomotorSet& locomotorSet, con // if the newCell was already on the open list, remove it so it can be re-inserted in order if (newCell->getOpen()) - m_openList = newCell->removeFromOpenList( m_openList ); + newCell->removeFromOpenList( m_openList ); #if RETAIL_COMPATIBLE_PATHFINDING // TheSuperHacker @info This is here to catch a retail pathfinding crash point and to recover from it // A cell has gotten onto the open list without pathfinding info due to a danling m_open pointer on the previous listed cell so we need to force a cleanup - if (!s_useFixedPathfinding && m_openList && !m_openList->hasInfo()) { + if (!s_useFixedPathfinding && m_openList.getHead() && !m_openList.getHead()->hasInfo()) { s_useFixedPathfinding = true; forceCleanCells(); return MAX_COST; @@ -8210,7 +8253,7 @@ Int Pathfinder::checkPathCost(Object *obj, const LocomotorSet& locomotorSet, con #endif // insert newCell in open list such that open list is sorted, smallest total path cost first - m_openList = newCell->putOnSortedOpenList( m_openList ); + newCell->putOnSortedOpenList( m_openList ); } } @@ -8274,7 +8317,7 @@ Path *Pathfinder::findClosestPath( Object *obj, const LocomotorSet& locomotorSet adjustTo.x += PATHFIND_CELL_SIZE_F/2; adjustTo.y += PATHFIND_CELL_SIZE_F/2; } - DEBUG_ASSERTCRASH(m_openList== nullptr && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); // create unique "mark" values for open and closed cells for this pathfind invocation Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false; @@ -8379,7 +8422,16 @@ Path *Pathfinder::findClosestPath( Object *obj, const LocomotorSet& locomotorSet Real closestDistScreenSqr = FLT_MAX; // initialize "open" list to contain start cell - m_openList = parentCell; +#if RETAIL_COMPATIBLE_PATHFINDING + if (!s_useFixedPathfinding) { + m_openList.reset(parentCell); + } + else +#endif + { + m_openList.reset(); + parentCell->putOnSortedOpenList(m_openList); + } // "closed" list is initially empty m_closedList = nullptr; @@ -8388,14 +8440,14 @@ Path *Pathfinder::findClosestPath( Object *obj, const LocomotorSet& locomotorSet // Continue search until "open" list is empty, or // until goal is found. // - while( m_openList != nullptr ) + while( !m_openList.empty() ) { Real dx; Real dy; Real distSqr; // take head cell off of open list - it has lowest estimated total path cost - parentCell = m_openList; - m_openList = parentCell->removeFromOpenList(m_openList); + parentCell = m_openList.getHead(); + parentCell->removeFromOpenList(m_openList); if (parentCell == goalCell) { @@ -9849,7 +9901,7 @@ Path *Pathfinder::getMoveAwayFromPath(Object* obj, Object *otherObj, Int radius; getRadiusAndCenter(obj, radius, centerInCell); - DEBUG_ASSERTCRASH(m_openList== nullptr && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); // determine start cell ICoord2D startCellNdx; @@ -9890,7 +9942,16 @@ Path *Pathfinder::getMoveAwayFromPath(Object* obj, Object *otherObj, parentCell->startPathfind(nullptr); // initialize "open" list to contain start cell - m_openList = parentCell; +#if RETAIL_COMPATIBLE_PATHFINDING + if (!s_useFixedPathfinding) { + m_openList.reset(parentCell); + } + else +#endif + { + m_openList.reset(); + parentCell->putOnSortedOpenList(m_openList); + } // "closed" list is initially empty m_closedList = nullptr; @@ -9905,11 +9966,11 @@ Path *Pathfinder::getMoveAwayFromPath(Object* obj, Object *otherObj, boxHalfWidth += otherRadius*PATHFIND_CELL_SIZE_F; if (otherCenter) boxHalfWidth+=PATHFIND_CELL_SIZE_F/2; - while( m_openList != nullptr ) + while( !m_openList.empty() ) { // take head cell off of open list - it has lowest estimated total path cost - parentCell = m_openList; - m_openList = parentCell->removeFromOpenList(m_openList); + parentCell = m_openList.getHead(); + parentCell->removeFromOpenList(m_openList); Region2D bounds; Coord3D cellCenter; @@ -10026,7 +10087,7 @@ Path *Pathfinder::patchPath( const Object *obj, const LocomotorSet& locomotorSet m_zoneManager.setAllPassable(); - DEBUG_ASSERTCRASH(m_openList== nullptr && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); enum {CELL_LIMIT = 2000}; // max cells to examine. Int cellCount = 0; @@ -10057,7 +10118,16 @@ Path *Pathfinder::patchPath( const Object *obj, const LocomotorSet& locomotorSet parentCell->startPathfind( nullptr); // initialize "open" list to contain start cell - m_openList = parentCell; +#if RETAIL_COMPATIBLE_PATHFINDING + if (!s_useFixedPathfinding) { + m_openList.reset(parentCell); + } + else +#endif + { + m_openList.reset(); + parentCell->putOnSortedOpenList(m_openList); + } // "closed" list is initially empty m_closedList = nullptr; @@ -10141,11 +10211,11 @@ Path *Pathfinder::patchPath( const Object *obj, const LocomotorSet& locomotorSet return nullptr; } - while( m_openList != nullptr ) + while( !m_openList.empty() ) { // take head cell off of open list - it has lowest estimated total path cost - parentCell = m_openList; - m_openList = parentCell->removeFromOpenList(m_openList); + parentCell = m_openList.getHead(); + parentCell->removeFromOpenList(m_openList); Coord3D cellCenter; adjustCoordToCell(parentCell->getXIndex(), parentCell->getYIndex(), centerInCell, cellCenter, parentCell->getLayer()); @@ -10300,7 +10370,7 @@ Path *Pathfinder::findAttackPath( const Object *obj, const LocomotorSet& locomot Int cellCount = 0; - DEBUG_ASSERTCRASH(m_openList== nullptr && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); Int attackDistance = weapon->getAttackDistance(obj, victim, victimPos); attackDistance += 3*PATHFIND_CELL_SIZE; @@ -10342,7 +10412,16 @@ Path *Pathfinder::findAttackPath( const Object *obj, const LocomotorSet& locomot } // initialize "open" list to contain start cell - m_openList = parentCell; +#if RETAIL_COMPATIBLE_PATHFINDING + if (!s_useFixedPathfinding) { + m_openList.reset(parentCell); + } + else +#endif + { + m_openList.reset(); + parentCell->putOnSortedOpenList(m_openList); + } // "closed" list is initially empty m_closedList = nullptr; @@ -10362,11 +10441,11 @@ Path *Pathfinder::findAttackPath( const Object *obj, const LocomotorSet& locomot checkLOS = true; } - while( m_openList != nullptr ) + while( !m_openList.empty() ) { // take head cell off of open list - it has lowest estimated total path cost - parentCell = m_openList; - m_openList = parentCell->removeFromOpenList(m_openList); + parentCell = m_openList.getHead(); + parentCell->removeFromOpenList(m_openList); Coord3D cellCenter; adjustCoordToCell(parentCell->getXIndex(), parentCell->getYIndex(), centerInCell, cellCenter, parentCell->getLayer()); @@ -10535,7 +10614,7 @@ Path *Pathfinder::findSafePath( const Object *obj, const LocomotorSet& locomotor isHuman = false; // computer gets to cheat. } - DEBUG_ASSERTCRASH(m_openList== nullptr && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); // create unique "mark" values for open and closed cells for this pathfind invocation m_zoneManager.setAllPassable(); @@ -10554,7 +10633,16 @@ Path *Pathfinder::findSafePath( const Object *obj, const LocomotorSet& locomotor parentCell->startPathfind( nullptr); // initialize "open" list to contain start cell - m_openList = parentCell; +#if RETAIL_COMPATIBLE_PATHFINDING + if (!s_useFixedPathfinding) { + m_openList.reset(parentCell); + } + else +#endif + { + m_openList.reset(); + parentCell->putOnSortedOpenList(m_openList); + } // "closed" list is initially empty m_closedList = nullptr; @@ -10566,11 +10654,11 @@ Path *Pathfinder::findSafePath( const Object *obj, const LocomotorSet& locomotor Real farthestDistanceSqr = 0; - while( m_openList != nullptr ) + while( !m_openList.empty() ) { // take head cell off of open list - it has lowest estimated total path cost - parentCell = m_openList; - m_openList = parentCell->removeFromOpenList(m_openList); + parentCell = m_openList.getHead(); + parentCell->removeFromOpenList(m_openList); Coord3D cellCenter; adjustCoordToCell(parentCell->getXIndex(), parentCell->getYIndex(), centerInCell, cellCenter, parentCell->getLayer()); @@ -10589,7 +10677,7 @@ Path *Pathfinder::findSafePath( const Object *obj, const LocomotorSet& locomotor if (distSqr>repulsorDistSqr) { ok = true; } - if (m_openList == nullptr && cellCount>0) { + if (m_openList.empty() && cellCount>0) { ok = true; // exhausted the search space, just take the last cell. } if (distSqr > farthestDistanceSqr) { diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/AIPathfind.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/AIPathfind.h index a7ab136c909..98e0b57ca5e 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/AIPathfind.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/AIPathfind.h @@ -248,6 +248,24 @@ class PathfindCellInfo UnsignedInt m_closed:1; ///< place for marking this cell as on the closed list }; +// TheSuperHackers @info The PathfindCellList class acts as a new management class for the pathfindcell open and closed lists +class PathfindCellList +{ + friend class PathfindCell; + +public: + PathfindCellList() : m_head(nullptr) {} + + void reset(PathfindCell* newHead = nullptr) { m_head = newHead; } + + PathfindCell* getHead() const { return m_head; } + + Bool empty() const { return m_head == nullptr; } + +private: + PathfindCell* m_head; +}; + /** * This represents one cell in the pathfinding grid. * These cells categorize the world into idealized cellular states, @@ -308,11 +326,11 @@ class PathfindCell UnsignedInt costSoFar( PathfindCell *parent ); - /// put self on "open" list in ascending cost order, return new list - PathfindCell *putOnSortedOpenList( PathfindCell *list ); + /// put self on "open" list in ascending cost order + void putOnSortedOpenList( PathfindCellList &list ); /// remove self from "open" list - PathfindCell *removeFromOpenList( PathfindCell *list ); + void removeFromOpenList( PathfindCellList &list ); /// put self on "closed" list, return new list PathfindCell *putOnClosedList( PathfindCell *list ); @@ -324,7 +342,7 @@ class PathfindCell static Int releaseClosedList( PathfindCell *list ); /// remove all cells from closed list. - static Int releaseOpenList( PathfindCell *list ); + static Int releaseOpenList( PathfindCellList &list ); inline PathfindCell *getNextOpen(void) {return m_info->m_nextOpen?m_info->m_nextOpen->m_cell: nullptr;} @@ -856,7 +874,7 @@ class Pathfinder : PathfindServicesInterface, public Snapshot IRegion2D m_extent; ///< Grid extent limits IRegion2D m_logicalExtent; ///< Logical grid extent limits - PathfindCell *m_openList; ///< Cells ready to be explored + PathfindCellList m_openList; ///< Cells ready to be explored PathfindCell *m_closedList; ///< Cells already explored Bool m_isMapReady; ///< True if all cells of map have been classified diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp index f6f4c98fc5d..599eba344bd 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp @@ -1132,7 +1132,7 @@ void Pathfinder::forceCleanCells() TheAudio->addAudioEvent(&TheAudio->getMiscAudio()->m_allCheerSound); PathfindCellInfo::forceCleanPathFindCellInfos(); - m_openList = nullptr; + m_openList.reset(); m_closedList = nullptr; for (int j = 0; j <= m_extent.hi.y; ++j) { @@ -1682,13 +1682,13 @@ Bool PathfindCell::removeObstacle( Object *obstacle ) } /// put self on "open" list in ascending cost order, return new list -PathfindCell *PathfindCell::putOnSortedOpenList( PathfindCell *list ) +void PathfindCell::putOnSortedOpenList( PathfindCellList &list ) { DEBUG_ASSERTCRASH(m_info, ("Has to have info.")); DEBUG_ASSERTCRASH(m_info->m_closed==FALSE && m_info->m_open==FALSE, ("Serious error - Invalid flags. jba")); - if (list == nullptr) + if (list.m_head == nullptr) { - list = this; + list.m_head = this; m_info->m_prevOpen = nullptr; m_info->m_nextOpen = nullptr; } @@ -1701,11 +1701,11 @@ PathfindCell *PathfindCell::putOnSortedOpenList( PathfindCell *list ) // External code should pickup on the bad behaviour and cleanup properly, but we need to explicitly break out here // The fixed pathfinding does not have this issue due to the proper cleanup of pathfindCells and their pathfindCellInfos UnsignedInt cellCount = 0; - for (c = list; c && cellCount < PATHFIND_CELLS_PER_FRAME; c = c->getNextOpen()) + for (c = list.m_head; c && cellCount < PATHFIND_CELLS_PER_FRAME; c = c->getNextOpen()) { cellCount++; #else - for (c = list; c; c = c->getNextOpen()) + for (c = list.m_head; c; c = c->getNextOpen()) { #endif if (c->m_info->m_totalCost > m_info->m_totalCost) @@ -1720,7 +1720,7 @@ PathfindCell *PathfindCell::putOnSortedOpenList( PathfindCell *list ) if (c->m_info->m_prevOpen) c->m_info->m_prevOpen->m_nextOpen = this->m_info; else - list = this; + list.m_head = this; m_info->m_prevOpen = c->m_info->m_prevOpen; c->m_info->m_prevOpen = this->m_info; @@ -1741,11 +1741,10 @@ PathfindCell *PathfindCell::putOnSortedOpenList( PathfindCell *list ) m_info->m_open = true; m_info->m_closed = false; - return list; } /// remove self from "open" list -PathfindCell *PathfindCell::removeFromOpenList( PathfindCell *list ) +void PathfindCell::removeFromOpenList( PathfindCellList &list ) { DEBUG_ASSERTCRASH(m_info, ("Has to have info.")); DEBUG_ASSERTCRASH(m_info->m_closed==FALSE && m_info->m_open==TRUE, ("Serious error - Invalid flags. jba")); @@ -1755,25 +1754,24 @@ PathfindCell *PathfindCell::removeFromOpenList( PathfindCell *list ) if (m_info->m_prevOpen) m_info->m_prevOpen->m_nextOpen = m_info->m_nextOpen; else - list = getNextOpen(); + list.m_head = getNextOpen(); m_info->m_open = false; m_info->m_nextOpen = nullptr; m_info->m_prevOpen = nullptr; - return list; } /// remove all cells from "open" list -Int PathfindCell::releaseOpenList( PathfindCell *list ) +Int PathfindCell::releaseOpenList( PathfindCellList &list ) { Int count = 0; - while (list) { + while (list.m_head) { count++; - DEBUG_ASSERTCRASH(list->m_info, ("Has to have info.")); - DEBUG_ASSERTCRASH(list->m_info->m_closed==FALSE && list->m_info->m_open==TRUE, ("Serious error - Invalid flags. jba")); - PathfindCell *cur = list; - PathfindCellInfo *curInfo = list->m_info; + DEBUG_ASSERTCRASH(list.m_head->m_info, ("Has to have info.")); + DEBUG_ASSERTCRASH(list.m_head->m_info->m_closed==FALSE && list.m_head->m_info->m_open==TRUE, ("Serious error - Invalid flags. jba")); + PathfindCell *cur = list.m_head; + PathfindCellInfo *curInfo = list.m_head->m_info; #if RETAIL_COMPATIBLE_PATHFINDING // TheSuperHackers @info This is only here to catch a crash point in the retail compatible pathfinding @@ -1787,9 +1785,9 @@ Int PathfindCell::releaseOpenList( PathfindCell *list ) #endif if (curInfo->m_nextOpen) { - list = curInfo->m_nextOpen->m_cell; + list.m_head = curInfo->m_nextOpen->m_cell; } else { - list = nullptr; + list.m_head = nullptr; } DEBUG_ASSERTCRASH(cur == curInfo->m_cell, ("Bad backpointer in PathfindCellInfo")); curInfo->m_nextOpen = nullptr; @@ -3882,7 +3880,7 @@ void Pathfinder::reset( void ) // reset the pathfind grid m_extent.lo.x=m_extent.lo.y=m_extent.hi.x=m_extent.hi.y=0; m_logicalExtent.lo.x=m_logicalExtent.lo.y=m_logicalExtent.hi.x=m_logicalExtent.hi.y=0; - m_openList = nullptr; + m_openList.reset(); m_closedList = nullptr; m_ignoreObstacleID = INVALID_ID; @@ -4637,7 +4635,7 @@ void Pathfinder::debugShowSearch( Bool pathFound ) addIcon(nullptr, 0, 0, color); // erase. } - for( s = m_openList; s; s=s->getNextOpen() ) + for( s = m_openList.getHead(); s; s=s->getNextOpen() ) { // create objects to show path - they decay RGBColor color; @@ -4741,9 +4739,9 @@ Bool Pathfinder::validMovementTerrain( PathfindLayerEnum layer, const Locomotor* // void Pathfinder::cleanOpenAndClosedLists(void) { Int count = 0; - if (m_openList) { + if (!m_openList.empty()) { count += PathfindCell::releaseOpenList(m_openList); - m_openList = nullptr; + m_openList.reset(); } #if RETAIL_COMPATIBLE_PATHFINDING @@ -5881,7 +5879,7 @@ void Pathfinder::checkChangeLayers(PathfindCell *parentCell) newCell->setCostSoFar(parentCell->getCostSoFar()); // same as parent cost newCell->setTotalCost(parentCell->getTotalCost()); // insert newCell in open list such that open list is sorted, smallest total path cost first - m_openList = newCell->putOnSortedOpenList( m_openList ); + newCell->putOnSortedOpenList( m_openList ); } bool Pathfinder::checkCellOutsideExtents(ICoord2D& cell) { @@ -5984,10 +5982,10 @@ struct ExamineCellsStruct // if the to was already on the open list, remove it so it can be re-inserted in order if (to->getOpen()) - d->thePathfinder->m_openList = to->removeFromOpenList( d->thePathfinder->m_openList ); + to->removeFromOpenList( d->thePathfinder->m_openList ); // insert to in open list such that open list is sorted, smallest total path cost first - d->thePathfinder->m_openList = to->putOnSortedOpenList( d->thePathfinder->m_openList ); + to->putOnSortedOpenList( d->thePathfinder->m_openList ); } return 0; // keep going @@ -6218,10 +6216,10 @@ Int Pathfinder::examineNeighboringCells(PathfindCell *parentCell, PathfindCell * // if the newCell was already on the open list, remove it so it can be re-inserted in order if (newCell->getOpen()) - m_openList = newCell->removeFromOpenList( m_openList ); + newCell->removeFromOpenList( m_openList ); // insert newCell in open list such that open list is sorted, smallest total path cost first - m_openList = newCell->putOnSortedOpenList( m_openList ); + newCell->putOnSortedOpenList( m_openList ); } return cellCount; } @@ -6287,7 +6285,7 @@ Path *Pathfinder::internalFindPath( Object *obj, const LocomotorSet& locomotorSe DEBUG_LOG(("Attempting pathfind to 0,0, generally a bug.")); return nullptr; } - DEBUG_ASSERTCRASH(m_openList== nullptr && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); if (m_isMapReady == false) { return nullptr; } @@ -6399,7 +6397,16 @@ Path *Pathfinder::internalFindPath( Object *obj, const LocomotorSet& locomotorSe parentCell->startPathfind(goalCell); // initialize "open" list to contain start cell - m_openList = parentCell; +#if RETAIL_COMPATIBLE_PATHFINDING + if (!s_useFixedPathfinding) { + m_openList.reset(parentCell); + } + else +#endif + { + m_openList.reset(); + parentCell->putOnSortedOpenList(m_openList); + } // "closed" list is initially empty m_closedList = nullptr; @@ -6410,11 +6417,11 @@ Path *Pathfinder::internalFindPath( Object *obj, const LocomotorSet& locomotorSe // Continue search until "open" list is empty, or // until goal is found. // - while( m_openList != nullptr ) + while( !m_openList.empty() ) { // take head cell off of open list - it has lowest estimated total path cost - parentCell = m_openList; - m_openList = parentCell->removeFromOpenList(m_openList); + parentCell = m_openList.getHead(); + parentCell->removeFromOpenList(m_openList); if (parentCell == goalCell) { @@ -6819,7 +6826,7 @@ struct GroundCellsStruct to->setTotalCost(to->getCostSoFar() + costRemaining) ; // insert to in open list such that open list is sorted, smallest total path cost first - d->thePathfinder->m_openList = to->putOnSortedOpenList( d->thePathfinder->m_openList ); + to->putOnSortedOpenList( d->thePathfinder->m_openList ); } return 0; // keep going @@ -6855,7 +6862,7 @@ Path *Pathfinder::findGroundPath( const Coord3D *from, DEBUG_LOG(("Attempting pathfind to 0,0, generally a bug.")); return nullptr; } - DEBUG_ASSERTCRASH(m_openList== nullptr && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); if (m_isMapReady == false) { return nullptr; } @@ -6948,7 +6955,16 @@ Path *Pathfinder::findGroundPath( const Coord3D *from, parentCell->startPathfind(goalCell); // initialize "open" list to contain start cell - m_openList = parentCell; +#if RETAIL_COMPATIBLE_PATHFINDING + if (!s_useFixedPathfinding) { + m_openList.reset(parentCell); + } + else +#endif + { + m_openList.reset(); + parentCell->putOnSortedOpenList(m_openList); + } // "closed" list is initially empty m_closedList = nullptr; @@ -6963,11 +6979,11 @@ Path *Pathfinder::findGroundPath( const Coord3D *from, // until goal is found. // Int cellCount = 0; - while( m_openList != nullptr ) + while( !m_openList.empty() ) { // take head cell off of open list - it has lowest estimated total path cost - parentCell = m_openList; - m_openList = parentCell->removeFromOpenList(m_openList); + parentCell = m_openList.getHead(); + parentCell->removeFromOpenList(m_openList); if (parentCell == goalCell) { @@ -7139,10 +7155,10 @@ Path *Pathfinder::findGroundPath( const Coord3D *from, // if the newCell was already on the open list, remove it so it can be re-inserted in order if (newCell->getOpen()) - m_openList = newCell->removeFromOpenList( m_openList ); + newCell->removeFromOpenList( m_openList ); // insert newCell in open list such that open list is sorted, smallest total path cost first - m_openList = newCell->putOnSortedOpenList( m_openList ); + newCell->putOnSortedOpenList( m_openList ); } } // failure - goal cannot be reached @@ -7299,7 +7315,7 @@ void Pathfinder::processHierarchicalCell( const ICoord2D &scanCell, const ICoord adjNewCell->setTotalCost(adjNewCell->getCostSoFar()+remCost); adjNewCell->setParentCellHierarchical(parentCell); // insert newCell in open list such that open list is sorted, smallest total path cost first - m_openList = adjNewCell->putOnSortedOpenList( m_openList ); + adjNewCell->putOnSortedOpenList( m_openList ); } } @@ -7345,7 +7361,7 @@ Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSu DEBUG_LOG(("Attempting pathfind to 0,0, generally a bug.")); return nullptr; } - DEBUG_ASSERTCRASH(m_openList== nullptr && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); if (m_isMapReady == false) { return nullptr; } @@ -7421,7 +7437,16 @@ Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSu } // initialize "open" list to contain start cell - m_openList = parentCell; +#if RETAIL_COMPATIBLE_PATHFINDING + if (!s_useFixedPathfinding) { + m_openList.reset(parentCell); + } + else +#endif + { + m_openList.reset(); + parentCell->putOnSortedOpenList(m_openList); + } if (parentCell->getLayer()!=LAYER_GROUND) { PathfindLayerEnum layer = parentCell->getLayer(); @@ -7434,7 +7459,7 @@ Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSu PathfindCell *startCell = getCell(LAYER_GROUND, ndx.x, ndx.y); if (cell && startCell) { // Close parent cell; - m_openList = parentCell->removeFromOpenList(m_openList); + parentCell->removeFromOpenList(m_openList); m_closedList = parentCell->putOnClosedList(m_closedList); if (!startCell->allocateInfo(ndx)) { // TheSuperHackers @info We need to forcefully cleanup dangling pathfinding cells if this failure condition is hit in retail @@ -7460,7 +7485,7 @@ Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSu startCell->setTotalCost(remCost); startCell->setParentCellHierarchical(parentCell); // insert newCell in open list such that open list is sorted, smallest total path cost first - m_openList = startCell->putOnSortedOpenList( m_openList ); + startCell->putOnSortedOpenList( m_openList ); cellCount++; if(!cell->allocateInfo(toNdx)) { @@ -7485,7 +7510,7 @@ Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSu cell->setTotalCost(remCost); cell->setParentCellHierarchical(parentCell); // insert newCell in open list such that open list is sorted, smallest total path cost first - m_openList = cell->putOnSortedOpenList( m_openList ); + cell->putOnSortedOpenList( m_openList ); } } @@ -7496,11 +7521,11 @@ Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSu // Continue search until "open" list is empty, or // until goal is found. // - while( m_openList != nullptr ) + while( !m_openList.empty() ) { // take head cell off of open list - it has lowest estimated total path cost - parentCell = m_openList; - m_openList = parentCell->removeFromOpenList(m_openList); + parentCell = m_openList.getHead(); + parentCell->removeFromOpenList(m_openList); zoneStorageType parentZone; if (parentCell->getLayer()==LAYER_GROUND) { @@ -7613,7 +7638,7 @@ Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSu cell->setTotalCost(cell->getCostSoFar()+remCost); cell->setParentCellHierarchical(startCell); // insert newCell in open list such that open list is sorted, smallest total path cost first - m_openList = cell->putOnSortedOpenList( m_openList ); + cell->putOnSortedOpenList( m_openList ); } } @@ -8115,7 +8140,7 @@ Bool Pathfinder::pathDestination( Object *obj, const LocomotorSet& locomotorSet Coord3D adjustTo = *groupDest; Coord3D *to = &adjustTo; - DEBUG_ASSERTCRASH(m_openList== nullptr && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); // create unique "mark" values for open and closed cells for this pathfind invocation Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false; @@ -8178,7 +8203,16 @@ Bool Pathfinder::pathDestination( Object *obj, const LocomotorSet& locomotorSet parentCell->startPathfind(goalCell); // initialize "open" list to contain start cell - m_openList = parentCell; +#if RETAIL_COMPATIBLE_PATHFINDING + if (!s_useFixedPathfinding) { + m_openList.reset(parentCell); + } + else +#endif + { + m_openList.reset(); + parentCell->putOnSortedOpenList(m_openList); + } // "closed" list is initially empty m_closedList = nullptr; @@ -8187,11 +8221,11 @@ Bool Pathfinder::pathDestination( Object *obj, const LocomotorSet& locomotorSet // Continue search until "open" list is empty, or // until goal is found. // - while( m_openList != nullptr ) + while( !m_openList.empty() ) { // take head cell off of open list - it has lowest estimated total path cost - parentCell = m_openList; - m_openList = parentCell->removeFromOpenList(m_openList); + parentCell = m_openList.getHead(); + parentCell->removeFromOpenList(m_openList); Coord3D pos; // put parent cell onto closed list - its evaluation is finished @@ -8316,10 +8350,10 @@ Bool Pathfinder::pathDestination( Object *obj, const LocomotorSet& locomotorSet // if the newCell was already on the open list, remove it so it can be re-inserted in order if (newCell->getOpen()) - m_openList = newCell->removeFromOpenList( m_openList ); + newCell->removeFromOpenList( m_openList ); // insert newCell in open list such that open list is sorted, smallest total path cost first - m_openList = newCell->putOnSortedOpenList( m_openList ); + newCell->putOnSortedOpenList( m_openList ); } } @@ -8409,7 +8443,7 @@ Int Pathfinder::checkPathCost(Object *obj, const LocomotorSet& locomotorSet, con Coord3D adjustTo = *rawTo; Coord3D *to = &adjustTo; - DEBUG_ASSERTCRASH(m_openList== nullptr && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); // create unique "mark" values for open and closed cells for this pathfind invocation Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false; @@ -8454,7 +8488,16 @@ Int Pathfinder::checkPathCost(Object *obj, const LocomotorSet& locomotorSet, con parentCell->startPathfind(goalCell); // initialize "open" list to contain start cell - m_openList = parentCell; +#if RETAIL_COMPATIBLE_PATHFINDING + if (!s_useFixedPathfinding) { + m_openList.reset(parentCell); + } + else +#endif + { + m_openList.reset(); + parentCell->putOnSortedOpenList(m_openList); + } // "closed" list is initially empty m_closedList = nullptr; @@ -8463,11 +8506,11 @@ Int Pathfinder::checkPathCost(Object *obj, const LocomotorSet& locomotorSet, con // Continue search until "open" list is empty, or // until goal is found. // - while( m_openList != nullptr ) + while( !m_openList.empty() ) { // take head cell off of open list - it has lowest estimated total path cost - parentCell = m_openList; - m_openList = parentCell->removeFromOpenList(m_openList); + parentCell = m_openList.getHead(); + parentCell->removeFromOpenList(m_openList); // put parent cell onto closed list - its evaluation is finished - Retail compatible behaviour #if RETAIL_COMPATIBLE_PATHFINDING @@ -8601,12 +8644,12 @@ Int Pathfinder::checkPathCost(Object *obj, const LocomotorSet& locomotorSet, con // if the newCell was already on the open list, remove it so it can be re-inserted in order if (newCell->getOpen()) - m_openList = newCell->removeFromOpenList( m_openList ); + newCell->removeFromOpenList( m_openList ); #if RETAIL_COMPATIBLE_PATHFINDING // TheSuperHacker @info This is here to catch a retail pathfinding crash point and to recover from it // A cell has gotten onto the open list without pathfinding info due to a danling m_open pointer on the previous listed cell so we need to force a cleanup - if (!s_useFixedPathfinding && m_openList && !m_openList->hasInfo()) { + if (!s_useFixedPathfinding && m_openList.getHead() && !m_openList.getHead()->hasInfo()) { s_useFixedPathfinding = true; forceCleanCells(); return MAX_COST; @@ -8614,7 +8657,7 @@ Int Pathfinder::checkPathCost(Object *obj, const LocomotorSet& locomotorSet, con #endif // insert newCell in open list such that open list is sorted, smallest total path cost first - m_openList = newCell->putOnSortedOpenList( m_openList ); + newCell->putOnSortedOpenList( m_openList ); } } @@ -8678,7 +8721,7 @@ Path *Pathfinder::findClosestPath( Object *obj, const LocomotorSet& locomotorSet adjustTo.x += PATHFIND_CELL_SIZE_F/2; adjustTo.y += PATHFIND_CELL_SIZE_F/2; } - DEBUG_ASSERTCRASH(m_openList== nullptr && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); // create unique "mark" values for open and closed cells for this pathfind invocation Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false; @@ -8783,7 +8826,16 @@ Path *Pathfinder::findClosestPath( Object *obj, const LocomotorSet& locomotorSet Real closestDistScreenSqr = FLT_MAX; // initialize "open" list to contain start cell - m_openList = parentCell; +#if RETAIL_COMPATIBLE_PATHFINDING + if (!s_useFixedPathfinding) { + m_openList.reset(parentCell); + } + else +#endif + { + m_openList.reset(); + parentCell->putOnSortedOpenList(m_openList); + } // "closed" list is initially empty m_closedList = nullptr; @@ -8793,14 +8845,14 @@ Path *Pathfinder::findClosestPath( Object *obj, const LocomotorSet& locomotorSet // until goal is found. // Bool foundGoal = false; - while( m_openList != nullptr ) + while( !m_openList.empty() ) { Real dx; Real dy; Real distSqr; // take head cell off of open list - it has lowest estimated total path cost - parentCell = m_openList; - m_openList = parentCell->removeFromOpenList(m_openList); + parentCell = m_openList.getHead(); + parentCell->removeFromOpenList(m_openList); if (parentCell == goalCell) { @@ -10274,7 +10326,7 @@ Path *Pathfinder::getMoveAwayFromPath(Object* obj, Object *otherObj, Int radius; getRadiusAndCenter(obj, radius, centerInCell); - DEBUG_ASSERTCRASH(m_openList== nullptr && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); // determine start cell ICoord2D startCellNdx; @@ -10315,7 +10367,16 @@ Path *Pathfinder::getMoveAwayFromPath(Object* obj, Object *otherObj, parentCell->startPathfind(nullptr); // initialize "open" list to contain start cell - m_openList = parentCell; +#if RETAIL_COMPATIBLE_PATHFINDING + if (!s_useFixedPathfinding) { + m_openList.reset(parentCell); + } + else +#endif + { + m_openList.reset(); + parentCell->putOnSortedOpenList(m_openList); + } // "closed" list is initially empty m_closedList = nullptr; @@ -10330,11 +10391,11 @@ Path *Pathfinder::getMoveAwayFromPath(Object* obj, Object *otherObj, boxHalfWidth += otherRadius*PATHFIND_CELL_SIZE_F; if (otherCenter) boxHalfWidth+=PATHFIND_CELL_SIZE_F/2; - while( m_openList != nullptr ) + while( !m_openList.empty() ) { // take head cell off of open list - it has lowest estimated total path cost - parentCell = m_openList; - m_openList = parentCell->removeFromOpenList(m_openList); + parentCell = m_openList.getHead(); + parentCell->removeFromOpenList(m_openList); Region2D bounds; Coord3D cellCenter; @@ -10451,7 +10512,7 @@ Path *Pathfinder::patchPath( const Object *obj, const LocomotorSet& locomotorSet m_zoneManager.setAllPassable(); - DEBUG_ASSERTCRASH(m_openList== nullptr && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); enum {CELL_LIMIT = 2000}; // max cells to examine. Int cellCount = 0; @@ -10482,7 +10543,16 @@ Path *Pathfinder::patchPath( const Object *obj, const LocomotorSet& locomotorSet parentCell->startPathfind( nullptr); // initialize "open" list to contain start cell - m_openList = parentCell; +#if RETAIL_COMPATIBLE_PATHFINDING + if (!s_useFixedPathfinding) { + m_openList.reset(parentCell); + } + else +#endif + { + m_openList.reset(); + parentCell->putOnSortedOpenList(m_openList); + } // "closed" list is initially empty m_closedList = nullptr; @@ -10566,11 +10636,11 @@ Path *Pathfinder::patchPath( const Object *obj, const LocomotorSet& locomotorSet return nullptr; } - while( m_openList != nullptr ) + while( !m_openList.empty() ) { // take head cell off of open list - it has lowest estimated total path cost - parentCell = m_openList; - m_openList = parentCell->removeFromOpenList(m_openList); + parentCell = m_openList.getHead(); + parentCell->removeFromOpenList(m_openList); Coord3D cellCenter; adjustCoordToCell(parentCell->getXIndex(), parentCell->getYIndex(), centerInCell, cellCenter, parentCell->getLayer()); @@ -10725,7 +10795,7 @@ Path *Pathfinder::findAttackPath( const Object *obj, const LocomotorSet& locomot Int cellCount = 0; - DEBUG_ASSERTCRASH(m_openList== nullptr && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); Int attackDistance = weapon->getAttackDistance(obj, victim, victimPos); attackDistance += 3*PATHFIND_CELL_SIZE; @@ -10767,7 +10837,16 @@ Path *Pathfinder::findAttackPath( const Object *obj, const LocomotorSet& locomot } // initialize "open" list to contain start cell - m_openList = parentCell; +#if RETAIL_COMPATIBLE_PATHFINDING + if (!s_useFixedPathfinding) { + m_openList.reset(parentCell); + } + else +#endif + { + m_openList.reset(); + parentCell->putOnSortedOpenList(m_openList); + } // "closed" list is initially empty m_closedList = nullptr; @@ -10787,11 +10866,11 @@ Path *Pathfinder::findAttackPath( const Object *obj, const LocomotorSet& locomot checkLOS = true; } - while( m_openList != nullptr ) + while( !m_openList.empty() ) { // take head cell off of open list - it has lowest estimated total path cost - parentCell = m_openList; - m_openList = parentCell->removeFromOpenList(m_openList); + parentCell = m_openList.getHead(); + parentCell->removeFromOpenList(m_openList); Coord3D cellCenter; adjustCoordToCell(parentCell->getXIndex(), parentCell->getYIndex(), centerInCell, cellCenter, parentCell->getLayer()); @@ -11017,7 +11096,7 @@ Path *Pathfinder::findSafePath( const Object *obj, const LocomotorSet& locomotor isHuman = false; // computer gets to cheat. } - DEBUG_ASSERTCRASH(m_openList== nullptr && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); // create unique "mark" values for open and closed cells for this pathfind invocation m_zoneManager.setAllPassable(); @@ -11036,7 +11115,16 @@ Path *Pathfinder::findSafePath( const Object *obj, const LocomotorSet& locomotor parentCell->startPathfind( nullptr); // initialize "open" list to contain start cell - m_openList = parentCell; +#if RETAIL_COMPATIBLE_PATHFINDING + if (!s_useFixedPathfinding) { + m_openList.reset(parentCell); + } + else +#endif + { + m_openList.reset(); + parentCell->putOnSortedOpenList(m_openList); + } // "closed" list is initially empty m_closedList = nullptr; @@ -11048,11 +11136,11 @@ Path *Pathfinder::findSafePath( const Object *obj, const LocomotorSet& locomotor Real farthestDistanceSqr = 0; - while( m_openList != nullptr ) + while( !m_openList.empty() ) { // take head cell off of open list - it has lowest estimated total path cost - parentCell = m_openList; - m_openList = parentCell->removeFromOpenList(m_openList); + parentCell = m_openList.getHead(); + parentCell->removeFromOpenList(m_openList); Coord3D cellCenter; adjustCoordToCell(parentCell->getXIndex(), parentCell->getYIndex(), centerInCell, cellCenter, parentCell->getLayer()); @@ -11071,7 +11159,7 @@ Path *Pathfinder::findSafePath( const Object *obj, const LocomotorSet& locomotor if (distSqr>repulsorDistSqr) { ok = true; } - if (m_openList == nullptr && cellCount>0) { + if (m_openList.empty() && cellCount>0) { ok = true; // exhausted the search space, just take the last cell. } if (distSqr > farthestDistanceSqr) { From 357a0bae529186362490f1e256152a73753cea19 Mon Sep 17 00:00:00 2001 From: Mauller <26652186+Mauller@users.noreply.github.com> Date: Sat, 21 Feb 2026 15:14:25 +0000 Subject: [PATCH 2/2] refactor(pathfinder): Implement PathfindCellList class for the pathfindcell closedList (#2327) --- .../GameEngine/Include/GameLogic/AIPathfind.h | 8 +- .../Source/GameLogic/AI/AIPathfind.cpp | 128 +++++++++-------- .../GameEngine/Include/GameLogic/AIPathfind.h | 8 +- .../Source/GameLogic/AI/AIPathfind.cpp | 130 +++++++++--------- 4 files changed, 135 insertions(+), 139 deletions(-) diff --git a/Generals/Code/GameEngine/Include/GameLogic/AIPathfind.h b/Generals/Code/GameEngine/Include/GameLogic/AIPathfind.h index 7a8805c2df6..e2c6cd32856 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/AIPathfind.h +++ b/Generals/Code/GameEngine/Include/GameLogic/AIPathfind.h @@ -332,13 +332,13 @@ class PathfindCell void removeFromOpenList( PathfindCellList &list ); /// put self on "closed" list, return new list - PathfindCell *putOnClosedList( PathfindCell *list ); + void putOnClosedList( PathfindCellList &list ); /// remove self from "closed" list - PathfindCell *removeFromClosedList( PathfindCell *list ); + void removeFromClosedList( PathfindCellList &list ); /// remove all cells from closed list. - static Int releaseClosedList( PathfindCell *list ); + static Int releaseClosedList( PathfindCellList &list ); /// remove all cells from closed list. static Int releaseOpenList( PathfindCellList &list ); @@ -867,7 +867,7 @@ class Pathfinder : PathfindServicesInterface, public Snapshot IRegion2D m_logicalExtent; ///< Logical grid extent limits PathfindCellList m_openList; ///< Cells ready to be explored - PathfindCell *m_closedList; ///< Cells already explored + PathfindCellList m_closedList; ///< Cells already explored Bool m_isMapReady; ///< True if all cells of map have been classified Bool m_isTunneling; ///< True if path started in an obstacle diff --git a/Generals/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp b/Generals/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp index 98b32987867..f74387efb0e 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp @@ -1118,7 +1118,7 @@ void Pathfinder::forceCleanCells() PathfindCellInfo::forceCleanPathFindCellInfos(); m_openList.reset(); - m_closedList = nullptr; + m_closedList.reset(); for (int j = 0; j <= m_extent.hi.y; ++j) { for (int i = 0; i <= m_extent.hi.x; ++i) { @@ -1782,15 +1782,15 @@ Int PathfindCell::releaseOpenList( PathfindCellList &list ) } /// remove all cells from "closed" list -Int PathfindCell::releaseClosedList( PathfindCell *list ) +Int PathfindCell::releaseClosedList( PathfindCellList &list ) { Int count = 0; - while (list) { + while (list.m_head) { count++; - DEBUG_ASSERTCRASH(list->m_info, ("Has to have info.")); - DEBUG_ASSERTCRASH(list->m_info->m_closed==TRUE && list->m_info->m_open==FALSE, ("Serious error - Invalid flags. jba")); - PathfindCell *cur = list; - PathfindCellInfo *curInfo = list->m_info; + DEBUG_ASSERTCRASH(list.m_head->m_info, ("Has to have info.")); + DEBUG_ASSERTCRASH(list.m_head->m_info->m_closed==TRUE && list.m_head->m_info->m_open==FALSE, ("Serious error - Invalid flags. jba")); + PathfindCell *cur = list.m_head; + PathfindCellInfo *curInfo = list.m_head->m_info; #if RETAIL_COMPATIBLE_PATHFINDING // TheSuperHackers @info This is only here to catch a crash point in the retail compatible pathfinding // One crash mode is where a cell has no PathfindCellInfo, resulting in a nullptr access and a crash. @@ -1803,9 +1803,9 @@ Int PathfindCell::releaseClosedList( PathfindCell *list ) #endif if (curInfo->m_nextOpen) { - list = curInfo->m_nextOpen->m_cell; + list.m_head = curInfo->m_nextOpen->m_cell; } else { - list = nullptr; + list.m_head = nullptr; } DEBUG_ASSERTCRASH(cur == curInfo->m_cell, ("Bad backpointer in PathfindCellInfo")); curInfo->m_nextOpen = nullptr; @@ -1817,7 +1817,7 @@ Int PathfindCell::releaseClosedList( PathfindCell *list ) } /// put self on "closed" list, return new list -PathfindCell *PathfindCell::putOnClosedList( PathfindCell *list ) +void PathfindCell::putOnClosedList( PathfindCellList &list ) { DEBUG_ASSERTCRASH(m_info, ("Has to have info.")); DEBUG_ASSERTCRASH(m_info->m_closed==FALSE && m_info->m_open==FALSE, ("Serious error - Invalid flags. jba")); @@ -1828,18 +1828,17 @@ PathfindCell *PathfindCell::putOnClosedList( PathfindCell *list ) m_info->m_closed = TRUE; m_info->m_prevOpen = nullptr; - m_info->m_nextOpen = list?list->m_info:nullptr; - if (list) - list->m_info->m_prevOpen = this->m_info; + m_info->m_nextOpen = list.m_head ? list.m_head->m_info : nullptr; + if (list.m_head) + list.m_head->m_info->m_prevOpen = this->m_info; - list = this; + list.m_head = this; } - return list; } /// remove self from "closed" list -PathfindCell *PathfindCell::removeFromClosedList( PathfindCell *list ) +void PathfindCell::removeFromClosedList( PathfindCellList &list ) { DEBUG_ASSERTCRASH(m_info, ("Has to have info.")); DEBUG_ASSERTCRASH(m_info->m_closed==TRUE && m_info->m_open==FALSE, ("Serious error - Invalid flags. jba")); @@ -1849,13 +1848,12 @@ PathfindCell *PathfindCell::removeFromClosedList( PathfindCell *list ) if (m_info->m_prevOpen) m_info->m_prevOpen->m_nextOpen = m_info->m_nextOpen; else - list = getNextOpen(); + list.m_head = getNextOpen(); m_info->m_closed = false; m_info->m_nextOpen = nullptr; m_info->m_prevOpen = nullptr; - return list; } /** @@ -3653,7 +3651,7 @@ void Pathfinder::reset( void ) m_extent.lo.x=m_extent.lo.y=m_extent.hi.x=m_extent.hi.y=0; m_logicalExtent.lo.x=m_logicalExtent.lo.y=m_logicalExtent.hi.x=m_logicalExtent.hi.y=0; m_openList.reset(); - m_closedList = nullptr; + m_closedList.reset(); m_ignoreObstacleID = INVALID_ID; m_isTunneling = false; @@ -4363,7 +4361,7 @@ void Pathfinder::debugShowSearch( Bool pathFound ) addIcon(&pos, PATHFIND_CELL_SIZE_F*.6f, 200, color); } - for( s = m_closedList; s; s=s->getNextOpen() ) + for( s = m_closedList.getHead(); s; s=s->getNextOpen() ) { // create objects to show path - they decay RGBColor color; @@ -4467,9 +4465,9 @@ void Pathfinder::cleanOpenAndClosedLists(void) { } #endif - if (m_closedList) { + if (!m_closedList.empty()) { count += PathfindCell::releaseClosedList(m_closedList); - m_closedList = nullptr; + m_closedList.reset(); } #if RETAIL_COMPATIBLE_PATHFINDING @@ -5676,7 +5674,7 @@ struct ExamineCellsStruct // if to was on closed list, remove it from the list if (to->getClosed()) - d->thePathfinder->m_closedList = to->removeFromClosedList( d->thePathfinder->m_closedList ); + to->removeFromClosedList( d->thePathfinder->m_closedList ); // if the to was already on the open list, remove it so it can be re-inserted in order if (to->getOpen()) @@ -5910,7 +5908,7 @@ Int Pathfinder::examineNeighboringCells(PathfindCell *parentCell, PathfindCell * // if newCell was on closed list, remove it from the list if (newCell->getClosed()) - m_closedList = newCell->removeFromClosedList( m_closedList ); + newCell->removeFromClosedList( m_closedList ); // if the newCell was already on the open list, remove it so it can be re-inserted in order if (newCell->getOpen()) @@ -5983,7 +5981,7 @@ Path *Pathfinder::internalFindPath( Object *obj, const LocomotorSet& locomotorSe DEBUG_LOG(("Attempting pathfind to 0,0, generally a bug.")); return nullptr; } - DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList.empty(), ("Dangling lists.")); if (m_isMapReady == false) { return nullptr; } @@ -6107,7 +6105,7 @@ Path *Pathfinder::internalFindPath( Object *obj, const LocomotorSet& locomotorSe } // "closed" list is initially empty - m_closedList = nullptr; + m_closedList.reset(); Int cellCount = 0; @@ -6162,7 +6160,7 @@ Path *Pathfinder::internalFindPath( Object *obj, const LocomotorSet& locomotorSe } // put parent cell onto closed list - its evaluation is finished - m_closedList = parentCell->putOnClosedList( m_closedList ); + parentCell->putOnClosedList( m_closedList ); // Check to see if we can change layers in this cell. checkChangeLayers(parentCell); @@ -6534,7 +6532,7 @@ Path *Pathfinder::findGroundPath( const Coord3D *from, DEBUG_LOG(("Attempting pathfind to 0,0, generally a bug.")); return nullptr; } - DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList.empty(), ("Dangling lists.")); if (m_isMapReady == false) { return nullptr; } @@ -6639,7 +6637,7 @@ Path *Pathfinder::findGroundPath( const Coord3D *from, } // "closed" list is initially empty - m_closedList = nullptr; + m_closedList.reset(); // TheSuperHackers @fix helmutbuhler This was originally uninitialized and in the loop below. #if RETAIL_COMPATIBLE_CRC @@ -6688,7 +6686,7 @@ Path *Pathfinder::findGroundPath( const Coord3D *from, } // put parent cell onto closed list - its evaluation is finished - m_closedList = parentCell->putOnClosedList( m_closedList ); + parentCell->putOnClosedList( m_closedList ); // Check to see if we can change layers in this cell. checkChangeLayers(parentCell); @@ -6823,7 +6821,7 @@ Path *Pathfinder::findGroundPath( const Coord3D *from, // if newCell was on closed list, remove it from the list if (newCell->getClosed()) - m_closedList = newCell->removeFromClosedList( m_closedList ); + newCell->removeFromClosedList( m_closedList ); // if the newCell was already on the open list, remove it so it can be re-inserted in order if (newCell->getOpen()) @@ -6948,14 +6946,14 @@ void Pathfinder::processHierarchicalCell( const ICoord2D &scanCell, const ICoord if (!s_useFixedPathfinding) { if (!newCell->getClosed() && !newCell->getOpen()) { - m_closedList = newCell->putOnClosedList(m_closedList); + newCell->putOnClosedList(m_closedList); } } else #endif { if (newCell->hasInfo() && !newCell->getClosed() && !newCell->getOpen()) { - m_closedList = newCell->putOnClosedList(m_closedList); + newCell->putOnClosedList(m_closedList); } } @@ -7019,7 +7017,7 @@ Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSu DEBUG_LOG(("Attempting pathfind to 0,0, generally a bug.")); return nullptr; } - DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList.empty(), ("Dangling lists.")); if (m_isMapReady == false) { return nullptr; } @@ -7076,7 +7074,7 @@ Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSu parentCell->startPathfind(goalCell); // "closed" list is initially empty - m_closedList = nullptr; + m_closedList.reset(); Int cellCount = 0; @@ -7118,7 +7116,7 @@ Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSu if (cell && startCell) { // Close parent cell; parentCell->removeFromOpenList(m_openList); - m_closedList = parentCell->putOnClosedList(m_closedList); + parentCell->putOnClosedList(m_closedList); if (!startCell->allocateInfo(ndx)) { // TheSuperHackers @info We need to forcefully cleanup dangling pathfinding cells if this failure condition is hit in retail // Retail clients will crash beyond this point, but we attempt to recover by performing a full cleanup then enabling the fixed pathfinding codepath @@ -7267,7 +7265,7 @@ Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSu } startCell->setParentCellHierarchical(parentCell); if (!startCell->getClosed() && !startCell->getOpen()) { - m_closedList = startCell->putOnClosedList(m_closedList); + startCell->putOnClosedList(m_closedList); } } if(!cell->allocateInfo(toNdx)) { @@ -7365,7 +7363,7 @@ Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSu } // put parent cell onto closed list - its evaluation is finished - m_closedList = parentCell->putOnClosedList( m_closedList ); + parentCell->putOnClosedList( m_closedList ); Int i; zoneStorageType examinedZones[PathfindZoneManager::ZONE_BLOCK_SIZE]; @@ -7736,7 +7734,7 @@ Bool Pathfinder::pathDestination( Object *obj, const LocomotorSet& locomotorSet Coord3D adjustTo = *groupDest; Coord3D *to = &adjustTo; - DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList.empty(), ("Dangling lists.")); // create unique "mark" values for open and closed cells for this pathfind invocation Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false; @@ -7811,7 +7809,7 @@ Bool Pathfinder::pathDestination( Object *obj, const LocomotorSet& locomotorSet } // "closed" list is initially empty - m_closedList = nullptr; + m_closedList.reset(); // // Continue search until "open" list is empty, or @@ -7825,7 +7823,7 @@ Bool Pathfinder::pathDestination( Object *obj, const LocomotorSet& locomotorSet Coord3D pos; // put parent cell onto closed list - its evaluation is finished - m_closedList = parentCell->putOnClosedList( m_closedList ); + parentCell->putOnClosedList( m_closedList ); if (checkForAdjust(obj, locomotorSet, isHuman, parentCell->getXIndex(), parentCell->getYIndex(), parentCell->getLayer(), radius, center, &pos, groupDest)) { Int dx = IABS(goalCell->getXIndex()-parentCell->getXIndex()); @@ -7942,7 +7940,7 @@ Bool Pathfinder::pathDestination( Object *obj, const LocomotorSet& locomotorSet // if newCell was on closed list, remove it from the list if (newCell->getClosed()) - m_closedList = newCell->removeFromClosedList( m_closedList ); + newCell->removeFromClosedList( m_closedList ); // if the newCell was already on the open list, remove it so it can be re-inserted in order if (newCell->getOpen()) @@ -8039,7 +8037,7 @@ Int Pathfinder::checkPathCost(Object *obj, const LocomotorSet& locomotorSet, con Coord3D adjustTo = *rawTo; Coord3D *to = &adjustTo; - DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList.empty(), ("Dangling lists.")); // create unique "mark" values for open and closed cells for this pathfind invocation Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false; @@ -8096,7 +8094,7 @@ Int Pathfinder::checkPathCost(Object *obj, const LocomotorSet& locomotorSet, con } // "closed" list is initially empty - m_closedList = nullptr; + m_closedList.reset(); // // Continue search until "open" list is empty, or @@ -8111,7 +8109,7 @@ Int Pathfinder::checkPathCost(Object *obj, const LocomotorSet& locomotorSet, con // put parent cell onto closed list - its evaluation is finished - Retail compatible behaviour #if RETAIL_COMPATIBLE_PATHFINDING if (!s_useFixedPathfinding) { - m_closedList = parentCell->putOnClosedList( m_closedList ); + parentCell->putOnClosedList( m_closedList ); } #endif @@ -8133,7 +8131,7 @@ Int Pathfinder::checkPathCost(Object *obj, const LocomotorSet& locomotorSet, con if (s_useFixedPathfinding) #endif { - m_closedList = parentCell->putOnClosedList( m_closedList ); + parentCell->putOnClosedList( m_closedList ); } if (cellCount > MAX_CELL_COUNT) { @@ -8236,7 +8234,7 @@ Int Pathfinder::checkPathCost(Object *obj, const LocomotorSet& locomotorSet, con // if newCell was on closed list, remove it from the list if (newCell->getClosed()) - m_closedList = newCell->removeFromClosedList( m_closedList ); + newCell->removeFromClosedList( m_closedList ); // if the newCell was already on the open list, remove it so it can be re-inserted in order if (newCell->getOpen()) @@ -8317,7 +8315,7 @@ Path *Pathfinder::findClosestPath( Object *obj, const LocomotorSet& locomotorSet adjustTo.x += PATHFIND_CELL_SIZE_F/2; adjustTo.y += PATHFIND_CELL_SIZE_F/2; } - DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList.empty(), ("Dangling lists.")); // create unique "mark" values for open and closed cells for this pathfind invocation Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false; @@ -8434,7 +8432,7 @@ Path *Pathfinder::findClosestPath( Object *obj, const LocomotorSet& locomotorSet } // "closed" list is initially empty - m_closedList = nullptr; + m_closedList.reset(); Int count = 0; // // Continue search until "open" list is empty, or @@ -8463,7 +8461,7 @@ Path *Pathfinder::findClosestPath( Object *obj, const LocomotorSet& locomotorSet #ifdef INTENSE_DEBUG Int count = 0; PathfindCell *cur; - for (cur = m_closedList; cur; cur=cur->getNextOpen()) { + for (cur = m_closedList.getHead(); cur; cur=cur->getNextOpen()) { count++; } if (count>1000) { @@ -8500,7 +8498,7 @@ Path *Pathfinder::findClosestPath( Object *obj, const LocomotorSet& locomotorSet return path; } // put parent cell onto closed list - its evaluation is finished - m_closedList = parentCell->putOnClosedList( m_closedList ); + parentCell->putOnClosedList( m_closedList ); if (!m_isTunneling && checkDestination(obj, parentCell->getXIndex(), parentCell->getYIndex(), parentCell->getLayer(), radius, centerInCell)) { if (!startedStuck || validMovementPosition( isCrusher, locomotorSet.getValidSurfaces(), parentCell )) { dx = IABS(goalCell->getXIndex()-parentCell->getXIndex()); @@ -9901,7 +9899,7 @@ Path *Pathfinder::getMoveAwayFromPath(Object* obj, Object *otherObj, Int radius; getRadiusAndCenter(obj, radius, centerInCell); - DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList.empty(), ("Dangling lists.")); // determine start cell ICoord2D startCellNdx; @@ -9954,7 +9952,7 @@ Path *Pathfinder::getMoveAwayFromPath(Object* obj, Object *otherObj, } // "closed" list is initially empty - m_closedList = nullptr; + m_closedList.reset(); // // Continue search until "open" list is empty, or @@ -10036,7 +10034,7 @@ Path *Pathfinder::getMoveAwayFromPath(Object* obj, Object *otherObj, return newPath; } // put parent cell onto closed list - its evaluation is finished - m_closedList = parentCell->putOnClosedList( m_closedList ); + parentCell->putOnClosedList( m_closedList ); // Check to see if we can change layers in this cell. checkChangeLayers(parentCell); @@ -10087,7 +10085,7 @@ Path *Pathfinder::patchPath( const Object *obj, const LocomotorSet& locomotorSet m_zoneManager.setAllPassable(); - DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList.empty(), ("Dangling lists.")); enum {CELL_LIMIT = 2000}; // max cells to examine. Int cellCount = 0; @@ -10130,7 +10128,7 @@ Path *Pathfinder::patchPath( const Object *obj, const LocomotorSet& locomotorSet } // "closed" list is initially empty - m_closedList = nullptr; + m_closedList.reset(); // // Continue search until "open" list is empty, or @@ -10259,7 +10257,7 @@ Path *Pathfinder::patchPath( const Object *obj, const LocomotorSet& locomotorSet return path; } // put parent cell onto closed list - its evaluation is finished - m_closedList = parentCell->putOnClosedList( m_closedList ); + parentCell->putOnClosedList( m_closedList ); if (cellCount < CELL_LIMIT) { // Check to see if we can change layers in this cell. @@ -10370,7 +10368,7 @@ Path *Pathfinder::findAttackPath( const Object *obj, const LocomotorSet& locomot Int cellCount = 0; - DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList.empty(), ("Dangling lists.")); Int attackDistance = weapon->getAttackDistance(obj, victim, victimPos); attackDistance += 3*PATHFIND_CELL_SIZE; @@ -10424,7 +10422,7 @@ Path *Pathfinder::findAttackPath( const Object *obj, const LocomotorSet& locomot } // "closed" list is initially empty - m_closedList = nullptr; + m_closedList.reset(); // // Continue search until "open" list is empty, or @@ -10485,7 +10483,7 @@ Path *Pathfinder::findAttackPath( const Object *obj, const LocomotorSet& locomot #ifdef INTENSE_DEBUG Int count = 0; PathfindCell *cur; - for (cur = m_closedList; cur; cur=cur->getNextOpen()) { + for (cur = m_closedList.getHead(); cur; cur=cur->getNextOpen()) { count++; } if (count>1000) { @@ -10537,7 +10535,7 @@ Path *Pathfinder::findAttackPath( const Object *obj, const LocomotorSet& locomot } // put parent cell onto closed list - its evaluation is finished - m_closedList = parentCell->putOnClosedList( m_closedList ); + parentCell->putOnClosedList( m_closedList ); if (cellCount < ATTACK_CELL_LIMIT) { // Check to see if we can change layers in this cell. @@ -10614,7 +10612,7 @@ Path *Pathfinder::findSafePath( const Object *obj, const LocomotorSet& locomotor isHuman = false; // computer gets to cheat. } - DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList.empty(), ("Dangling lists.")); // create unique "mark" values for open and closed cells for this pathfind invocation m_zoneManager.setAllPassable(); @@ -10645,7 +10643,7 @@ Path *Pathfinder::findSafePath( const Object *obj, const LocomotorSet& locomotor } // "closed" list is initially empty - m_closedList = nullptr; + m_closedList.reset(); // // Continue search until "open" list is empty, or @@ -10697,7 +10695,7 @@ Path *Pathfinder::findSafePath( const Object *obj, const LocomotorSet& locomotor #ifdef INTENSE_DEBUG Int count = 0; PathfindCell *cur; - for (cur = m_closedList; cur; cur=cur->getNextOpen()) { + for (cur = m_closedList.getHead(); cur; cur=cur->getNextOpen()) { count++; } if (count>2000) { @@ -10734,7 +10732,7 @@ Path *Pathfinder::findSafePath( const Object *obj, const LocomotorSet& locomotor } // put parent cell onto closed list - its evaluation is finished - m_closedList = parentCell->putOnClosedList( m_closedList ); + parentCell->putOnClosedList( m_closedList ); // Check to see if we can change layers in this cell. checkChangeLayers(parentCell); diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/AIPathfind.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/AIPathfind.h index 98e0b57ca5e..1491f751927 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/AIPathfind.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/AIPathfind.h @@ -333,13 +333,13 @@ class PathfindCell void removeFromOpenList( PathfindCellList &list ); /// put self on "closed" list, return new list - PathfindCell *putOnClosedList( PathfindCell *list ); + void putOnClosedList( PathfindCellList &list ); /// remove self from "closed" list - PathfindCell *removeFromClosedList( PathfindCell *list ); + void removeFromClosedList( PathfindCellList &list ); /// remove all cells from closed list. - static Int releaseClosedList( PathfindCell *list ); + static Int releaseClosedList( PathfindCellList &list ); /// remove all cells from closed list. static Int releaseOpenList( PathfindCellList &list ); @@ -875,7 +875,7 @@ class Pathfinder : PathfindServicesInterface, public Snapshot IRegion2D m_logicalExtent; ///< Logical grid extent limits PathfindCellList m_openList; ///< Cells ready to be explored - PathfindCell *m_closedList; ///< Cells already explored + PathfindCellList m_closedList; ///< Cells already explored Bool m_isMapReady; ///< True if all cells of map have been classified Bool m_isTunneling; ///< True if path started in an obstacle diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp index 599eba344bd..564cff7d769 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp @@ -1133,7 +1133,7 @@ void Pathfinder::forceCleanCells() PathfindCellInfo::forceCleanPathFindCellInfos(); m_openList.reset(); - m_closedList = nullptr; + m_closedList.reset(); for (int j = 0; j <= m_extent.hi.y; ++j) { for (int i = 0; i <= m_extent.hi.x; ++i) { @@ -1799,15 +1799,15 @@ Int PathfindCell::releaseOpenList( PathfindCellList &list ) } /// remove all cells from "closed" list -Int PathfindCell::releaseClosedList( PathfindCell *list ) +Int PathfindCell::releaseClosedList( PathfindCellList &list ) { Int count = 0; - while (list) { + while (list.m_head) { count++; - DEBUG_ASSERTCRASH(list->m_info, ("Has to have info.")); - DEBUG_ASSERTCRASH(list->m_info->m_closed==TRUE && list->m_info->m_open==FALSE, ("Serious error - Invalid flags. jba")); - PathfindCell *cur = list; - PathfindCellInfo *curInfo = list->m_info; + DEBUG_ASSERTCRASH(list.m_head->m_info, ("Has to have info.")); + DEBUG_ASSERTCRASH(list.m_head->m_info->m_closed==TRUE && list.m_head->m_info->m_open==FALSE, ("Serious error - Invalid flags. jba")); + PathfindCell *cur = list.m_head; + PathfindCellInfo *curInfo = list.m_head->m_info; #if RETAIL_COMPATIBLE_PATHFINDING // TheSuperHackers @info This is only here to catch a crash point in the retail compatible pathfinding // One crash mode is where a cell has no PathfindCellInfo, resulting in a nullptr access and a crash. @@ -1820,9 +1820,9 @@ Int PathfindCell::releaseClosedList( PathfindCell *list ) #endif if (curInfo->m_nextOpen) { - list = curInfo->m_nextOpen->m_cell; + list.m_head = curInfo->m_nextOpen->m_cell; } else { - list = nullptr; + list.m_head = nullptr; } DEBUG_ASSERTCRASH(cur == curInfo->m_cell, ("Bad backpointer in PathfindCellInfo")); curInfo->m_nextOpen = nullptr; @@ -1834,7 +1834,7 @@ Int PathfindCell::releaseClosedList( PathfindCell *list ) } /// put self on "closed" list, return new list -PathfindCell *PathfindCell::putOnClosedList( PathfindCell *list ) +void PathfindCell::putOnClosedList( PathfindCellList &list ) { DEBUG_ASSERTCRASH(m_info, ("Has to have info.")); DEBUG_ASSERTCRASH(m_info->m_closed==FALSE && m_info->m_open==FALSE, ("Serious error - Invalid flags. jba")); @@ -1845,18 +1845,17 @@ PathfindCell *PathfindCell::putOnClosedList( PathfindCell *list ) m_info->m_closed = TRUE; m_info->m_prevOpen = nullptr; - m_info->m_nextOpen = list?list->m_info:nullptr; - if (list) - list->m_info->m_prevOpen = this->m_info; + m_info->m_nextOpen = list.m_head ? list.m_head->m_info : nullptr; + if (list.m_head) + list.m_head->m_info->m_prevOpen = this->m_info; - list = this; + list.m_head = this; } - return list; } /// remove self from "closed" list -PathfindCell *PathfindCell::removeFromClosedList( PathfindCell *list ) +void PathfindCell::removeFromClosedList( PathfindCellList &list ) { DEBUG_ASSERTCRASH(m_info, ("Has to have info.")); DEBUG_ASSERTCRASH(m_info->m_closed==TRUE && m_info->m_open==FALSE, ("Serious error - Invalid flags. jba")); @@ -1866,13 +1865,12 @@ PathfindCell *PathfindCell::removeFromClosedList( PathfindCell *list ) if (m_info->m_prevOpen) m_info->m_prevOpen->m_nextOpen = m_info->m_nextOpen; else - list = getNextOpen(); + list.m_head = getNextOpen(); m_info->m_closed = false; m_info->m_nextOpen = nullptr; m_info->m_prevOpen = nullptr; - return list; } /** @@ -3881,7 +3879,7 @@ void Pathfinder::reset( void ) m_extent.lo.x=m_extent.lo.y=m_extent.hi.x=m_extent.hi.y=0; m_logicalExtent.lo.x=m_logicalExtent.lo.y=m_logicalExtent.hi.x=m_logicalExtent.hi.y=0; m_openList.reset(); - m_closedList = nullptr; + m_closedList.reset(); m_ignoreObstacleID = INVALID_ID; m_isTunneling = false; @@ -4649,7 +4647,7 @@ void Pathfinder::debugShowSearch( Bool pathFound ) addIcon(&pos, PATHFIND_CELL_SIZE_F*.6f, 200, color); } - for( s = m_closedList; s; s=s->getNextOpen() ) + for( s = m_closedList.getHead(); s; s=s->getNextOpen() ) { // create objects to show path - they decay RGBColor color; @@ -4754,9 +4752,9 @@ void Pathfinder::cleanOpenAndClosedLists(void) { } #endif - if (m_closedList) { + if (!m_closedList.empty()) { count += PathfindCell::releaseClosedList(m_closedList); - m_closedList = nullptr; + m_closedList.reset(); } #if RETAIL_COMPATIBLE_PATHFINDING @@ -5978,7 +5976,7 @@ struct ExamineCellsStruct // if to was on closed list, remove it from the list if (to->getClosed()) - d->thePathfinder->m_closedList = to->removeFromClosedList( d->thePathfinder->m_closedList ); + to->removeFromClosedList( d->thePathfinder->m_closedList ); // if the to was already on the open list, remove it so it can be re-inserted in order if (to->getOpen()) @@ -6212,7 +6210,7 @@ Int Pathfinder::examineNeighboringCells(PathfindCell *parentCell, PathfindCell * // if newCell was on closed list, remove it from the list if (newCell->getClosed()) - m_closedList = newCell->removeFromClosedList( m_closedList ); + newCell->removeFromClosedList( m_closedList ); // if the newCell was already on the open list, remove it so it can be re-inserted in order if (newCell->getOpen()) @@ -6285,7 +6283,7 @@ Path *Pathfinder::internalFindPath( Object *obj, const LocomotorSet& locomotorSe DEBUG_LOG(("Attempting pathfind to 0,0, generally a bug.")); return nullptr; } - DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList.empty(), ("Dangling lists.")); if (m_isMapReady == false) { return nullptr; } @@ -6409,7 +6407,7 @@ Path *Pathfinder::internalFindPath( Object *obj, const LocomotorSet& locomotorSe } // "closed" list is initially empty - m_closedList = nullptr; + m_closedList.reset(); Int cellCount = 0; @@ -6464,7 +6462,7 @@ Path *Pathfinder::internalFindPath( Object *obj, const LocomotorSet& locomotorSe } // put parent cell onto closed list - its evaluation is finished - m_closedList = parentCell->putOnClosedList( m_closedList ); + parentCell->putOnClosedList( m_closedList ); // Check to see if we can change layers in this cell. checkChangeLayers(parentCell); @@ -6862,7 +6860,7 @@ Path *Pathfinder::findGroundPath( const Coord3D *from, DEBUG_LOG(("Attempting pathfind to 0,0, generally a bug.")); return nullptr; } - DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList.empty(), ("Dangling lists.")); if (m_isMapReady == false) { return nullptr; } @@ -6967,7 +6965,7 @@ Path *Pathfinder::findGroundPath( const Coord3D *from, } // "closed" list is initially empty - m_closedList = nullptr; + m_closedList.reset(); // TheSuperHackers @fix helmutbuhler This was originally uninitialized and in the loop below. #if RETAIL_COMPATIBLE_CRC @@ -7016,7 +7014,7 @@ Path *Pathfinder::findGroundPath( const Coord3D *from, } // put parent cell onto closed list - its evaluation is finished - m_closedList = parentCell->putOnClosedList( m_closedList ); + parentCell->putOnClosedList( m_closedList ); // Check to see if we can change layers in this cell. checkChangeLayers(parentCell); @@ -7151,7 +7149,7 @@ Path *Pathfinder::findGroundPath( const Coord3D *from, // if newCell was on closed list, remove it from the list if (newCell->getClosed()) - m_closedList = newCell->removeFromClosedList( m_closedList ); + newCell->removeFromClosedList( m_closedList ); // if the newCell was already on the open list, remove it so it can be re-inserted in order if (newCell->getOpen()) @@ -7286,14 +7284,14 @@ void Pathfinder::processHierarchicalCell( const ICoord2D &scanCell, const ICoord if (!s_useFixedPathfinding) { if (!newCell->getClosed() && !newCell->getOpen()) { - m_closedList = newCell->putOnClosedList(m_closedList); + newCell->putOnClosedList(m_closedList); } } else #endif { if (newCell->hasInfo() && !newCell->getClosed() && !newCell->getOpen()) { - m_closedList = newCell->putOnClosedList(m_closedList); + newCell->putOnClosedList(m_closedList); } } @@ -7361,7 +7359,7 @@ Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSu DEBUG_LOG(("Attempting pathfind to 0,0, generally a bug.")); return nullptr; } - DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList.empty(), ("Dangling lists.")); if (m_isMapReady == false) { return nullptr; } @@ -7418,7 +7416,7 @@ Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSu parentCell->startPathfind(goalCell); // "closed" list is initially empty - m_closedList = nullptr; + m_closedList.reset(); Int cellCount = 0; @@ -7460,7 +7458,7 @@ Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSu if (cell && startCell) { // Close parent cell; parentCell->removeFromOpenList(m_openList); - m_closedList = parentCell->putOnClosedList(m_closedList); + parentCell->putOnClosedList(m_closedList); if (!startCell->allocateInfo(ndx)) { // TheSuperHackers @info We need to forcefully cleanup dangling pathfinding cells if this failure condition is hit in retail // Retail clients will crash beyond this point, but we attempt to recover by performing a full cleanup then enabling the fixed pathfinding codepath @@ -7609,7 +7607,7 @@ Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSu } startCell->setParentCellHierarchical(parentCell); if (!startCell->getClosed() && !startCell->getOpen()) { - m_closedList = startCell->putOnClosedList(m_closedList); + startCell->putOnClosedList(m_closedList); } } if(!cell->allocateInfo(toNdx)) { @@ -7707,7 +7705,7 @@ Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSu } // put parent cell onto closed list - its evaluation is finished - m_closedList = parentCell->putOnClosedList( m_closedList ); + parentCell->putOnClosedList( m_closedList ); Int i; zoneStorageType examinedZones[PathfindZoneManager::ZONE_BLOCK_SIZE]; @@ -8140,7 +8138,7 @@ Bool Pathfinder::pathDestination( Object *obj, const LocomotorSet& locomotorSet Coord3D adjustTo = *groupDest; Coord3D *to = &adjustTo; - DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList.empty(), ("Dangling lists.")); // create unique "mark" values for open and closed cells for this pathfind invocation Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false; @@ -8215,7 +8213,7 @@ Bool Pathfinder::pathDestination( Object *obj, const LocomotorSet& locomotorSet } // "closed" list is initially empty - m_closedList = nullptr; + m_closedList.reset(); // // Continue search until "open" list is empty, or @@ -8229,7 +8227,7 @@ Bool Pathfinder::pathDestination( Object *obj, const LocomotorSet& locomotorSet Coord3D pos; // put parent cell onto closed list - its evaluation is finished - m_closedList = parentCell->putOnClosedList( m_closedList ); + parentCell->putOnClosedList( m_closedList ); if (checkForAdjust(obj, locomotorSet, isHuman, parentCell->getXIndex(), parentCell->getYIndex(), parentCell->getLayer(), radius, center, &pos, groupDest)) { Int dx = IABS(goalCell->getXIndex()-parentCell->getXIndex()); @@ -8346,7 +8344,7 @@ Bool Pathfinder::pathDestination( Object *obj, const LocomotorSet& locomotorSet // if newCell was on closed list, remove it from the list if (newCell->getClosed()) - m_closedList = newCell->removeFromClosedList( m_closedList ); + newCell->removeFromClosedList( m_closedList ); // if the newCell was already on the open list, remove it so it can be re-inserted in order if (newCell->getOpen()) @@ -8443,7 +8441,7 @@ Int Pathfinder::checkPathCost(Object *obj, const LocomotorSet& locomotorSet, con Coord3D adjustTo = *rawTo; Coord3D *to = &adjustTo; - DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList.empty(), ("Dangling lists.")); // create unique "mark" values for open and closed cells for this pathfind invocation Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false; @@ -8500,7 +8498,7 @@ Int Pathfinder::checkPathCost(Object *obj, const LocomotorSet& locomotorSet, con } // "closed" list is initially empty - m_closedList = nullptr; + m_closedList.reset(); // // Continue search until "open" list is empty, or @@ -8515,7 +8513,7 @@ Int Pathfinder::checkPathCost(Object *obj, const LocomotorSet& locomotorSet, con // put parent cell onto closed list - its evaluation is finished - Retail compatible behaviour #if RETAIL_COMPATIBLE_PATHFINDING if (!s_useFixedPathfinding) { - m_closedList = parentCell->putOnClosedList( m_closedList ); + parentCell->putOnClosedList( m_closedList ); } #endif @@ -8537,7 +8535,7 @@ Int Pathfinder::checkPathCost(Object *obj, const LocomotorSet& locomotorSet, con if (s_useFixedPathfinding) #endif { - m_closedList = parentCell->putOnClosedList( m_closedList ); + parentCell->putOnClosedList( m_closedList ); } if (cellCount > MAX_CELL_COUNT) { @@ -8640,7 +8638,7 @@ Int Pathfinder::checkPathCost(Object *obj, const LocomotorSet& locomotorSet, con // if newCell was on closed list, remove it from the list if (newCell->getClosed()) - m_closedList = newCell->removeFromClosedList( m_closedList ); + newCell->removeFromClosedList( m_closedList ); // if the newCell was already on the open list, remove it so it can be re-inserted in order if (newCell->getOpen()) @@ -8721,7 +8719,7 @@ Path *Pathfinder::findClosestPath( Object *obj, const LocomotorSet& locomotorSet adjustTo.x += PATHFIND_CELL_SIZE_F/2; adjustTo.y += PATHFIND_CELL_SIZE_F/2; } - DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList.empty(), ("Dangling lists.")); // create unique "mark" values for open and closed cells for this pathfind invocation Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false; @@ -8838,7 +8836,7 @@ Path *Pathfinder::findClosestPath( Object *obj, const LocomotorSet& locomotorSet } // "closed" list is initially empty - m_closedList = nullptr; + m_closedList.reset(); Int count = 0; // // Continue search until "open" list is empty, or @@ -8870,7 +8868,7 @@ Path *Pathfinder::findClosestPath( Object *obj, const LocomotorSet& locomotorSet #ifdef INTENSE_DEBUG Int count = 0; PathfindCell *cur; - for (cur = m_closedList; cur; cur=cur->getNextOpen()) { + for (cur = m_closedList.getHead(); cur; cur=cur->getNextOpen()) { count++; } if (count>1000) { @@ -8907,7 +8905,7 @@ Path *Pathfinder::findClosestPath( Object *obj, const LocomotorSet& locomotorSet return path; } // put parent cell onto closed list - its evaluation is finished - m_closedList = parentCell->putOnClosedList( m_closedList ); + parentCell->putOnClosedList( m_closedList ); if (!m_isTunneling && checkDestination(obj, parentCell->getXIndex(), parentCell->getYIndex(), parentCell->getLayer(), radius, centerInCell)) { if (!startedStuck || validMovementPosition( isCrusher, locomotorSet.getValidSurfaces(), parentCell )) { dx = IABS(goalCell->getXIndex()-parentCell->getXIndex()); @@ -10326,7 +10324,7 @@ Path *Pathfinder::getMoveAwayFromPath(Object* obj, Object *otherObj, Int radius; getRadiusAndCenter(obj, radius, centerInCell); - DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList.empty(), ("Dangling lists.")); // determine start cell ICoord2D startCellNdx; @@ -10379,7 +10377,7 @@ Path *Pathfinder::getMoveAwayFromPath(Object* obj, Object *otherObj, } // "closed" list is initially empty - m_closedList = nullptr; + m_closedList.reset(); // // Continue search until "open" list is empty, or @@ -10461,7 +10459,7 @@ Path *Pathfinder::getMoveAwayFromPath(Object* obj, Object *otherObj, return newPath; } // put parent cell onto closed list - its evaluation is finished - m_closedList = parentCell->putOnClosedList( m_closedList ); + parentCell->putOnClosedList( m_closedList ); // Check to see if we can change layers in this cell. checkChangeLayers(parentCell); @@ -10512,7 +10510,7 @@ Path *Pathfinder::patchPath( const Object *obj, const LocomotorSet& locomotorSet m_zoneManager.setAllPassable(); - DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList.empty(), ("Dangling lists.")); enum {CELL_LIMIT = 2000}; // max cells to examine. Int cellCount = 0; @@ -10555,7 +10553,7 @@ Path *Pathfinder::patchPath( const Object *obj, const LocomotorSet& locomotorSet } // "closed" list is initially empty - m_closedList = nullptr; + m_closedList.reset(); // // Continue search until "open" list is empty, or @@ -10684,7 +10682,7 @@ Path *Pathfinder::patchPath( const Object *obj, const LocomotorSet& locomotorSet return path; } // put parent cell onto closed list - its evaluation is finished - m_closedList = parentCell->putOnClosedList( m_closedList ); + parentCell->putOnClosedList( m_closedList ); if (cellCount < CELL_LIMIT) { // Check to see if we can change layers in this cell. @@ -10795,7 +10793,7 @@ Path *Pathfinder::findAttackPath( const Object *obj, const LocomotorSet& locomot Int cellCount = 0; - DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList.empty(), ("Dangling lists.")); Int attackDistance = weapon->getAttackDistance(obj, victim, victimPos); attackDistance += 3*PATHFIND_CELL_SIZE; @@ -10849,7 +10847,7 @@ Path *Pathfinder::findAttackPath( const Object *obj, const LocomotorSet& locomot } // "closed" list is initially empty - m_closedList = nullptr; + m_closedList.reset(); // // Continue search until "open" list is empty, or @@ -10910,7 +10908,7 @@ Path *Pathfinder::findAttackPath( const Object *obj, const LocomotorSet& locomot #ifdef INTENSE_DEBUG Int count = 0; PathfindCell *cur; - for (cur = m_closedList; cur; cur=cur->getNextOpen()) { + for (cur = m_closedList.getHead(); cur; cur=cur->getNextOpen()) { count++; } if (count>1000) { @@ -10929,7 +10927,7 @@ Path *Pathfinder::findAttackPath( const Object *obj, const LocomotorSet& locomot debugShowSearch(true); // put parent cell onto closed list - its evaluation is finished - m_closedList = parentCell->putOnClosedList( m_closedList ); + parentCell->putOnClosedList( m_closedList ); // construct and return path if (obj->isKindOf(KINDOF_VEHICLE)) { // Strip backwards. @@ -11019,7 +11017,7 @@ Path *Pathfinder::findAttackPath( const Object *obj, const LocomotorSet& locomot } // put parent cell onto closed list - its evaluation is finished - m_closedList = parentCell->putOnClosedList( m_closedList ); + parentCell->putOnClosedList( m_closedList ); if (cellCount < ATTACK_CELL_LIMIT) { // Check to see if we can change layers in this cell. @@ -11096,7 +11094,7 @@ Path *Pathfinder::findSafePath( const Object *obj, const LocomotorSet& locomotor isHuman = false; // computer gets to cheat. } - DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList == nullptr, ("Dangling lists.")); + DEBUG_ASSERTCRASH(m_openList.empty() && m_closedList.empty(), ("Dangling lists.")); // create unique "mark" values for open and closed cells for this pathfind invocation m_zoneManager.setAllPassable(); @@ -11127,7 +11125,7 @@ Path *Pathfinder::findSafePath( const Object *obj, const LocomotorSet& locomotor } // "closed" list is initially empty - m_closedList = nullptr; + m_closedList.reset(); // // Continue search until "open" list is empty, or @@ -11179,7 +11177,7 @@ Path *Pathfinder::findSafePath( const Object *obj, const LocomotorSet& locomotor #ifdef INTENSE_DEBUG Int count = 0; PathfindCell *cur; - for (cur = m_closedList; cur; cur=cur->getNextOpen()) { + for (cur = m_closedList.getHead(); cur; cur=cur->getNextOpen()) { count++; } if (count>2000) { @@ -11216,7 +11214,7 @@ Path *Pathfinder::findSafePath( const Object *obj, const LocomotorSet& locomotor } // put parent cell onto closed list - its evaluation is finished - m_closedList = parentCell->putOnClosedList( m_closedList ); + parentCell->putOnClosedList( m_closedList ); // Check to see if we can change layers in this cell. checkChangeLayers(parentCell);