Using finite state machines for behaviour
I use finite state machines for the behaviour of townsfolk and enemies in my game.
What does that mean?
At any given time an NPC can be in one of many “behaviour states” depending on what's happening to them.
For example, an enemy might contain states for “patrolling” and “chasing”.
Breaking up behaviour makes it simpler to think about. When “patrolling”, the enemy just follows a path back and forward until they see the player. Then they change state to “chasing” where they move towards the player. If they lose sight of the player they go back to “patrolling” and return to their path.
Breaking up behaviour has another benefit - you can share them with other types of NPC.
A friendly person in a town might also make use of the patrolling state.
There's no real limit to how many states you can have or how much you share them but there are two rules that I've found have helped keep things manageable:
States define their dependencies
Anything external information that the state needs should be explicitly defined. In the case of Godot this means declaring export variables that can be populated by the owner node.
States don't transition to other states themselves
Each state should have no idea that other states exist. That's the best way to keep them simple and shareable.
Instead of transitioning itself, a state should simply provide a list of events that can happen while in that state.
In the example from earlier, the patrolling state might signal that it has seen the player. From there the NPC that owns the state machine can decide whether it needs to transition or not.
If you're interested in using a finite state machine for your game then have a look at my videos on enemy behaviour and smarter NPCs and have a look at GDQuest's guide to finite state machines.