void fpathShutdown() { // Signal the path finding thread to quit fpathQuit = true; wzSemaphorePost(fpathSemaphore); // Wake up thread. if (fpathThread) { wzThreadJoin(fpathThread); fpathThread = NULL; wzMutexDestroy(fpathMutex); fpathMutex = NULL; wzSemaphoreDestroy(fpathSemaphore); fpathSemaphore = NULL; wzSemaphoreDestroy(waitingForResultSemaphore); waitingForResultSemaphore = NULL; } fpathHardTableReset(); }
ASR_RETVAL fpathAStarRoute(MOVE_CONTROL *psMove, PATHJOB *psJob) { ASR_RETVAL retval = ASR_OK; bool mustReverse = true; const PathCoord tileOrig(map_coord(psJob->origX), map_coord(psJob->origY)); const PathCoord tileDest(map_coord(psJob->destX), map_coord(psJob->destY)); PathCoord endCoord; // Either nearest coord (mustReverse = true) or orig (mustReverse = false). std::list<PathfindContext>::iterator contextIterator = fpathContexts.begin(); for (contextIterator = fpathContexts.begin(); contextIterator != fpathContexts.end(); ++contextIterator) { if (!contextIterator->matches(psJob->blockingMap, tileDest)) { // This context is not for the same droid type and same destination. continue; } // We have tried going to tileDest before. if (contextIterator->map[tileOrig.x + tileOrig.y*mapWidth].iteration == contextIterator->iteration && contextIterator->map[tileOrig.x + tileOrig.y*mapWidth].visited) { // Already know the path from orig to dest. endCoord = tileOrig; } else { // Need to find the path from orig to dest, continue previous exploration. fpathAStarReestimate(*contextIterator, tileOrig); endCoord = fpathAStarExplore(*contextIterator, tileOrig); } if (endCoord != tileOrig) { // orig turned out to be on a different island than what this context was used for, so can't use this context data after all. continue; } mustReverse = false; // We have the path from the nearest reachable tile to dest, to orig. break; // Found the path! Don't search more contexts. } if (contextIterator == fpathContexts.end()) { // We did not find an appropriate context. Make one. if (fpathContexts.size() < 30) { fpathContexts.push_back(PathfindContext()); } --contextIterator; // Init a new context, overwriting the oldest one if we are caching too many. // We will be searching from orig to dest, since we don't know where the nearest reachable tile to dest is. fpathInitContext(*contextIterator, psJob->blockingMap, tileOrig, tileOrig, tileDest); endCoord = fpathAStarExplore(*contextIterator, tileDest); contextIterator->nearestCoord = endCoord; } PathfindContext &context = *contextIterator; // return the nearest route if no actual route was found if (context.nearestCoord != tileDest) { retval = ASR_NEAREST; } // Get route, in reverse order. static std::vector<Vector2i> path; // Declared static to save allocations. path.clear(); PathCoord newP; for (PathCoord p = endCoord; p != context.tileS; p = newP) { ASSERT_OR_RETURN(ASR_FAILED, tileOnMap(p.x, p.y), "Assigned XY coordinates (%d, %d) not on map!", (int)p.x, (int)p.y); ASSERT_OR_RETURN(ASR_FAILED, path.size() < (unsigned)mapWidth*mapHeight, "Pathfinding got in a loop."); path.push_back(Vector2i(world_coord(p.x) + TILE_UNITS / 2, world_coord(p.y) + TILE_UNITS / 2)); PathExploredTile &tile = context.map[p.x + p.y*mapWidth]; newP = PathCoord(p.x - tile.dx, p.y - tile.dy); if (p == newP) { break; // We stopped moving, because we reached the closest reachable tile to context.tileS. Give up now. } } if (path.empty()) { // We are probably already in the destination tile. Go to the exact coordinates. path.push_back(Vector2i(psJob->destX, psJob->destY)); } else if (retval == ASR_OK) { // Found exact path, so use exact coordinates for last point, no reason to lose precision Vector2i v(psJob->destX, psJob->destY); if (mustReverse) { path.front() = v; } else { path.back() = v; } } // TODO FIXME once we can change numPoints to something larger than uint16_t psMove->numPoints = std::min<int>(UINT16_MAX, path.size()); // Allocate memory psMove->asPath = static_cast<Vector2i *>(malloc(sizeof(*psMove->asPath) * path.size())); ASSERT(psMove->asPath, "Out of memory"); if (!psMove->asPath) { fpathHardTableReset(); return ASR_FAILED; } // get the route in the correct order // If as I suspect this is to reverse the list, then it's my suspicion that // we could route from destination to source as opposed to source to // destination. We could then save the reversal. to risky to try now...Alex M // // The idea is impractical, because you can't guarentee that the target is // reachable. As I see it, this is the reason why psNearest got introduced. // -- Dennis L. // // If many droids are heading towards the same destination, then destination // to source would be faster if reusing the information in nodeArray. --Cyp if (mustReverse) { // Copy the list, in reverse. std::copy(path.rbegin(), path.rend(), psMove->asPath); if (!context.isBlocked(tileOrig.x, tileOrig.y)) // If blocked, searching from tileDest to tileOrig wouldn't find the tileOrig tile. { // Next time, search starting from nearest reachable tile to the destination. fpathInitContext(context, psJob->blockingMap, tileDest, context.nearestCoord, tileOrig); } } else { // Copy the list. std::copy(path.begin(), path.end(), psMove->asPath); } // Move context to beginning of last recently used list. if (contextIterator != fpathContexts.begin()) // Not sure whether or not the splice is a safe noop, if equal. { fpathContexts.splice(fpathContexts.begin(), fpathContexts, contextIterator); } psMove->destination = psMove->asPath[path.size() - 1]; return retval; }