ID:151803
 
How should I go about this? In an older project, I set up a massive list of all attacks and their ranges, etc. and had the AI follow that list. However, I found it rather annoying to constantly have to update it, and I always disliked having giant lists at compile time. Any more efficient, easy to maintain methods?
I'm not sure if this is the best method, but generally what I do is break each function in an AI down into it's own proc.

The basic AI works like...

Start loop
Find target
If get target, move to it
If close to target, attack it
If no target, wander randomly
loop proc again

Each of those steps (except for the loop) is a very basic, and general proc, incapable of doing anything unique or special.

However, the whole point of this is so that I can define more unique behaviour for specific enemies.
A normal enemies attack proc might just consist of attack and nothing else. A Mage enemies might be "if got mp, use fireball, otherwise just attack".


Here is an example of the sort of code I use (though, this code was never intended to be read by anyone else, is not the best code ever and is only being posted so you get an idea of how this system works). (I am more or less just copying and pasting this code, so there is a lot of things in it that are not relivent here)


    
mob
proc
AI(mob/Monster/M)
var/v=1
if(!M.Active) //if the monster is not active delete it unless it is an NPC, in which case just disactivate it
if(!M.NPC)
M.DelTime++
if(M.DelTime>30) del M
else
M.DelTime=0
if(M.Target)
if(M.Target.Dead) M.Target=null
if(M.Target)
if(M.Target.invisibility || get_dist(M,M.Target)>14) M.Target=null
if(!M.Target) //if mob has no target attempt to find one
M.Target=M.AI_Find(M)

if(get_dist(M,M.Target)>M.AtkDist) //And target is far away
v=AI_StepTo(M,M.Target) //Move towards it
else
if(!M.canact) AI_Action(M,M.Target) //if target is close enough take action against it
if(!M.Target)
v=AI_Wander(M) //If no target then wander
spawn(M.WalkSpeed/v) //Sleep
M.AI(M) //And repeat AI

AI_Find(mob/Monster/M) //Find a valid target, can be over written for more specific AIs
for(var/mob/MM in view(M)) //Otherwise find and return first player in view OR the first minion of a player
if(get_dist(M,MM)<13)
if(MM.client) //If the mob has a client it is a player, return it as a valid target
return MM
return null
AI_Wander(mob/Monster/M) //Makes monster wander randomly, can be overwritten
if(M.nomove) return 1
var/turf/T=locate(M.orgx,M.orgy,M.orgz)
if(get_dist(M,T)<11) step_rand(M)
else step_to(M,T,1)
return 0.9

AI_StepTo(mob/Monster/M,mob/E) //Makes monster follow target, can be overwritten
if(M.nomove) return 1
if(M.Target.Dead)
M.Target=null
return 3
if(get_dist(M,E)<M.FollowDist) //If the target is within following distance follow it
step_to(M,E,max(1,M.AtkDist-M.GoodRange))
return 2.5
else
M.Target=null //Otherwise lose it as a target
return 1

AI_Action(mob/Monster/M,mob/E) //Action to take when close enough to a target to take action, can be overwritten
M.Attack(M,E,1)
return 1


That would be the "base" AI, so to say. Then for a mage I would do something like...


mob
Mage
AI_Action(mob/Monster/M,mob/E) //Action to take when close enough to a target to take action, can be overwritten
if(M.Mp>30)
M.FireBall(M,E)
else
M.Attack(M,E,1)
return 1


This basically means, whenever /mob/Mage activates the proc AI_Action, it will use fireball if it has enough MP, otherwise it will simply attack.

In a similar fashion, I could overwrite the find target proc, so it only returns players that are for example weakened. (by adding a if(hp<50) return target)


A system like this might not be the best ever, but I find it is much better than trying to use one huge proc for AIs. It is much easier to add and change unique actions for specific mobs, and allows for much more flexible and customizable AIs in general, without having to use one huge AI which you constantly have to add new conditions and what not to each time you add a new enemy.
In response to The Magic Man
Interesting ideas, though I wish to make it even more flexible than that, to the point where you could create new NPCs with different sets of attacks on the fly. That's the good thing about my old system, even though it was extremely annoying to keep up to date.
In response to Jeff8500
I don't see why a system like that could not be done with the method I use.
Though, I'd imagine it would require the use of lists, a lot, but I doubt there is a method of doing this that would not require the use of lists extensively.

For example, the Mage obj in my example could when created pick a skill to be used from a list, fireball, iceball or lightning bolt. (maybe these would be some sort of datum stored in a list somewhere, which the mage references)
The rest would work basically the same from there on, but it would result in the mage knowing a different skill each time a new one is created (again, referencing a datum and executing a proc the datum has to cast a spell would be an easy way of doing this without having to add an if condition for each possible spell the mage could use).