In the past week or so, I had to jump back to the AI in order to implement key gameplay mechanics, namely the contraband check and the arrest warrant.
The contraband check (as it was in 2400AD) is a Robot approaching the Player, seemingly randomly, and conducting a spot-check whether the Player is carrying any items.
The arrest warrant — not in 2400AD — is similar in concept, but that it checks whether you have accrued too many demerits and therefore will be accosted and told to submit to imprisonment.
(In 2400AD, when you collect too many demerits, the Robots just shoot at you. The Robot Authority is slightly more civilised than the Tzorg.)
These two mechanics required AI to be improved.
But I required a Robot to determine a Player’s total demerits, or decide on conducting a contraband check based on a controllable game mechanic formula, it was apparent that I needed to expand the AI graph’s ability to call in-game functions where those computations could be made.
dofunc is a directive inside the graph that simply calls in-game functions. The format is simple:
dofunc <function> [<arg>]
The image below shows an example. The blue circles are
dofunc IsCheckCooldown as the example. Contraband checks should not occur too often, so a cooldown threshold is implemented. That function’s purpose is to check whether a contraband check cooldown threshold has been met.
The in-game function looks like this.
It returns either “0” or “1” (strings), and that forms a GlobalEventHandler name of
onfunc IsCheckCooldown 0 or
onfunc IsCheckCooldown 1 depending on the result. In the top image, you can see that a function called
RollForCheck does the same thing.
So that’s one application of the functions.
The other application is one that sets variables. In the previous AI version, variables were set using the
var directive. But what was needed was for functions to set variables as dictated by the AI.
So here’s an example of that. When a Checker Robot is attacked, it doesn’t fight back because it doesn’t have weapons. So it runs away. It tries to find a location that is out of sight of your weapon.
I created a function called
CheckerFindNearestHiddenTile. See the left blue circle below. Note that I use
hiddentile as the
<arg> parameter (the 3rd keyword).
The in-game function looks like:
One difference here is that I don’t use the GlobalEventHandler as a way to get the variable. Instead, the function uses the
<arg>, which is
"hiddentile" as the base name of the variables to set in the AI’s memory. In the function above, I set an X and Y variable using
"hiddentile" as my base name, and back in the AI graph, I use it to determine the coordinates for the Robot’s movement.
Engine’s the limit
I also use functions to do what would normally be tedious to do if I didn’t have the AI as the framework. For example, I am able to call a function
dofunc CheckerSayAttacked, which brings up a speech bubble above the Robot saying its being attacked.
The function capability now attached to the AI’s framework allows graphs to be re-used across different Robots, so that the same function can be used on another kind of Robot.
Functions are also used to raise the alert level on a global scope, they are used to bring up the Convo system so when the Robot comes close to the Player, and if the Robot intends to accost the Player, the Convo system comes up with the appropriate topic entry.
So far, the fanciest function I’ve done was a predictive search for the Player when the Robot has lost LOS of the Player. The power of engine is the limit, whatever the engine is. And because this AI is agnostic, I hope to be able to port this functionality in Unity when the time comes around to do it.
The video below shows a sample of the AI in action. The Player has accumulated enough demerits that the Checker will accost him. If the Player decides not to submit to detention, the Checker runs away, and the Minder (the other Robot) engages the Player in combat.