one thing i have noticed about the A* approach (and probably most others) is the exponential increase in calculations over greater distance (due to the exponential increase in path options, most of which are redundant and unnecessary)
The complexity is about min((k/2)^N ,Nmax) which means node explosion . A* is also fooled by dead ends . there are a few examples for this
there are two suggestions i have. one would help a lot for pathing in a fortress. by breaking the fortress down into rooms separated by doors, you could simplify the code by using a macroscopic version to calculate what rooms you need to go to, and a microscopic one for pathing through each room. even if it needs to preform the same amount of pathing checks it won't have to do them all at once, only once every time the object enters the room, spreading the lag more evenly. this also means numerous shorter distances instead of one long one minimizing the exponential growth in possibilities ( i know it is better then exponential but the algorithm hates distance. you could even use memory to store the path from one end of the room to the other (though this would have to be compiled and called on when needed because RAM is taxed as it is (i think))
I think that's around page 15-17 or so of this thread.
I did implement an arbitrary room generation with patch caching between them . you'll see it in the picture above your post(it's spoilered to save space). the cyan on gray Rs are the rooms and the path between them is shown in light gray. The path itself is cyan background floors.
here is a condensed version of my code with dest-path part choped (same as spath after reseting variable) but it's still pretty big though what it does is simple.
function findPathInMazeWaypointRooms()
{
/*
.
.
declarations
.
.
*/
var testpoints=[[-1,-1],[1,1],[-1,1],[1,-1],[0,1],[-1,0],[0,-1],[1,0],[0,1]];
var dist=8;
var len_temp;
var candidates=[];
xs=gbl_start[0];ys=(gbl_start[1]);xd=gbl_dest[0];yd=(gbl_dest[1]);
wprxs=Math.min(Math.floor((xs*gbl_wp_rooms[0].length)/32),gbl_wp_rooms[0].length-1);
wprys=Math.min(Math.floor((ys*gbl_wp_rooms.length)/32),gbl_wp_rooms.length-1);
wprxd=Math.min(Math.floor((xd*gbl_wp_rooms[0].length)/32),gbl_wp_rooms[0].length-1);
wpryd=Math.min(Math.floor((yd*gbl_wp_rooms.length)/32),gbl_wp_rooms.length-1);
x=gbl_wp_rooms[wprys][wprxs].x;
y=gbl_wp_rooms[wprys][wprxs].y;
spath=findPathInMazeGenericLimited(xs,ys,x,y,dist*2);
if(spath.length==0)
{
spath=findPathInMazeGenericLimited(xs,ys,x,y,dist*4);
}
if(spath.length==0)
{
dist*=2;
for(i=0;i<testpoints.length;++i)
{
xx=wprxs+testpoints[i][0];
yy=wprys+testpoints[i][1];
if(xx<0||yy<0||xx>=gbl_wp_rooms[0].length||yy>=gbl_wp_rooms.length)
continue;
len_temp=gbl_wp_rooms[wprys][wprxs].dest.length;
for(j=0;j<len_temp;++j)
{
if( gbl_wp_rooms[wprys][wprxs].dest[j][0]==xx && gbl_wp_rooms[wprys][wprxs].dest[j][1]==yy )
break;
}
if(j==len_temp)
candidates.push([xx,yy]);
}
for(i=0;i<candidates.length;++i)
{
xx=candidates[i][0];yy=candidates[i][1];x=gbl_wp_rooms[yy][xx].x; y=gbl_wp_rooms[yy][xx].y;
spath=findPathInMazeGenericLimited(xs,ys,x,y,dist);
ntracker+=gbl_a_star_last_n;
if(spath.length!=0)
break;
}
start_fail+=i;
if(i==candidates.length)
{
gbl_path=[];
return ;
}
wprxs=xx;wprys=yy;
}
/* same as above for dpath
.
.
.
*/
wprpath=findPathInMazeGenericLimitedBoardType(gbl_wp_rooms,wprxs,wprys,wprxd,wpryd,-1,2);
if(wprpath.length>0)
{
path=spath.slice(0);
var wprp;
for(i=1;i<wprpath.length;++i)
{
wprp=gbl_wp_rooms[wprpath[i-1][1]][wprpath[i-1][0]];
for(j=0;j<wprp.dest.length;++j)
{
if((wprp.dest[j][1]==wprpath[i][1]) &&(wprp.dest[j][0]==wprpath[i][0]) )
break;
}
path.length-=1;
path=path.concat(wprp.paths[j]);
}
path.length-=1;
path=path.concat(dpath);
}
else path=[];
gbl_path=path;
updateMazes();
}
first thing it tries to find a path to the room closest to it but it limits its search to nodes 16 or less away. then if it fails it tries again with longer distance . (the complexity of the second search is much larger than the first). if it still fails it assumes that it's by a wall and the best candidate rooms are the ones not connected to it's closest room. if it fails to find a path to those rooms it gives up.
after it's done with path (xs,ys)->R(wprxs,wprys) it will then find the path R(wprxd,wpryd) -> (xd,yd) the same way (this part is choped). then it look for a path from start room to end room on the room "maze" which is a 4X4 size (atm) and if it doesn't find a path - it returns empty path. otherwise it goes and concatenate spath to all the subpaths of the room path (not necessary to save path - short distance A* is usually pretty fast).
As I said before the begining of the function that got to do with room selection needs a lot of work because waypoint based rooms are just terrible in terms of finding "where you are" . The last part is pretty simple once you find the connections.
Edit1:
After playing with the code a little I added a BFS collider to find edges between rooms - sure it goes through every node in the maze but it finds all the connections between rooms and their shape in one pass . All I need to do now is to add the info to room data structure , then I'll need to create a better navigation function then a room update scheme ... lots of work

. I still need to fix the bfs limits scheme so it know when to update the depth counter correctly .
here is how it looks like - it puts the output at the bottom whenever updating rooms: