/* Request a new multipath, store the result and return a handle-id to it. */ unsigned int CPathManager::RequestPath( CSolidObject* caller, const MoveDef* moveDef, float3 startPos, float3 goalPos, float goalRadius, bool synced ) { if (!IsFinalized()) return 0; // in misc since it is called from many points SCOPED_TIMER("Misc::Path::RequestPath"); startPos.ClampInBounds(); goalPos.ClampInBounds(); // Create an estimator definition. goalRadius = std::max<float>(goalRadius, PATH_NODE_SPACING * SQUARE_SIZE); //FIXME do on a per PE & PF level? assert(moveDef == moveDefHandler->GetMoveDefByPathType(moveDef->pathType)); MultiPath newPath = MultiPath(moveDef, startPos, goalPos, goalRadius); newPath.finalGoal = goalPos; newPath.caller = caller; newPath.peDef.synced = synced; if (caller != nullptr) caller->UnBlock(); const IPath::SearchResult result = ArrangePath(&newPath, moveDef, startPos, goalPos, caller); unsigned int pathID = 0; if (result != IPath::Error) { if (newPath.maxResPath.path.empty()) { if (result != IPath::CantGetCloser) { LowRes2MedRes(newPath, startPos, caller, synced); MedRes2MaxRes(newPath, startPos, caller, synced); } else { // add one dummy waypoint so that the calling MoveType // does not consider this request a failure, which can // happen when startPos is very close to goalPos // // otherwise, code relying on MoveType::progressState // (eg. BuilderCAI::MoveInBuildRange) would misbehave // (eg. reject build orders) newPath.maxResPath.path.push_back(startPos); newPath.maxResPath.squares.push_back(int2(startPos.x / SQUARE_SIZE, startPos.z / SQUARE_SIZE)); } } FinalizePath(&newPath, startPos, goalPos, result == IPath::CantGetCloser); newPath.searchResult = result; pathID = Store(newPath); } if (caller != nullptr) caller->Block(); return pathID; }
/* Removes and return the next waypoint in the multipath corresponding to given id. */ float3 CPathManager::NextWayPoint( const CSolidObject* owner, unsigned int pathID, unsigned int numRetries, float3 callerPos, float radius, bool synced ) { // in misc since it is called from many points SCOPED_TIMER("Misc::Path::NextWayPoint"); const float3 noPathPoint = -XZVector; if (!IsFinalized()) return noPathPoint; // 0 indicates the null-path ID if (pathID == 0) return noPathPoint; // find corresponding multipath entry MultiPath* multiPath = GetMultiPath(pathID); if (multiPath == nullptr) return noPathPoint; if (numRetries > MAX_PATH_REFINEMENT_DEPTH) return (multiPath->finalGoal); IPath::Path& maxResPath = multiPath->maxResPath; IPath::Path& medResPath = multiPath->medResPath; IPath::Path& lowResPath = multiPath->lowResPath; if ((callerPos == ZeroVector) && !maxResPath.path.empty()) callerPos = maxResPath.path.back(); assert(multiPath->peDef.synced == synced); #define EXTEND_PATH_POINTS(curResPts, nxtResPts, dist) ((!curResPts.empty() && (curResPts.back()).SqDistance2D(callerPos) < Square((dist))) || nxtResPts.size() <= 2) const bool extendMaxResPath = EXTEND_PATH_POINTS(medResPath.path, maxResPath.path, MAXRES_SEARCH_DISTANCE_EXT); const bool extendMedResPath = EXTEND_PATH_POINTS(lowResPath.path, medResPath.path, MEDRES_SEARCH_DISTANCE_EXT); #undef EXTEND_PATH_POINTS // check whether the max-res path needs extending through // recursive refinement of its lower-resolution segments // if so, check if the med-res path also needs extending if (extendMaxResPath) { if (multiPath->caller != nullptr) multiPath->caller->UnBlock(); if (extendMedResPath) LowRes2MedRes(*multiPath, callerPos, owner, synced); MedRes2MaxRes(*multiPath, callerPos, owner, synced); if (multiPath->caller != nullptr) multiPath->caller->Block(); FinalizePath(multiPath, callerPos, multiPath->finalGoal, multiPath->searchResult == IPath::CantGetCloser); } float3 waypoint = noPathPoint; do { // eat waypoints from the max-res path until we // find one that lies outside the search-radius // or equals the goal // // if this is not possible, then either we are // at the goal OR the path could not reach all // the way to it (ie. a GoalOutOfRange result) // OR we are stuck on an impassable square if (maxResPath.path.empty()) { if (lowResPath.path.empty() && medResPath.path.empty()) { if (multiPath->searchResult == IPath::Ok) { waypoint = multiPath->finalGoal; break; } else { // reached in the CantGetCloser case for any max-res searches // that start within their goal radius (ie. have no waypoints) // RequestPath always puts startPos into maxResPath to handle // this so waypoint will have been set to it (during previous // iteration) if we end up here break; } } else { waypoint = NextWayPoint(owner, pathID, numRetries + 1, callerPos, radius, synced); break; } } else { waypoint = maxResPath.path.back(); maxResPath.path.pop_back(); } } while ((callerPos.SqDistance2D(waypoint) < Square(radius)) && (waypoint != maxResPath.pathGoal)); // y=0 indicates this is not a temporary waypoint // (the default PFS does not queue path-requests) return (waypoint * XZVector); }
/* Request a new multipath, store the result and return a handle-id to it. */ unsigned int CPathManager::RequestPath( const MoveData* md, const float3& startPos, const float3& goalPos, CPathFinderDef* pfDef, CSolidObject* caller, bool synced ) { SCOPED_TIMER("PFS"); MoveData* moveData = moveinfo->moveData[md->pathType]; moveData->tempOwner = caller; // Creates a new multipath. MultiPath* newPath = new MultiPath(startPos, pfDef, moveData); newPath->finalGoal = goalPos; newPath->caller = caller; if (caller) { caller->UnBlock(); } const int ownerId = caller? caller->id: 0; unsigned int retValue = 0; // choose the PF or the PE depending on the goal-distance const float distanceToGoal = pfDef->Heuristic(int(startPos.x / SQUARE_SIZE), int(startPos.z / SQUARE_SIZE)); if (distanceToGoal < DETAILED_DISTANCE) { // Get a detailed path. IPath::SearchResult result = maxResPF->GetPath(*moveData, startPos, *pfDef, newPath->maxResPath, true, false, MAX_SEARCHED_NODES_PF, true, ownerId, synced); newPath->searchResult = result; if (result == IPath::Ok || result == IPath::GoalOutOfRange) { retValue = Store(newPath); } else { delete newPath; } } else if (distanceToGoal < ESTIMATE_DISTANCE) { // Get an estimate path. IPath::SearchResult result = medResPE->GetPath(*moveData, startPos, *pfDef, newPath->medResPath, MAX_SEARCHED_NODES_PE, synced); newPath->searchResult = result; if (result == IPath::Ok || result == IPath::GoalOutOfRange) { // Turn a part of it into detailed path. MedRes2MaxRes(*newPath, startPos, ownerId, synced); // Store the path. retValue = Store(newPath); } else { // if we fail see if it can work find a better block to start from float3 sp = medResPE->FindBestBlockCenter(moveData, startPos, synced); if (sp.x != 0 && (((int) sp.x) / (SQUARE_SIZE * 8) != ((int) startPos.x) / (SQUARE_SIZE * 8) || ((int) sp.z) / (SQUARE_SIZE * 8) != ((int) startPos.z) / (SQUARE_SIZE * 8))) { IPath::SearchResult result = medResPE->GetPath(*moveData, sp, *pfDef, newPath->medResPath, MAX_SEARCHED_NODES_PE, synced); newPath->searchResult = result; if (result == IPath::Ok || result == IPath::GoalOutOfRange) { MedRes2MaxRes(*newPath, startPos, ownerId, synced); retValue = Store(newPath); } else { delete newPath; } } else { delete newPath; } } } else { // Get a low-res. estimate path. IPath::SearchResult result = lowResPE->GetPath(*moveData, startPos, *pfDef, newPath->lowResPath, MAX_SEARCHED_NODES_PE, synced); newPath->searchResult = result; if (result == IPath::Ok || result == IPath::GoalOutOfRange) { // Turn a part of it into hi-res. estimate path. LowRes2MedRes(*newPath, startPos, ownerId, synced); // And estimate into detailed. MedRes2MaxRes(*newPath, startPos, ownerId, synced); // Store the path. retValue = Store(newPath); } else { // sometimes the 32*32 squares can be wrong (admissibility...) so if it fails to get a path also try with 8*8 squares IPath::SearchResult result = medResPE->GetPath(*moveData, startPos, *pfDef, newPath->medResPath, MAX_SEARCHED_NODES_PE, synced); newPath->searchResult = result; if (result == IPath::Ok || result == IPath::GoalOutOfRange) { MedRes2MaxRes(*newPath, startPos, ownerId, synced); retValue = Store(newPath); } else { // 8*8 can also fail rarely, so see if we can find a better 8*8 to start from float3 sp = medResPE->FindBestBlockCenter(moveData, startPos, synced); if (sp.x != 0 && (((int) sp.x) / (SQUARE_SIZE * 8) != ((int) startPos.x) / (SQUARE_SIZE * 8) || ((int) sp.z) / (SQUARE_SIZE * 8) != ((int) startPos.z) / (SQUARE_SIZE * 8))) { IPath::SearchResult result = medResPE->GetPath(*moveData, sp, *pfDef, newPath->medResPath, MAX_SEARCHED_NODES_PE, synced); if (result == IPath::Ok || result == IPath::GoalOutOfRange) { MedRes2MaxRes(*newPath, startPos, ownerId, synced); retValue = Store(newPath); } else { delete newPath; } } else { delete newPath; } } } } if (caller) { caller->Block(); } moveData->tempOwner = NULL; return retValue; }
/* Removes and return the next waypoint in the multipath corresponding to given id. */ float3 CPathManager::NextWaypoint( unsigned int pathId, float3 callerPos, float minDistance, int numRetries, int ownerId, bool synced ) const { SCOPED_TIMER("PFS"); // 0 indicates a no-path id if (pathId == 0) return float3(-1.0f, -1.0f, -1.0f); if (numRetries > 4) return float3(-1.0f, -1.0f, -1.0f); // Find corresponding multipath. const std::map<unsigned int, MultiPath*>::const_iterator pi = pathMap.find(pathId); if (pi == pathMap.end()) return float3(-1.0f, -1.0f, -1.0f); MultiPath* multiPath = pi->second; if (callerPos == ZeroVector) { if (!multiPath->maxResPath.path.empty()) callerPos = multiPath->maxResPath.path.back(); } // check if detailed path needs bettering if (!multiPath->medResPath.path.empty() && (multiPath->medResPath.path.back().SqDistance2D(callerPos) < Square(MIN_DETAILED_DISTANCE * SQUARE_SIZE) || multiPath->maxResPath.path.size() <= 2)) { if (!multiPath->lowResPath.path.empty() && // if so, check if estimated path also needs bettering (multiPath->lowResPath.path.back().SqDistance2D(callerPos) < Square(MIN_ESTIMATE_DISTANCE * SQUARE_SIZE) || multiPath->medResPath.path.size() <= 2)) { LowRes2MedRes(*multiPath, callerPos, ownerId, synced); } if (multiPath->caller) { multiPath->caller->UnBlock(); } MedRes2MaxRes(*multiPath, callerPos, ownerId, synced); if (multiPath->caller) { multiPath->caller->Block(); } } float3 waypoint; do { // get the next waypoint from the high-res path // // if this is not possible, then either we are // at the goal OR the path could not reach all // the way to it (ie. a GoalOutOfRange result) if (multiPath->maxResPath.path.empty()) { if (multiPath->lowResPath.path.empty() && multiPath->medResPath.path.empty()) { if (multiPath->searchResult == IPath::Ok) { return multiPath->finalGoal; } else { return float3(-1.0f, -1.0f, -1.0f); } } else { return NextWaypoint(pathId, callerPos, minDistance, numRetries + 1, ownerId, synced); } } else { waypoint = multiPath->maxResPath.path.back(); multiPath->maxResPath.path.pop_back(); } } while (callerPos.SqDistance2D(waypoint) < Square(minDistance) && waypoint != multiPath->maxResPath.pathGoal); return waypoint; }