I imagine that one of the biggest problems with pathfinding is pathing over stairs in multi-level fortresses.
Snipping lots to just keep to the relevent bits for my reply.
as long as the goal is more distant in the horizontal than in the vertical from the stairs. Basically, the algorithm would then not follow the stairs down to the correct level before walking off, but instead on each level leave the stairs because it decreases the distance to the goal more than going down.
This is based on a common misunderstanding of A* that it is based on distance, it isn't, A* is based on
cost. You're perfectly free to adjust the cost of movement in any direction, this means that you can tweak matters so that cost of horizontal movement is higher (how much higher is a matter of "suck it and see") than that of vertical, that means that staircases become "sticky", once you path onto them you'll prioritize them as a prefered search path.
This is only a partial solution although it does address the problem of backtracking across intermediate levels when you need to go up/down further and can do on the current stair.
Adjusting the movement cost is probably how the traffic areas are implemented, although as a professional coder I'm hesitent to guess how
any bit of code is implemented with only a view from the outside.
But basically, my main point is that A* prioritizes the next node to search down based on cost to get there so far and estimated cost to get from there to the destination, and there's lots of ways you can tweak those costing formula that isn't
just linear distance, and for most implementations you probably
should tweak them, and spend more time doing that than writing your A* search in the first place.
Hence, it might be worthwhile to treat stairs/ramps explicitly, in particularly maintaining a list of them, and this should be worthwhile doing as long as the number of stairs/ramps is small compared to the number of total tiles.
This rapidly becomes problematic if you have unconnected areas on the same level, each with their own stairs to levels that may or may not be connected - you cannot assume that you can path entirely within a single level even if start and destination are on the same level.
Also, even if you can, if you have for example a large U shape and need to path from one end to the other, you will traverse the length of the U even if there is a shorter path linking the two directly down some stairs, across, then back up.
In practice, you can't do this sort of hierarchical pathfinding without a lot of oddities unless you go with a full-blown implementation where you're working on generic clusters of tiles with bottlenecks between them and accepting the fact that estimating cost of traversal of a cluster can be problematic enough that you're going to get noticably odder results than A* produces on its own, and you're going to be making your implementation significantly more complicated and potentially be trading off more (in a dynamic environment) keeping your list of clusters up to date than just pathing the 'simple' way.
(or potentially cache these paths).
This can be a big win, especially if you cache on destination and then check for partial matches within your A*, ie I've pathed this far and I've reached somewhere that I've already pathed to the destination I want already.
Problem with caching is selectively invalidating routes based on dynamic events, but you can usually get away with just traversing the returned path and checking for accessibility at each step of the route, and invalidating then.