This guide is both a view on monster AIs as they will be in alpha6 and a call for features requests if there are any, about AIs obviously.
The day before
Previously monster AIs were extremely simple:
- Check if I want/can cast a spell
- If so, then cast it
- If not then move toward target in straight line
And .. that's it.
Then DarkGod came and said "Thou shall be smart!"
The day after
In alpha6 monsters will be able(and will have to use) the new AI framework which consists of:
- User definable AIs in lua
An AI state that can take predefined parameters and that stays for the whole life(however short it may be;>) of the monster
- The ability to combine AIs into a new AI
- Different speed for different actions(just like the player)
- A set of basic predefined AIs in the engine
How does AIs work
The engine when it is time to make a monster take its turn lookup the monster current AI and calls it. The AI does its work and returns telling the engine if it must now move the monster in a certain direction or if it must do nothing(i.e: the AI did cast a spell). (Note that it is then possible to make a monster cast 3 spells per turns AND move, isn't that groovy ?)
Simple example
-- Movement AI: The zombie
-- Very basic "zombie-like" AI: move toward the target, and hit it.
ai.new
{
name = "ZOMBIE"
state =
{
MIN_RANGE = -1
BEST_RANGE = -1
}
init = function(monst, state)
print("init done")
end
exec = function(m_idx, monst, state)
local range = flag_get(state, FLAG_BEST_RANGE)
if range == -1 then range = flag_get(state, FLAG_MIN_RANGE) end
local y, x = ai.target(monst)
local dist = ai.distance(monst)
if range == -1 or dist > range or not los(monst.fy, monst.fx, y, x) then
monst.ai_action = ai.action.MOVE
monst.ai_move_y = y
monst.ai_move_x = x
else
monst.ai_action = ai.action.MOVE
monst.ai_move_y = monst.fy * 2 - y
monst.ai_move_x = monst.fx * 2 - x
end
end
}
What does this do:
- We give it a name, woh impressive!
- We define what we need in the AI state. This part will automagically define corresponding flags if they do not exist, in this example the flags FLAG_MIN_RANGE and FLAG_BEST_RANGE will be defined and any monsters using this AI will have their values set to -1 unless specified otherwise.
- We define a state init function in case we want to do something special(we do not here)
- And last but not least we define the AI itself which does:
- Get the parameters in the state
- Get the target location and distance
- If the target not visible or we are not in range, move toward it
- If the target is below the "safe" range limit, we go backwards
That's it! This AI will move dumbly toward its target and hit it when it is close enough.
Spellcasting
Now an example of a dumb caster:
-- Casting AI: The dumb random cast
ai.new
{
name = "RANDOM_CAST"
exec = function(m_idx, monst, state)
local y, x = ai.target(monst)
local spl = flag_get_rand(monst.spells)
if projectable(monst.fy, monst.fx, y, x) then
local level, fail = flag_get(monst.spells, spl), flag_get2(monst.spells, spl)
set_auto_cast(m_idx, level, y, x)
if get_mana(spl) <= flag_get(monst.flags, FLAG_MANA_CURR) then
if cast_monster_spell(m_idx, spl, level, fail, y, x) then
monst.ai_action = ai.action.OTHER
flag_inc(monst.flags, FLAG_MANA_CURR, -get_mana(flag_get(monst.spells, spl)));
end
end
unset_auto_cast()
else
monst.ai_action = ai.action.REST
end
end
}
What does it do ?
- Get the target location
- Get a random spell from the spell list
- Check if spells can hit the target
- Get the level, fail rate and mana usage of the spell
- Check if enough mana
- Cast it
Easy and dumb! Not we can bypass the mana check, if we do not want monsters spells to use mana.
Combining AIs
-- Combinaision AI: cast spells and move toward target
ai.new
{
name = "SPELL_AND_MOVE"
state =
{
MOVE_AI = ai.NONE
CASTING_AI = ai.NONE
}
init = function(monst, state)
ai.init(monst, flag_get(state, FLAG_MOVE_AI))
ai.init(monst, flag_get(state, FLAG_CASTING_AI))
end
exec = function(m_idx, monst, state)
if rng.percent(monst.freq_spell) then
ai.exec(flag_get(state, FLAG_CASTING_AI), m_idx, monst)
if monst.ai_action == ai.action.OTHER then return end
end
ai.exec(flag_get(state, FLAG_MOVE_AI), m_idx, monst)
end
}
This AI combine 2 AIs to work. Basically it is a move or cast spell AI, just like the old T-Engine AI.
What does it do:
- Define a state with flags for each AIs to use
- When it is initialized, initialize the sub-AIs
- When it is run:
- If we feel like casting, call the casting AI
- If we did cast, stop
- If we did not, call the movement AI
Now with this AI we can combine all other basic AIs to make a new range of AIs. If we have a move-like-a-snake AI we can have a random caster snake. If we have a smart-cast AI we can have a smart casting snake-like moving monster. You can make custom AIs for your boss. And so on, the limits and without limits
You will have to check the ToME module for a example of a smart casting AI.
Things to remember
The more monsters, the more complex the AIs the slower the game will run. If is fine to do an overly complex AI for 5% of the monsters, but not for 90% of them(probably).
Have fun!
How to use them ?!
Last thing, how to give monsters AIs ? First, remeber, a monster with AI will just stand there doing nothing.
Now it's quite simple, in the monster flags(or default flags) simply add:
AI = ai.ZOMBIE
AI_STATES = getter.flags{ MIN_RANGE=3 }
That's all yes, define the AI and set the parameters you want.
So for the spell-and-move AI we get:
AI = ai.SPELL_AND_MOVE
AI_STATES = getter.flags{ MOVE_AI=ai.SNAKE CASTING_AI=ai.RANDOM_CAST MIN_RANGE=3 }
Chatter
BucketMan: AI will be something we can change at any time from code, yes? So...after the battle with the Red Ribbon Army, I'll be able to assign Mai and Shuu an AI that walks them back at normal speed to their standing position at Emperor Pilaf's side, and once they get their resume never moving? I'll be able to create guards who walk back and forth between two points endlessly, until a condition is met, or the player enters within a certain range? I'll be able to have fast moving 'spell casters' that move AWAY from the player if they're at melee range, and stand stationary and cast if they're at range? Excellent! Since you're asking for feature requests...I suppose pre-defined AI's that did each of those three things would be very nice.
(Walk to y/x, 'patrol' between two points unless, and 'avoid' player but cast while at range.)
DarkGod: Indeed you will be able to do that.
I might add some of those yes but really they are not hardto do, it's good for you .. and THEN I can stealthem and include them in the engine ;>
ReenenLaurie: This is what I envisioned... better AI, and then less crazy monsters (ie. not 50000 HP, just being smarter and therefor trickier to kill).
Anyway, stuff like ball / summon spells when LOS is broken. This would really make the game hard, if not impossible, but it can be balanced or very restricted to *smart* spellcasters.
DarkGod: Yes obviously not every monster should be a walking angel of death with a IQ of 350
As for HP actaully for ToME3 we will rescale player & monster HP to be more similar(mainly due to the fact that friendly monsters will work better this way), but it is lpayer HP taht will be reworked, that his player will have about 3 times the HP of ToME2, monsters will have the same as now and monster atatcks will do 3 times the damage they do now. Changes nothing for the player but makes pet much more effective without ugly hacks(and possessors will be easier to compute too
ShrikeDeCil: I think this is possible under what's listed above due to the inclusion of the state variables, but I thought I'd mention it. A collective intelligence type AI. The packs of wolves should work as a team. The worm masses aren't really separable in the first place. Many of the various breeders in fact. Using the state variables to keep track of 'center of mass', or enumerating pack members, or whatever seems like to could be used for adding some simple cooperativeness. Yes?
DarkGod: Sure, although there might not be a need for doing that, in T2 pack animals already worked as a .. pack, without any state machine going on. Anyway I'm planning a "battle mode" subsystem that will add group AIs to create large battles between multiple factions, like helm's deep, the 5 armies, the storming of isengard, the assault on the black gate, ... This will work by having each monster get a very basic AI that just does what the state commands it and an "overmind" AI that compute all the batte strategy and tells which monster to do what. It should also feature a UI mod to allow the player to act as one of the armires general/captpain/whatever and form monsters into battle groups, giving each of them orders and getting visual reports of the groups status.
ToME Wiki