En todo momento del juego, es decir, en todo intervalo de una unidad de tiempo, todas las entidades que forman parte del juego (bichos, habitaciones, personaje, objetos) se encuentran en un estado. Los estados los podemos dividir en:
Estados estáticos. Son estados que no emiten ninguna instrucción, sino que sólo se utilizan para chequear condiciones. (informan, propiamente, del estado en que se encuentra el objeto). Ejemplos: NIGHT, RAIN (para habitaciones); GUARD, SLEEP (para bichos).
Estados dinámicos. Son aquellos estados que tienen por función el llevar a cabo una acción que consume un determinado tiempo. También se usan para chequear condiciones, pero además al final del estado se ejecutan unas determinadas acciones (endofstate): ATTACK, SPELL...
En cada unidad de tiempo del juego, se recorren todos los bichos del mundo, que están en memoria. Se hace correr el contador de tiempo y se mira si toca cambiar de estado, atendiendo a las condiciones correspondientes. En tal caso, se realiza el cambio adecuado y se ejecutan instrucciones en caso de un estado dinámico.
bicho.timeunitsleft--; if ( bicho.timeunitsleft == 0 ) if ( endofstate actions ) then perform endofstate; changestate: if ( not combat ) if ( hostility check ) then //being attacked or hostile to character in screen engage combat; changestate; else if ( being addressed ) then talk.ai; else if ( bicho.state == guard ) then bicho.state = guard unless change time; else if ( bicho.state == rest ) then bicho.state = rest unless change time; else if ( bicho.state == idle ) then bicho.state = idle; dynamism%: move( random( permitted areas ) ); else if ( bicho.state == pursue ) then if ( saved path ) then bicho.state = pursue; move( saved path ); else bicho.state = default_state( vars ); else if ( bicho.state == goto ) then if ( saved path ) then bicho.state = goto; move ( saved path ); else bicho.state = default_state( vars ); else if ( combat ) [possible attacks: At1, At2, ... , AtN inc. spell attacks] if ( enemy.state == recovery ) then if ( bicho.any_stat < bicho.that_stat.wimpy ) then escape; if ( ( avg( efficiency( AtI ) ) < valor_constant ) for all i=1 to n ) then escape; else //do attack: if ( bicho.canenchant ) then 20%: if ( spell=random( combatench ).threshold < bicho.mp ) cast spell; else //1. Recalculate attack probabilities for i=1 to n AtI.efficiency = AtI.times_used=0 ?efic_const :(AtI.totaldamage + 10)/ AtI.times_used); for i=1 to n AtI.probability = (AtI.efficiency)/Sum(AtJ.efficiency, j=1..n); //2. Do attack probswitch for i=1 to n AtI.probability%: AtI.action; inc AtI.times_used; recalc AtI.totaldamage; else if ( enemy.state == attack ) then //defI usually block, defaux usually dodge for i = 1 to n if ( enemy.timeunitsleft < bicho.defI.avgtimeunits ) defI; defaux; else if ( enemy.state == spellattack ) then for i = 1 to n if ( enemy.timeunitsleft < bicho.counterspellI.avgtimeunits and bicho.mp > counterspellI.threshold ) counterspellI;
Decidir y sincronizar los cambios de estado es una parte esencial, y en este pseudocódigo se perfila cómo será aproximadamente la IA para el combate y de qué parámetros dependerá. Como vemos, el enemigo sigue unas técnicas un tanto "heurísticas" para decidir cuál de sus posibles ataques es el que utiliza: va llevando un cálculo de la eficiencia de los ataques en función del daño que le han hecho anteriormente al personaje. Al cabo de dos o tres ataques ya sabrá, por ejemplo, que no tiene sentido atacar con bolas de fuego al mago al que le gusta el escudo de fuego.
No obstante, la parte más difícil en lo que se refiere a programación son las operaciones sobre datos que requieren algunos de estos estados y la organización de dichos datos.