The difficulty with using Dijkstra is that it explores all possible path nodes (multiple times), which isn’t particularly performant in all cases. The use of A* merely avoids visiting overly expensive routes (with the appropriate heuristic).
Planning shouldn’t happen that frequently, so theoretically the performance difficulties are found in calculation spikes, rather than framerate-drops. You could look at something like a thread-based planner, because the in/out data can be kept quite simple (though procedural conditions may require some more thought).
I did find that reading the papers wasn’t enough to get a working planner. The A* planner used essentially works backwards from the goal, keeping track of how each action affects the state, until the state of the node equals its goal state. The neighbours for each node are just the actions that have effects that may/may not produce useful results. The heuristic used is the number of unsatisfied state symbols between the goal and current state of a node. There are some peculiarities here, such as the way we check that the path is complete. The main reason for this that we don’t compare goal and current states is because a) we allow preconditions to involve symbols that are also in the action effects dictionary, which isn’t handled, and we don’t check if neighbours of actions actually solve the state, or if they instead diverge.
A* heuristics are different for each task, and the algorithm must be careful to support them. (Or, rather, some people use simplified algorithms if the heuristic is guaranteed not to over-estimate the distance to the goal (e.g the Wikipedia example)).
The Actions used in the planner should avoid running expensive procedural checks, and defer most of the work to subsystems via something like the blackboard. I’m experimenting with a sensory system to allow for the planner to make decisions based upon potentially flawed information (like blind zombies, guessing where the threat is positioned), rather than doing this lookup inside the action explicitly, but it is all a little ill-defined and up-to-you in this field.
Jeff’s articles suggest using a sensory system to run most expensive checks regularly, to avoid latency spikes (instead incurring a regular, predictable cost). You can see what I’m doing with this here https://github.com/agoose77/PyAuthServer/tree/master/game_system/ai
My implementation will be slower than the one used in F.E.A.R, not simply because it’s Python - I allow for different variable types to be used, whilst in the FEAR planner, boolean bitfields are used (which can be optimised for speed). C++ would be faster, if you can allow for this trade-off. Of course, once you support procedural preconditions / effects, it makes sense that most of theg ame allows a C++ API.