Pseudocódigo del cambio de estado para bichos.

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.


Vade retro