AI behaviours: Routines and QA Bot Basics
More work these weeks on AI. The AI approach is currently an ordered sequence of high-level behaviours, like survival, combat, etc. The behaviours can either be composites themselves, or leaf nodes that implement some logic. These nodes either return false (couldn't do anything) or they schedule a parameterised action (e.g. "(move_adjacent, tgt_position)") and return true.
One of these high-level behaviours are "routines". Routine is a low-priority behaviour that can vary from creature to creature. E.g. a predator's routine is hunting, a guard's routine is patrolling, etc. An additional challenge is to implement such behaviours in a procedural environment - patrolling what exactly was one of those pains, and temptation to overengineer further is strong.
The patrol routine basically selects 2 nearby points (along a line, passing through creature's starting position) and the creature goes to each, stays for a bit and leaves. To add a little bit of flair, while the creature is patrolling a point, it changes its animation to "looking around", to communicate visually what's going on a little bit more. Moving between the two patrol points is done with steering to avoid using A* for simple movement like that. But a creature kites the patrolling entity to some further point, then steering might not cut it, and A* is used. The result is that most of the time, behaviour should be cheap. As an additional bit of context, the AI refactoring work started because of performance - too much unnecessary pathfinding mostly.
The hunt routine works with a timer, currently set every at 6 hours. Every 6 hours the creature gets hungry and looks for a potential alive snack in its vicinity. While predators are typically neutral with "critters" (bunnies, owls, etc), when hungry they find the closest one, change relationship to enemy, and start chasing it. After they kill it, they're relaxed once more until the hunger clock strikes again. It's simple, but the intent is for the player to be able to see the behaviour. I'll probably need to tweak hunger clock period and critter spawn rate in the future. More routines to come eventually, but for now these two should suffice.
Another high-level behaviour was the essential Go to, using A* for pathfinding. I described above that there's a sequence of high-level behaviours. In addition to standard ones, there are two extra initially-empty composites: "dynamic" and "high-priority dynamic". The high-priority stuff gets executed before combat, and the others after combat. This means we can hook "GoTo" to the high-priority dynamic list, and the creature will try to go there while ignoring enemies.
Another cool thing with easy-to-override behaviours is to hook all this machinery to the player as well, and in the far future, to hero NPCs. What scripted behaviour would we want the player to use? Quite a few actually:
Auto-explore is the usual divisive one, so that you could press a button and your character can go around and try to explore the entirety of the level. As a player-behaviour, we need some sort of exit mechanism to resume control. This can be a key of course, but can also be failure to take an action. Auto-explore ends when there's nothing more to explore, so it fails to schedule a simple movement. We can tweak this so that if also fails when traps or enemies are spotted.
Gather loot is another one, probably usable by some greedy creatures occasionally. Basically we look at all items on ground or containers we know about, and we go get their contents. Filters can be set to ignore ground loot or chests, or even set a condition on what items we're interested in, e.g. a particular item or items with a given value range. And now the cool thing is that we can set a composite behaviour for the player to be "gather loot, auto-explore", so that if there's loot we'll grab it, otherwise we keep exploring. The beginnings of a QA bot basically: an AI agent that plays like a player, for bug detection.
Approach entity is another one, currently used for going to level exits. We can specify a condition (must be stairs, or world exit) and a max distance (must be on it), and every turn we look for these exits and if none are found or we're sitting on one, then "fail". Fail in this case means "no action was scheduled". Maybe in the future the return type might benefit from having one more state, but it will complicate things so not doing that yet. Why is this useful? When you're done with a level, it would be nice to schedule an action "go back up" really quickly to not waste time. I don't like "magic portals to exits" (ala Dungeonmans), designed quick exits (ala Skyrim) are too much work, so the ToME approach is quite nice here.
Things to do at the moment: contemplate villager routines and take some notes for potential implementation (probably requires quite a lot of village-related work that I don't plan to do soon), set high-priority targets (allies or threats) and try to make an escort quest work with the new AI, and we're done with this long tangent.
... Finally, talking about tangents, I recently got Age of Empires II: DE + Return to Rome and I'm having an absolute blast (from the past, since I was playing it back then too). So, a bit reduced development velocity can totally be attributed to immense joy of playing AoE. Have a nice weekend!
