bool TCOD_path_compute(TCOD_path_t p, int ox,int oy, int dx, int dy) { TCOD_path_data_t *path=(TCOD_path_data_t *)p; TCOD_IFNOT(p != NULL) return false; path->ox=ox; path->oy=oy; path->dx=dx; path->dy=dy; TCOD_list_clear(path->path); TCOD_list_clear(path->heap); if ( ox == dx && oy == dy ) return true; /* trivial case */ /* check that origin and destination are inside the map */ TCOD_IFNOT((unsigned)ox < (unsigned)path->w && (unsigned)oy < (unsigned)path->h) return false; TCOD_IFNOT((unsigned)dx < (unsigned)path->w && (unsigned)dy < (unsigned)path->h) return false; /* initialize djikstra grids */ memset(path->grid,0,sizeof(float)*path->w*path->h); memset(path->prev,NONE,sizeof(dir_t)*path->w*path->h); path->heur[ ox + oy * path->w ] = 1.0f; /* anything != 0 */ TCOD_path_push_cell(path,ox,oy); /* put the origin cell as a bootstrap */ /* fill the djikstra grid until we reach dx,dy */ TCOD_path_set_cells(path); if ( path->grid[dx + dy * path->w] == 0 ) return false; /* no path found */ /* there is a path. retrieve it */ do { /* walk from destination to origin, using the 'prev' array */ int step=path->prev[ dx + dy * path->w ]; TCOD_list_push(path->path,(void *)(uintptr)step); dx -= dirx[step]; dy -= diry[step]; } while ( dx != ox || dy != oy ); return true; }
/* fill the grid, starting from the origin until we reach the destination */ static void TCOD_path_set_cells(TCOD_path_data_t *path, int cutoff) { int stepstaken = 0; while ( path->grid[path->dx + path->dy * path->w ] == 0 && ! TCOD_list_is_empty(path->heap) ) { stepstaken++; if (stepstaken > cutoff) break; int x,y,i,imax; float distance; TCOD_path_get_cell(path,&x,&y,&distance); imax= ( path->diagonalCost == 0.0f ? 4 : 8) ; for (i=0; i < imax; i++ ) { /* convert i to dx,dy */ static int idirx[]={0,-1,1,0,-1,1,-1,1}; static int idiry[]={-1,0,0,1,-1,-1,1,1}; /* convert i to direction */ static dir_t prevdirs[] = { NORTH, WEST, EAST, SOUTH, NORTH_WEST, NORTH_EAST,SOUTH_WEST,SOUTH_EAST }; /* coordinate of the adjacent cell */ int cx=x+idirx[i]; int cy=y+idiry[i]; if ( cx >= 0 && cy >= 0 && cx < path->w && cy < path->h ) { float walk_cost = TCOD_path_walk_cost(path,x,y,cx,cy); if ( walk_cost > 0.0f ) { /* in of the map and walkable */ float covered=distance + walk_cost * (i>=4 ? path->diagonalCost : 1.0f); float previousCovered = path->grid[cx + cy * path->w ]; if ( previousCovered == 0 ) { /* put a new cell in the heap */ int offset=cx + cy * path->w; /* A* heuristic : remaining distance */ float remaining=(float)sqrt((cx-path->dx)*(cx-path->dx)+(cy-path->dy)*(cy-path->dy)); path->grid[ offset ] = covered; path->heur[ offset ] = covered + remaining; path->prev[ offset ] = prevdirs[i]; TCOD_path_push_cell(path,cx,cy); } else if ( previousCovered > covered ) { /* we found a better path to a cell already in the heap */ int offset=cx + cy * path->w; path->grid[ offset ] = covered; path->heur[ offset ] -= (previousCovered - covered); /* fix the A* score */ path->prev[ offset ] = prevdirs[i]; /* reorder the heap */ heap_reorder(path,offset); } } } } } }