Hello everyone,
I'm working on a tactical, turn based dungeon crawler and want to start to implement enemy AI. I have opted for FSMs as they seem a straightforward and intuitive framework. The basics of FSMs are clear. I checked a couple of books on AI design, but while informative, they always consider rather simple systems / proof of concepts. I have a somewhat bigger system in mind, so I wonder about the right level of abstraction for a bigger FSM system.
There are a couple of different enemy types I have in mind, for example: a slime, a spider, a bat and a skeleton, and I want them to be varied in behaviour. They will all use a number of similar states, such as Sleep, MoveTo, Flee, Fight, etc. so this seems a good opportunity to write a generic sleep class that can be reused for all of them. All of them wake up when damaged, so that behaviour is shared, but I also want the transition out of this state to be enemy dependent: a slime also wakes up when it smells blood, a spider when something disturbs its web, bats are sensitive to sound, and skeleton senses close enemies even when sleeping.
I can solve this problem by using a transition table that is enemy specific instead of including the transitions in the state itself. This solves the problem for the Sleep state, since there is no logic needed there (the enemy doesn't do anything in this state).
But what about more complicated states? For example: selecting a point of interest and moving to it. This would again be enemy dependent. The different units are interested in different things, and also their movement might be different. Lets say spiders and bats are skittish, and will try to avoid hostile units, while slimes and skeletons do not care. So while initially it seems like they all share the MoveTo state, they rely on different logic. Do I work these differences in logic into a reusable MoveTo state? Do I make (at least) two variants of the MoveTo state (one that avoids other units one that does not)?
This leads me to the question: to what level should I try to write reusable code that can be shared? The on extreme is to write a complete set of states for every enemy type, e.i. SlimeSleep, BatSleep, etc. which will contain all the relevant code. However, this will entail frequently repeated code. Furthermore, if I happen to change something regarding the game's logic. I will have to change these repeating code snippets everywhere in parallel, which seems prone to bugs.
On the other hand, if I want to reuse a few State classes for all enemies, I will have to separate from the State, both the State Transitions and the State Logic, which begs the question; what is left of the state, except for a name?
So, what is a good way to slot reusable bits of logic and transitions into the states? Thoughts?