// Check that we are still close to the start of the A* path, // and the end of the path is close to our goal static int AStarCloseToPath( AIGotoContext *c, Vec2i currentTile, Vec2i goalTile) { Vec2i *pathTile; Vec2i *pathEnd; if (!c || c->PathIndex >= (int)ASPathGetCount(c->Path) - 1) // at end of path { return 0; } // Check if we're too far from the current start of the path pathTile = ASPathGetNode(c->Path, c->PathIndex); if (CHEBYSHEV_DISTANCE( currentTile.x, currentTile.y, pathTile->x, pathTile->y) > 2) { return 0; } // Check if we're too far from the end of the path pathEnd = ASPathGetNode(c->Path, ASPathGetCount(c->Path) - 1); if (CHEBYSHEV_DISTANCE( goalTile.x, goalTile.y, pathEnd->x, pathEnd->y) > 0) { return 0; } return 1; }
int AIGoto(TActor *actor, Vec2i p, bool ignoreObjects) { Vec2i a = Vec2iFull2Real(actor->Pos); Vec2i currentTile = Vec2iToTile(a); Vec2i goalTile = Vec2iToTile(p); AIGotoContext *c = &actor->aiContext->Goto; // If we are already there, bail // This can happen if AI is trying to track the player, // but the player has died, for example. if (Vec2iEqual(currentTile, goalTile)) { return 0; } // If we are currently following an A* path, // and it is still valid, keep following it until // we have reached a new tile if (c && c->IsFollowing && AStarCloseToPath(c, currentTile, goalTile)) { return AStarFollow(c, currentTile, &actor->tileItem, a); } else if (AIHasClearPath(a, p, ignoreObjects)) { // Simple case: if there's a clear line between AI and target, // walk straight towards it return AIGotoDirect(a, p); } else { // We need to recalculate A* AStarContext ac; ac.Map = &gMap; ac.IsTileOk = ignoreObjects ? IsTileWalkable : IsTileWalkableAroundObjects; // First, if the goal tile is blocked itself, // find a nearby tile that can be walked to c->Goal = MapSearchTileAround(ac.Map, goalTile, ac.IsTileOk); c->PathIndex = 1; // start navigating to the next path node ASPathDestroy(c->Path); c->Path = ASPathCreate( &cPathNodeSource, &ac, ¤tTile, &c->Goal); // In case we can't calculate A* for some reason, // try simple navigation again if (ASPathGetCount(c->Path) <= 1) { debug( D_MAX, "Error: can't calculate path from {%d, %d} to {%d, %d}", currentTile.x, currentTile.y, goalTile.x, goalTile.y); return AIGotoDirect(a, p); } return AStarFollow(c, currentTile, &actor->tileItem, a); } }
// Use pathfinding to check that there is a path between // source and destination tiles bool AIHasPath(const Vec2i from, const Vec2i to, const bool ignoreObjects) { // Quick first test: check there is a clear path if (AIHasClearPath(from, to, ignoreObjects)) { return true; } // Pathfind AStarContext ac; ac.Map = &gMap; ac.IsTileOk = ignoreObjects ? IsTileWalkable : IsTileWalkableAroundObjects; Vec2i fromTile = Vec2iToTile(from); Vec2i toTile = MapSearchTileAround(ac.Map, Vec2iToTile(to), ac.IsTileOk); ASPath path = ASPathCreate(&cPathNodeSource, &ac, &fromTile, &toTile); size_t pathCount = ASPathGetCount(path); ASPathDestroy(path); return pathCount > 1; }
int getEdgeCount(ASPath path) { return ASPathGetCount(path) - 1; }