The original design had a Brain class, that contained a single current BrainState, which could be one of Waiting, Searching, Pathing, Wander, RePathing, Walking, Shooting, Hiding, etc.
This breaks down when you want the AI to be in more than one state at once, or remember what it was doing before, say Pathing. This was done in an ad-hoc manner of remembering a 'DestinationState' which was the state to do next. Part of the issue was that the DestinationState was what to do next if things went well. If you lost your target, couldn't make progress on your path, etc, it wasn't always clear what to do next.
I experimented with the idea of going for BehaviorTrees, which I have been using at work, but didn't want to implement a completely new system, and wanted the existing levels & save-games to work with any changes.
So, the current plan, which I have coded up, and am about to begin testing, is to have three concurrent states in the Brain class.
One Main State, which would be the AI's overall positioning & behavior strategy, like Flee, Wander, Follow, Patrol, None, etc.
One CombatState, which can be Shooting, Hiding ( for reloading, etc. ), Charging, None
One PathingState, which can be Pathing, RePathing, Searching or None.
The desired position-choosing logic used to be clustered in the Searching state, which used code like
if ( mDestinationState == "Charging" ) // choose a spot near the target
to choose a place with a good Line Of Fire, or a poor one ( for cover ), or near or far from the target, etc.
The new code uses virtual functions on the Brain states to get things like desired location bounding boxes, location evaluations, movement speeds, etc.
The Main State provides the bounding box of where to even look for navnode spots to go to. The Brain then chooses spots randomly inside the box. The Main State then gets to veto or evaluate these spots, then, if not vetoed, the Combat state can then chime in and veto or evaluate the remaining spots. Then the spot is selected randomly based on its desirability.
This way, the friendly NPCs can Follow the player, but still find Shooting and Hiding spots to attack enemy NPCs.
It feels great to rip out and simplify a bunch of code, but I'll let you know how it works out in game...