diff --git a/Generals/Code/GameEngine/Include/GameLogic/AIPathfind.h b/Generals/Code/GameEngine/Include/GameLogic/AIPathfind.h index ed300278582..59fdf0ad70d 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/AIPathfind.h +++ b/Generals/Code/GameEngine/Include/GameLogic/AIPathfind.h @@ -651,7 +651,8 @@ class Pathfinder : PathfindServicesInterface, public Snapshot void xfer( Xfer *xfer ); void loadPostProcess(); - Bool quickDoesPathExist( const LocomotorSet& locomotorSet, const Coord3D *from, const Coord3D *to ); ///< Can we build any path at all between the locations (terrain & buildings check - fast) + Bool clientSafeQuickDoesPathExist( const LocomotorSet& locomotorSet, const Coord3D *from, const Coord3D *to ); ///< Can we build any path at all between the locations (terrain & buildings check - fast) + Bool clientSafeQuickDoesPathExistForUI( const LocomotorSet& locomotorSet, const Coord3D *from, const Coord3D *to ); ///< Can we build any path at all between the locations (terrain only - fast) Bool slowDoesPathExist( Object *obj, const Coord3D *from, const Coord3D *to, ObjectID ignoreObject=INVALID_ID ); ///< Can we build any path at all between the locations (terrain, buildings & units check - slower) diff --git a/Generals/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp b/Generals/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp index 91aa3585180..022b56d6d75 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp @@ -5248,10 +5248,10 @@ Bool Pathfinder::checkForAdjust(Object *obj, const LocomotorSet& locomotorSet, B pathExists = true; adjustedPathExists = true; } else { - pathExists = quickDoesPathExist( locomotorSet, obj->getPosition(), dest); - adjustedPathExists = quickDoesPathExist( locomotorSet, obj->getPosition(), &adjustDest); + pathExists = clientSafeQuickDoesPathExist( locomotorSet, obj->getPosition(), dest); + adjustedPathExists = clientSafeQuickDoesPathExist( locomotorSet, obj->getPosition(), &adjustDest); if (!pathExists) { - if (quickDoesPathExist( locomotorSet, dest, &adjustDest)) { + if (clientSafeQuickDoesPathExist( locomotorSet, dest, &adjustDest)) { adjustedPathExists = true; } } @@ -5534,7 +5534,11 @@ Bool Pathfinder::checkForPossible(Bool isCrusher, Int fromZone, Bool center, co { PathfindCell *goalCell = getCell(layer, cellX, cellY); if (!goalCell) return false; +#if RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING if (goalCell->getType() == PathfindCell::CELL_OBSTACLE) return false; +#else + if (IS_IMPASSABLE(goalCell->getType())) return false; +#endif Int zone2 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), isCrusher, goalCell->getZone()); if (startingInObstacle) { zone2 = m_zoneManager.getEffectiveTerrainZone(zone2); @@ -5925,7 +5929,9 @@ void Pathfinder::processPathfindQueue() m_logicalExtent = bounds; m_cumulativeCellsAllocated = 0; // Number of pathfind cells examined. +#ifdef DEBUG_QPF Int pathsFound = 0; +#endif while (m_cumulativeCellsAllocated < PATHFIND_CELLS_PER_FRAME && m_queuePRTail!=m_queuePRHead) { Object *obj = TheGameLogic->findObjectByID(m_queuedPathfindRequests[m_queuePRHead]); @@ -5934,7 +5940,9 @@ void Pathfinder::processPathfindQueue() AIUpdateInterface *ai = obj->getAIUpdateInterface(); if (ai) { ai->doPathfind(this); +#ifdef DEBUG_QPF pathsFound++; +#endif } } m_queuePRHead = m_queuePRHead+1; @@ -5942,8 +5950,8 @@ void Pathfinder::processPathfindQueue() m_queuePRHead = 0; } } - if (pathsFound>0) { #ifdef DEBUG_QPF + if (pathsFound>0) { #ifdef DEBUG_LOGGING QueryPerformanceCounter((LARGE_INTEGER *)&endTime64); timeToUpdate = ((double)(endTime64-startTime64) / (double)(freq64)); @@ -5953,8 +5961,8 @@ void Pathfinder::processPathfindQueue() DEBUG_LOG(("time %f (%f)", timeToUpdate, (::GetTickCount()-startTimeMS)/1000.0f)); } #endif + } #endif - } #if defined(RTS_DEBUG) doDebugIcons(); #endif @@ -6273,14 +6281,18 @@ Int Pathfinder::examineNeighboringCells(PathfindCell *parentCell, PathfindCell * newCell->setBlockedByAlly(false); if (info.allyFixedCount>0) { +#if RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING newCostSoFar += 3*COST_DIAGONAL*info.allyFixedCount; +#else + newCostSoFar += 3*COST_DIAGONAL; +#endif if (!canPathThroughUnits) newCell->setBlockedByAlly(true); } Int costRemaining = 0; if (goalCell) { - if (attackDistance == 0) { + if (attackDistance == NO_ATTACK) { costRemaining = newCell->costToGoal( goalCell ); } else { dx = newCellCoord.x - goalCell->getXIndex(); @@ -6344,7 +6356,7 @@ Int Pathfinder::examineNeighboringCells(PathfindCell *parentCell, PathfindCell * Path *Pathfinder::findPath( Object *obj, const LocomotorSet& locomotorSet, const Coord3D *from, const Coord3D *rawTo) { - if (!quickDoesPathExist(locomotorSet, from, rawTo)) { + if (!clientSafeQuickDoesPathExist(locomotorSet, from, rawTo)) { return nullptr; } Bool isHuman = true; @@ -6789,6 +6801,27 @@ Path *Pathfinder::buildHierarchicalPath( const Coord3D *fromPos, PathfindCell *g prependCells(path, fromPos, goalCell, true); +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING) + // Expand the hierarchical path around the starting point. jba [8/24/2003] + // This allows the unit to get around friendly units that may be near it. + Coord3D pos = *path->getFirstNode()->getPosition(); + Coord3D minPos = pos; + minPos.x -= PathfindZoneManager::ZONE_BLOCK_SIZE*PATHFIND_CELL_SIZE_F; + minPos.y -= PathfindZoneManager::ZONE_BLOCK_SIZE*PATHFIND_CELL_SIZE_F; + Coord3D maxPos = pos; + maxPos.x += PathfindZoneManager::ZONE_BLOCK_SIZE*PATHFIND_CELL_SIZE_F; + maxPos.y += PathfindZoneManager::ZONE_BLOCK_SIZE*PATHFIND_CELL_SIZE_F; + ICoord2D cellNdxMin, cellNdxMax; + worldToCell(&minPos, &cellNdxMin); + worldToCell(&maxPos, &cellNdxMax); + Int i, j; + for (i=cellNdxMin.x; i<=cellNdxMax.x; i++) { + for (j=cellNdxMin.y; j<=cellNdxMax.y; j++) { + m_zoneManager.setPassable(i, j, true); + } + } +#endif + #if defined(RTS_DEBUG) if (TheGlobalData->m_debugAI==AI_DEBUG_PATHS) { @@ -6845,6 +6878,14 @@ struct MADStruct return 0; // Only move allies. } if (otherObj && otherObj->getAI() && !otherObj->getAI()->isMoving()) { +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING) + //Kris: Patch 1.01 November 3, 2003 + //Black Lotus exploit fix -- moving while hacking. + if( otherObj->testStatus( OBJECT_STATUS_IS_USING_ABILITY ) || otherObj->getAI()->isBusy() ) + { + return 0; // Packing or unpacking objects for example + } +#endif //DEBUG_LOG(("Moving ally")); otherObj->getAI()->aiMoveAwayFromUnit(d->obj, CMD_FROM_AI); } @@ -7321,11 +7362,27 @@ void Pathfinder::processHierarchicalCell( const ICoord2D &scanCell, const ICoord scanCell.ym_extent.hi.y) { return; } +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING) + if (parentZone == PathfindZoneManager::UNINITIALIZED_ZONE) { + return; + } +#endif if (parentZone == m_zoneManager.getBlockZone(LOCOMOTORSURFACE_GROUND, crusher, scanCell.x, scanCell.y, m_map)) { PathfindCell *newCell = getCell(LAYER_GROUND, scanCell.x, scanCell.y); +#if RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING if (newCell->hasInfo() && (newCell->getOpen() || newCell->getClosed())) return; // already looked at this one. - ICoord2D adjacentCell = scanCell; +#else + if( !newCell->hasInfo() ) + { + return; + } + + if( newCell->getOpen() || newCell->getClosed() ) + return; // already looked at this one. +#endif + + ICoord2D adjacentCell = scanCell; //DEBUG_ASSERTCRASH(parentZone==newCell->getZone(), ("Different zones?")); if (parentZone!=newCell->getZone()) return; adjacentCell.x += delta.x; @@ -7374,21 +7431,25 @@ void Pathfinder::processHierarchicalCell( const ICoord2D &scanCell, const ICoord } adjNewCell->allocateInfo(adjacentCell); - cellCount++; - Int curCost = adjNewCell->costToHierGoal(parentCell); - Int remCost = adjNewCell->costToHierGoal(goalCell); - if (adjNewCell->getPinched() || newCell->getPinched()) { - curCost += 2*COST_ORTHOGONAL; - } else { - examinedZones[numExZones] = newZone; - numExZones++; - } + if( adjNewCell->hasInfo() ) + { - adjNewCell->setCostSoFar(parentCell->getCostSoFar() + curCost); - adjNewCell->setTotalCost(adjNewCell->getCostSoFar()+remCost); - adjNewCell->setParentCellHierarchical(parentCell); - // insert newCell in open list such that open list is sorted, smallest total path cost first - adjNewCell->putOnSortedOpenList( m_openList ); + cellCount++; + Int curCost = adjNewCell->costToHierGoal(parentCell); + Int remCost = adjNewCell->costToHierGoal(goalCell); + if (adjNewCell->getPinched() || newCell->getPinched()) { + curCost += 2*COST_ORTHOGONAL; + } else { + examinedZones[numExZones] = newZone; + numExZones++; + } + + adjNewCell->setCostSoFar(parentCell->getCostSoFar() + curCost); + adjNewCell->setTotalCost(adjNewCell->getCostSoFar()+remCost); + adjNewCell->setParentCellHierarchical(parentCell); + // insert newCell in open list such that open list is sorted, smallest total path cost first + adjNewCell->putOnSortedOpenList( m_openList ); + } } } @@ -8043,12 +8104,15 @@ Bool Pathfinder::findBrokenBridge(const LocomotorSet& locoSet, * False means it is impossible to path. * True means it is possible given the terrain, but there may be units in the way. */ -Bool Pathfinder::quickDoesPathExist( const LocomotorSet& locomotorSet, +Bool Pathfinder::clientSafeQuickDoesPathExist( const LocomotorSet& locomotorSet, const Coord3D *from, const Coord3D *to ) { // See if terrain or building is blocking the destination. PathfindLayerEnum destinationLayer = TheTerrainLogic->getLayerForDestination(to); + if (!validMovementPosition(false, destinationLayer, locomotorSet, to)) { + return false; + } PathfindLayerEnum fromLayer = TheTerrainLogic->getLayerForDestination(from); Int zone1, zone2; @@ -8062,6 +8126,13 @@ Bool Pathfinder::quickDoesPathExist( const LocomotorSet& locomotorSet, if (parentCell->getType() == PathfindCell::CELL_OBSTACLE) { doingTerrainZone = true; +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING) + if (zone1 == PathfindZoneManager::UNINITIALIZED_ZONE) { + // We are in a building that just got placed, and zones haven't been updated yet. [8/8/2003] + // It is better to return a false positive than a false negative. jba. + return true; + } +#endif } zone2 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), false, goalCell->getZone()); if (goalCell->getType() == PathfindCell::CELL_OBSTACLE) { @@ -8077,8 +8148,62 @@ Bool Pathfinder::quickDoesPathExist( const LocomotorSet& locomotorSet, zone2 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), false, zone2); zone2 = m_zoneManager.getEffectiveTerrainZone(zone2); } - if (!validMovementPosition(false, destinationLayer, locomotorSet, to)) { - return false; + // If the terrain is connected using this locomotor set, we can path somehow. + if (zone1 == zone2) { + // There is not terrain blocking the from & to. + return true; + } + return FALSE; // no path exists + +} + +/** + * Does any path exist from 'from' to 'to' given the locomotor set + * This is the quick check, only looks at whether the terrain is possible or + * impossible to path over. Doesn't take other units into account. + * False means it is impossible to path. + * True means it is possible given the terrain, but there may be units in the way. + */ +Bool Pathfinder::clientSafeQuickDoesPathExistForUI( const LocomotorSet& locomotorSet, + const Coord3D *from, + const Coord3D *to ) +{ + // See if terrain or building is blocking the destination. + PathfindLayerEnum destinationLayer = TheTerrainLogic->getLayerForDestination(to); + PathfindLayerEnum fromLayer = TheTerrainLogic->getLayerForDestination(from); + Int zone1, zone2; + + PathfindCell *parentCell = getClippedCell(fromLayer, from); + PathfindCell *goalCell = getClippedCell(destinationLayer, to); + if (goalCell->getType()==PathfindCell::CELL_CLIFF) { + return false; // No goals on cliffs. + } + + zone1 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), false, parentCell->getZone()); + zone2 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), false, goalCell->getZone()); + + if (zone1 == PathfindZoneManager::UNINITIALIZED_ZONE || + zone2 == PathfindZoneManager::UNINITIALIZED_ZONE) { + // We are in a building that just got placed, and zones haven't been updated yet. [8/8/2003] + // It is better to return a false positive than a false negative. jba. + return true; + } + /* Do the effective terrain zone. This feedback is for the ui, so we won't take structures into account, + because if they are visible it will be obvious, and if they are stealthed they should be invisible to the + pathing as well. jba. */ + zone1 = parentCell->getZone(); + zone1 = m_zoneManager.getEffectiveTerrainZone(zone1); + zone1 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), false, zone1); + zone1 = m_zoneManager.getEffectiveTerrainZone(zone1); + zone2 = goalCell->getZone(); + zone2 = m_zoneManager.getEffectiveTerrainZone(zone2); + zone2 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), false, zone2); + zone2 = m_zoneManager.getEffectiveTerrainZone(zone2); + + if (zone1 == PathfindZoneManager::UNINITIALIZED_ZONE) { + // We are in a building that just got placed, and zones haven't been updated yet. [8/8/2003] + // It is better to return a false positive than a false negative. jba. + return true; } // If the terrain is connected using this locomotor set, we can path somehow. if (zone1 == zone2) { @@ -8521,7 +8646,11 @@ Path *Pathfinder::findClosestPath( Object *obj, const LocomotorSet& locomotorSet PathfindCell *ignoreCell = getClippedCell(goalObj->getLayer(), goalObj->getPosition()); if ( (goalCell->getObstacleID()==ignoreCell->getObstacleID()) && (goalCell->getObstacleID() != INVALID_ID) ) { Object* newObstacle = TheGameLogic->findObjectByID(goalCell->getObstacleID()); +#if RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING if (newObstacle != nullptr && newObstacle->isKindOf(KINDOF_AIRFIELD)) +#else + if (newObstacle != nullptr && newObstacle->isKindOf(KINDOF_FS_AIRFIELD)) +#endif { m_ignoreObstacleID = goalCell->getObstacleID(); goalOnObstacle = true; @@ -8614,6 +8743,7 @@ Path *Pathfinder::findClosestPath( Object *obj, const LocomotorSet& locomotorSet // Continue search until "open" list is empty, or // until goal is found. // + Bool foundGoal = false; while( !m_openList.empty() ) { Real dx; @@ -8629,7 +8759,13 @@ Path *Pathfinder::findClosestPath( Object *obj, const LocomotorSet& locomotorSet if (!goalOnObstacle) { // See if the goal is a valid destination. If not, accept closest cell. if (closesetCell!=nullptr && !canPathThroughUnits && !checkDestination(obj, parentCell->getXIndex(), parentCell->getYIndex(), parentCell->getLayer(), radius, centerInCell)) { +#if RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING break; +#else + foundGoal = true; + // Continue processing the open list to find a possibly closer cell. jba. [8/25/2003] + continue; +#endif } } @@ -8710,12 +8846,12 @@ Path *Pathfinder::findClosestPath( Object *obj, const LocomotorSet& locomotorSet continue; } } - - // Check to see if we can change layers in this cell. - checkChangeLayers(parentCell); - - - count += examineNeighboringCells(parentCell, goalCell, locomotorSet, isHuman, centerInCell, radius, startCellNdx, obj, NO_ATTACK); + // If we haven't already found the goal cell, continue examining. [8/25/2003] + if (!foundGoal) { + // Check to see if we can change layers in this cell. + checkChangeLayers(parentCell); + count += examineNeighboringCells(parentCell, goalCell, locomotorSet, isHuman, centerInCell, radius, startCellNdx, obj, NO_ATTACK); + } } if (closesetCell) { @@ -9595,8 +9731,16 @@ void Pathfinder::updateGoal( Object *obj, const Coord3D *newGoalPos, PathfindLay AIUpdateInterface *ai = obj->getAIUpdateInterface(); if (!ai) return; // only consider ai objects. if (!ai->isDoingGroundMovement()) { - updateAircraftGoal(obj, newGoalPos); - return; +#if RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING + Bool isUnmannedHelicopter = false; +#else + // exception:sniped choppers are on ground + Bool isUnmannedHelicopter = ( obj->isKindOf( KINDOF_PRODUCED_AT_HELIPAD ) && obj->isDisabledByType( DISABLED_UNMANNED ) ) ; +#endif + if (!isUnmannedHelicopter) { + updateAircraftGoal(obj, newGoalPos); + return; + } } PathfindLayerEnum originalLayer = obj->getDestinationLayer(); @@ -10036,6 +10180,18 @@ if (g_UT_startTiming) return false; continue; } +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING) + if (otherObj->getAI()->isAttacking()) { + continue; // Don't move units that are attacking. [8/14/2003] + } + + //Kris: Patch 1.01 November 3, 2003 + //Black Lotus exploit fix -- moving while hacking. + if( otherObj->testStatus( OBJECT_STATUS_IS_USING_ABILITY ) || otherObj->getAI()->isBusy() ) { + continue; // Packing or unpacking objects for example + } +#endif + //DEBUG_LOG(("Moving ally")); otherObj->getAI()->aiMoveAwayFromUnit(obj, CMD_FROM_AI); } @@ -10676,6 +10832,68 @@ Path *Pathfinder::findAttackPath( const Object *obj, const LocomotorSet& locomot if (show) debugShowSearch(true); +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING) + // put parent cell onto closed list - its evaluation is finished + parentCell->putOnClosedList( m_closedList ); + + if (obj->isKindOf(KINDOF_VEHICLE)) { + // Strip backwards. + PathfindCell *lastBlocked = nullptr; + PathfindCell *cur = parentCell; + Bool useLargeRadius = false; + Int cellLimit = 12; // Magic number, yes I know - jba. It is about 4 * size of an average vehicle width (3 cells) [8/15/2003] + while (cur) { + cellLimit--; + if (cellLimit<0) { + break; + } + TCheckMovementInfo info; + info.cell.x = cur->getXIndex(); + info.cell.y = cur->getYIndex(); + info.layer = cur->getLayer(); + if (useLargeRadius) { + info.centerInCell = centerInCell; + info.radius = radius; + } else { + info.centerInCell = true; + info.radius = 0; + } + info.considerTransient = false; + info.acceptableSurfaces = locomotorSet.getValidSurfaces(); + PathfindCell *cell = getCell(info.layer,info.cell.x,info.cell.y); + Bool unitIdle = false; + if (cell) { + ObjectID posUnit = cell->getPosUnit(); + Object *unit = TheGameLogic->findObjectByID(posUnit); + if (unit && unit->getAI() && unit->getAI()->isIdle()) { + unitIdle = true; + } + } + Bool checkMovement = checkForMovement(obj, info); + Bool blockedByEnemy = info.enemyFixed; + Bool blockedByAllies = info.allyFixedCount || info.allyGoal; + if (unitIdle) { + // If the unit present is idle, it doesn't block allies. [8/18/2003] + blockedByAllies = false; + } + + + if (!checkMovement || blockedByEnemy || blockedByAllies) { + lastBlocked = cur; + useLargeRadius = true; + } else { + useLargeRadius = false; + } + cur = cur->getParentCell(); + } + if (lastBlocked) { + parentCell = lastBlocked; + if (lastBlocked->getParentCell()) { + parentCell = lastBlocked->getParentCell(); + } + } + } +#endif // construct and return path Path *path = buildActualPath( obj, locomotorSet.getValidSurfaces(), obj->getPosition(), parentCell, centerInCell, false); #if RETAIL_COMPATIBLE_PATHFINDING diff --git a/Generals/Code/GameEngine/Source/GameLogic/AI/AIPlayer.cpp b/Generals/Code/GameEngine/Source/GameLogic/AI/AIPlayer.cpp index ace3419bde6..b96483a33b5 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/AI/AIPlayer.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/AI/AIPlayer.cpp @@ -605,7 +605,7 @@ Object *AIPlayer::buildStructureWithDozer(const ThingTemplate *bldgPlan, BuildLi } TheTerrainVisual->removeAllBibs(); // isLocationLegalToBuild adds bib feedback, turn it off. jba. - if (!TheAI->pathfinder()->quickDoesPathExist(dozer->getAI()->getLocomotorSet(), + if (!TheAI->pathfinder()->clientSafeQuickDoesPathExist(dozer->getAI()->getLocomotorSet(), dozer->getPosition(), &pos)) { AsciiString bldgName = bldgPlan->getName(); bldgName.concat(" - Dozer unable to reach building. Teleporting."); diff --git a/Generals/Code/GameEngine/Source/GameLogic/AI/AISkirmishPlayer.cpp b/Generals/Code/GameEngine/Source/GameLogic/AI/AISkirmishPlayer.cpp index 5230b44554b..afb876af064 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/AI/AISkirmishPlayer.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/AI/AISkirmishPlayer.cpp @@ -711,7 +711,7 @@ Bool AISkirmishPlayer::checkBridges(Object *unit, Waypoint *way) const LocomotorSet& locoSet = ai->getLocomotorSet(); Waypoint *curWay; for (curWay = way; curWay; curWay = curWay->getNext()) { - if (TheAI->pathfinder()->quickDoesPathExist(locoSet, &unitPos, curWay->getLocation())) { + if (TheAI->pathfinder()->clientSafeQuickDoesPathExist(locoSet, &unitPos, curWay->getLocation())) { continue; } ObjectID brokenBridge = INVALID_ID; diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/PartitionManager.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/PartitionManager.cpp index 852009b0de6..2bb86292763 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/PartitionManager.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/PartitionManager.cpp @@ -3924,7 +3924,7 @@ Bool PartitionManager::tryPosition( const Coord3D *center, const AIUpdateInterface *ai = options->sourceToPathToDest->getAIUpdateInterface(); // check for path existence - if( ai && TheAI->pathfinder()->quickDoesPathExist( ai->getLocomotorSet(), + if( ai && TheAI->pathfinder()->clientSafeQuickDoesPathExist( ai->getLocomotorSet(), options->sourceToPathToDest->getPosition(), &pos ) == FALSE ) return FALSE; diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate.cpp index b69a8520a32..dd3b07d378d 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate.cpp @@ -2043,7 +2043,7 @@ Bool AIUpdateInterface::isPathAvailable( const Coord3D *destination ) const const Coord3D *myPos = getObject()->getPosition(); - return TheAI->pathfinder()->quickDoesPathExist( m_locomotorSet, myPos, destination ); + return TheAI->pathfinder()->clientSafeQuickDoesPathExist( m_locomotorSet, myPos, destination ); } @@ -2060,7 +2060,11 @@ Bool AIUpdateInterface::isQuickPathAvailable( const Coord3D *destination ) const const Coord3D *myPos = getObject()->getPosition(); - return TheAI->pathfinder()->quickDoesPathExist( m_locomotorSet, myPos, destination ); +#if RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING + return TheAI->pathfinder()->clientSafeQuickDoesPathExist(m_locomotorSet, myPos, destination); +#else + return TheAI->pathfinder()->clientSafeQuickDoesPathExistForUI(m_locomotorSet, myPos, destination); +#endif } diff --git a/Generals/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp b/Generals/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp index 843af13f4e9..d6a8a1aeac7 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp @@ -149,7 +149,7 @@ static void doSetRallyPoint( Object *obj, const Coord3D& pos ) NameKeyType key = NAMEKEY( "BasicHumanLocomotor" ); LocomotorSet locomotorSet; locomotorSet.addLocomotor( TheLocomotorStore->findLocomotorTemplate( key ) ); - if( TheAI->pathfinder()->quickDoesPathExist( locomotorSet, obj->getPosition(), &pos ) == FALSE ) + if( TheAI->pathfinder()->clientSafeQuickDoesPathExist( locomotorSet, obj->getPosition(), &pos ) == FALSE ) { // user feedback diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/AIPathfind.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/AIPathfind.h index 4d2177adfd4..ae1ba7e77ae 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/AIPathfind.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/AIPathfind.h @@ -652,7 +652,7 @@ class Pathfinder : PathfindServicesInterface, public Snapshot void loadPostProcess(); Bool clientSafeQuickDoesPathExist( const LocomotorSet& locomotorSet, const Coord3D *from, const Coord3D *to ); ///< Can we build any path at all between the locations (terrain & buildings check - fast) - Bool clientSafeQuickDoesPathExistForUI( const LocomotorSet& locomotorSet, const Coord3D *from, const Coord3D *to ); ///< Can we build any path at all between the locations (terrain onlyk - fast) + Bool clientSafeQuickDoesPathExistForUI( const LocomotorSet& locomotorSet, const Coord3D *from, const Coord3D *to ); ///< Can we build any path at all between the locations (terrain only - fast) Bool slowDoesPathExist( Object *obj, const Coord3D *from, const Coord3D *to, ObjectID ignoreObject=INVALID_ID ); ///< Can we build any path at all between the locations (terrain, buildings & units check - slower) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp index b92934e854a..4dddc9560f0 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp @@ -5534,7 +5534,11 @@ Bool Pathfinder::checkForPossible(Bool isCrusher, Int fromZone, Bool center, co { PathfindCell *goalCell = getCell(layer, cellX, cellY); if (!goalCell) return false; +#if RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING + if (goalCell->getType() == PathfindCell::CELL_OBSTACLE) return false; +#else if (IS_IMPASSABLE(goalCell->getType())) return false; +#endif Int zone2 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), isCrusher, goalCell->getZone()); if (startingInObstacle) { zone2 = m_zoneManager.getEffectiveTerrainZone(zone2); @@ -5946,8 +5950,8 @@ void Pathfinder::processPathfindQueue() m_queuePRHead = 0; } } - if (pathsFound>0) { #ifdef DEBUG_QPF + if (pathsFound>0) { #ifdef DEBUG_LOGGING QueryPerformanceCounter((LARGE_INTEGER *)&endTime64); timeToUpdate = ((double)(endTime64-startTime64) / (double)(freq64)); @@ -5957,8 +5961,8 @@ void Pathfinder::processPathfindQueue() DEBUG_LOG(("time %f (%f)", timeToUpdate, (::GetTickCount()-startTimeMS)/1000.0f)); } #endif + } #endif - } #if defined(RTS_DEBUG) doDebugIcons(); #endif @@ -6277,7 +6281,11 @@ Int Pathfinder::examineNeighboringCells(PathfindCell *parentCell, PathfindCell * newCell->setBlockedByAlly(false); if (info.allyFixedCount>0) { +#if RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING + newCostSoFar += 3*COST_DIAGONAL*info.allyFixedCount; +#else newCostSoFar += 3*COST_DIAGONAL; +#endif if (!canPathThroughUnits) newCell->setBlockedByAlly(true); } @@ -6793,6 +6801,7 @@ Path *Pathfinder::buildHierarchicalPath( const Coord3D *fromPos, PathfindCell *g prependCells(path, fromPos, goalCell, true); +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING) // Expand the hierarchical path around the starting point. jba [8/24/2003] // This allows the unit to get around friendly units that may be near it. Coord3D pos = *path->getFirstNode()->getPosition(); @@ -6811,6 +6820,7 @@ Path *Pathfinder::buildHierarchicalPath( const Coord3D *fromPos, PathfindCell *g m_zoneManager.setPassable(i, j, true); } } +#endif #if defined(RTS_DEBUG) if (TheGlobalData->m_debugAI==AI_DEBUG_PATHS) @@ -6867,14 +6877,15 @@ struct MADStruct if (d->obj->getRelationship(otherObj)!=ALLIES) { return 0; // Only move allies. } - if( otherObj && otherObj->getAI() && !otherObj->getAI()->isMoving() ) - { + if (otherObj && otherObj->getAI() && !otherObj->getAI()->isMoving()) { +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING) //Kris: Patch 1.01 November 3, 2003 //Black Lotus exploit fix -- moving while hacking. if( otherObj->testStatus( OBJECT_STATUS_IS_USING_ABILITY ) || otherObj->getAI()->isBusy() ) { return 0; // Packing or unpacking objects for example } +#endif //DEBUG_LOG(("Moving ally")); otherObj->getAI()->aiMoveAwayFromUnit(d->obj, CMD_FROM_AI); } @@ -7351,12 +7362,17 @@ void Pathfinder::processHierarchicalCell( const ICoord2D &scanCell, const ICoord scanCell.ym_extent.hi.y) { return; } +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING) if (parentZone == PathfindZoneManager::UNINITIALIZED_ZONE) { return; } +#endif if (parentZone == m_zoneManager.getBlockZone(LOCOMOTORSURFACE_GROUND, crusher, scanCell.x, scanCell.y, m_map)) { PathfindCell *newCell = getCell(LAYER_GROUND, scanCell.x, scanCell.y); +#if RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING + if (newCell->hasInfo() && (newCell->getOpen() || newCell->getClosed())) return; // already looked at this one. +#else if( !newCell->hasInfo() ) { return; @@ -7364,6 +7380,7 @@ void Pathfinder::processHierarchicalCell( const ICoord2D &scanCell, const ICoord if( newCell->getOpen() || newCell->getClosed() ) return; // already looked at this one. +#endif ICoord2D adjacentCell = scanCell; //DEBUG_ASSERTCRASH(parentZone==newCell->getZone(), ("Different zones?")); @@ -8109,11 +8126,13 @@ Bool Pathfinder::clientSafeQuickDoesPathExist( const LocomotorSet& locomotorSet, if (parentCell->getType() == PathfindCell::CELL_OBSTACLE) { doingTerrainZone = true; +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING) if (zone1 == PathfindZoneManager::UNINITIALIZED_ZONE) { // We are in a building that just got placed, and zones haven't been updated yet. [8/8/2003] // It is better to return a false positive than a false negative. jba. return true; } +#endif } zone2 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), false, goalCell->getZone()); if (goalCell->getType() == PathfindCell::CELL_OBSTACLE) { @@ -8627,7 +8646,11 @@ Path *Pathfinder::findClosestPath( Object *obj, const LocomotorSet& locomotorSet PathfindCell *ignoreCell = getClippedCell(goalObj->getLayer(), goalObj->getPosition()); if ( (goalCell->getObstacleID()==ignoreCell->getObstacleID()) && (goalCell->getObstacleID() != INVALID_ID) ) { Object* newObstacle = TheGameLogic->findObjectByID(goalCell->getObstacleID()); +#if RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING + if (newObstacle != nullptr && newObstacle->isKindOf(KINDOF_AIRFIELD)) +#else if (newObstacle != nullptr && newObstacle->isKindOf(KINDOF_FS_AIRFIELD)) +#endif { m_ignoreObstacleID = goalCell->getObstacleID(); goalOnObstacle = true; @@ -8736,9 +8759,13 @@ Path *Pathfinder::findClosestPath( Object *obj, const LocomotorSet& locomotorSet if (!goalOnObstacle) { // See if the goal is a valid destination. If not, accept closest cell. if (closesetCell!=nullptr && !canPathThroughUnits && !checkDestination(obj, parentCell->getXIndex(), parentCell->getYIndex(), parentCell->getLayer(), radius, centerInCell)) { +#if RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING + break; +#else foundGoal = true; // Continue processing the open list to find a possibly closer cell. jba. [8/25/2003] continue; +#endif } } @@ -9703,18 +9730,17 @@ void Pathfinder::updateGoal( Object *obj, const Coord3D *newGoalPos, PathfindLay AIUpdateInterface *ai = obj->getAIUpdateInterface(); if (!ai) return; // only consider ai objects. - - - - if (!ai->isDoingGroundMovement()) // exception:sniped choppers are on ground - { - - Bool isUnmannedHelicopter = ( obj->isKindOf( KINDOF_PRODUCED_AT_HELIPAD ) && obj->isDisabledByType( DISABLED_UNMANNED ) ) ; - if ( ! isUnmannedHelicopter ) - { - updateAircraftGoal(obj, newGoalPos); - return; - } + if (!ai->isDoingGroundMovement()) { +#if RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING + Bool isUnmannedHelicopter = false; +#else + // exception:sniped choppers are on ground + Bool isUnmannedHelicopter = ( obj->isKindOf( KINDOF_PRODUCED_AT_HELIPAD ) && obj->isDisabledByType( DISABLED_UNMANNED ) ) ; +#endif + if (!isUnmannedHelicopter) { + updateAircraftGoal(obj, newGoalPos); + return; + } } PathfindLayerEnum originalLayer = obj->getDestinationLayer(); @@ -10154,6 +10180,7 @@ if (g_UT_startTiming) return false; continue; } +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING) if (otherObj->getAI()->isAttacking()) { continue; // Don't move units that are attacking. [8/14/2003] } @@ -10163,6 +10190,7 @@ if (g_UT_startTiming) return false; if( otherObj->testStatus( OBJECT_STATUS_IS_USING_ABILITY ) || otherObj->getAI()->isBusy() ) { continue; // Packing or unpacking objects for example } +#endif //DEBUG_LOG(("Moving ally")); otherObj->getAI()->aiMoveAwayFromUnit(obj, CMD_FROM_AI); @@ -10804,9 +10832,10 @@ Path *Pathfinder::findAttackPath( const Object *obj, const LocomotorSet& locomot if (show) debugShowSearch(true); +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING) // put parent cell onto closed list - its evaluation is finished parentCell->putOnClosedList( m_closedList ); - // construct and return path + if (obj->isKindOf(KINDOF_VEHICLE)) { // Strip backwards. PathfindCell *lastBlocked = nullptr; @@ -10864,6 +10893,8 @@ Path *Pathfinder::findAttackPath( const Object *obj, const LocomotorSet& locomot } } } +#endif + // construct and return path Path *path = buildActualPath( obj, locomotorSet.getValidSurfaces(), obj->getPosition(), parentCell, centerInCell, false); #if RETAIL_COMPATIBLE_PATHFINDING if (!s_useFixedPathfinding) { diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate.cpp index 24d3962d609..76c94b122c3 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate.cpp @@ -2102,7 +2102,11 @@ Bool AIUpdateInterface::isQuickPathAvailable( const Coord3D *destination ) const const Coord3D *myPos = getObject()->getPosition(); - return TheAI->pathfinder()->clientSafeQuickDoesPathExistForUI( m_locomotorSet, myPos, destination ); +#if RTS_GENERALS && RETAIL_COMPATIBLE_PATHFINDING + return TheAI->pathfinder()->clientSafeQuickDoesPathExist(m_locomotorSet, myPos, destination); +#else + return TheAI->pathfinder()->clientSafeQuickDoesPathExistForUI(m_locomotorSet, myPos, destination); +#endif }