Tutorial -- significantly improving the AI of Wolf3D

Make your own free website on Tripod.com

In this tutorial, you will significantly improve the A.I. of Wolf3D.

First, open WL_STATE.C and make the following change:







void SelectDodgeDir (objtype *ob)
{
int deltax,deltay,i;
unsigned absdx,absdy;
dirtype dirtry[5];
dirtype turnaround,tdir;

if (ob->flags & FL_FIRSTATTACK)
{
//
// turning around is only ok the very first time after noticing the
// player
//
turnaround = nodir;
ob->flags &= ~FL_FIRSTATTACK;
}
else
turnaround=opposite[ob->dir];

if ((ob->hitpoints < gamestate.health) || (gamestate.etactic == 1)) {
deltax=ob->tilex - player->tilex;
deltay=ob->tiley - player->tiley;
} else {
deltax=player->tilex - ob->tilex;
deltay=player->tiley - ob->tiley;
}

//
// arange 5 direction choices in order of preference
// the four cardinal directions plus the diagonal straight towards
// the player
//





This will make it so that enemies will run away from the player and take cover behind walls if either they have less health than the player or the enemy tactic is "defensive", otherwise they will run toward the player.

Now, make the following change (still in WL_STATE.C):







void SelectChaseDir (objtype *ob)
{
int deltax,deltay,i;
dirtype d[3];
dirtype tdir, olddir, turnaround;

olddir=ob->dir;
turnaround=opposite[olddir];

if ((ob->obclass == dogobj) || (ob->obclass == ghostobj) || (ob->obclass == spectreobj)) {
deltax=player->tilex - ob->tilex;
deltay=player->tiley - ob->tiley;
} else if (ob->hitpoints < gamestate.health) { // If health is too low, run away
deltax=ob->tilex - player->tilex;
deltay=ob->tiley - player->tiley;
} else if (gamestate.etactic == 1) {
ob->dir = nodir;
return;
} else {
deltax=player->tilex - ob->tilex;
deltay=player->tiley - ob->tiley;
}


d[1]=nodir;
d[2]=nodir;





This will make it so that, when under cover, enemies will run away from the player if they have less health than the player, otherwise stay still if the enemy tactic is "defensive" or run toward the player if the enemy tactic is "offensive".

Now, still in WL_STATE.C, make the following addition:







void DamageActor (objtype *ob, unsigned damage)
{
madenoise = true;

//
// do double damage if shooting a non attack mode actor
//
if ( !(ob->flags & FL_ATTACKMODE) )
damage <<= 1;

ob->hitpoints -= damage;

gamestate.dnum -= damage;
if (gamestate.dnum < MINDNUM) {
gamestate.etactic = (gamestate.etactic == 0) ? 1 : 0;
gamestate.dnum = 0;
}


if (ob->hitpoints<=0) {
ob->hitpoints = 0;
KillActor (ob);
}





Now, still in WL_STATE.C, make the following addition:







void KillActor (objtype *ob)
{
int tilex,tiley;

tilex = ob->tilex = ob->x >> TILESHIFT; // drop item on center
tiley = ob->tiley = ob->y >> TILESHIFT;

gamestate.dnum -= ob->hitpoints;

if (gamestate.dnum < MINDNUM) {
gamestate.etactic = (gamestate.etactic == 0) ? 1 : 0;
gamestate.dnum = 0;
}


switch (ob->obclass)
{





Now, open WL_DEF.H and make the following addition:







// object flag values

#define FL_SHOOTABLE 1
#define FL_BONUS 2
#define FL_NEVERMARK 4
#define FL_VISABLE 8
#define FL_ATTACKMODE 16
#define FL_FIRSTATTACK 32
#define FL_AMBUSH 64
#define FL_NONMARK 128

#define MINDNUM -500
#define MAXDNUM 1000


//
// sprite constants
//





Now, make the following addition (still in WL_DEF.H):







//---------------
//
// gamestate structure
//
//---------------

typedef struct
{
int difficulty;
int mapon;
long oldscore,score,nextextra;
int lives;
int health;
int ammo;
int keys;
weapontype bestweapon,weapon,chosenweapon;

int faceframe;
int attackframe,attackcount,weaponframe;

int episode,secretcount,treasurecount,killcount,
secrettotal,treasuretotal,killtotal;
long TimeCount;
long killx,killy;
boolean victoryflag; // set during victory animations
int dnum;
int etactic;

} gametype;





Now open WL_AGENT.C and make the following change:







void TakeDamage (int points,objtype *attacker)
{
LastAttacker = attacker;

if (gamestate.victoryflag)
return;
if (gamestate.difficulty==gd_baby)
points>>=2;

if (!godmode) {
gamestate.health -= points;
gamestate.dnum += points;
if (gamestate.dnum > MAXDNUM)
gamestate.dnum = MAXDNUM;
}

if (gamestate.health>=0)
{
gamestate.health = 0;
playstate = ex_died;
killerobj = attacker;
}





Now make the following addition to WL_GAME.C:







if (tedlevel == false) // SO'S YA DON'T GET KILLED WHILE LAUNCHING!
gamestate.lives--;

if (gamestate.lives > -1)
{
gamestate.health = 100;
gamestate.weapon = gamestate.bestweapon
= gamestate.chosenweapon = wp_pistol;
gamestate.ammo = STARTAMMO;
gamestate.keys = 0;
gamestate.attackframe = gamestate.attackcount =
gamestate.weaponframe = 0;
gamestate.dnum = 0;
gamestate.etactic = 0;


DrawKeys ();





That's all you have to do, except for one thing: adding blocking codes. Add the following code to WL_STATE.C:


#define CHECKDIAG(x,y) \
{ \

if (*(mapsegs[1]+farmapylookup[y]+x) == 1) return false; \

temp=(unsigned)actorat[x][y]; \
if (temp) \
{ \
if (temp<256) \
return false; \
if (((objtype *)temp)->flags&FL_SHOOTABLE) \
return false; \
} \
}

#define CHECKSIDE(x,y) \
{ \
if (*(mapsegs[1]+farmapylookup[y]+x) == 1) return false; \

temp=(unsigned)actorat[x][y]; \
if (temp) \
{ \
if (temp<128) \
return false; \
if (temp<256) \
doornum = temp&63; \
else if (((objtype *)temp)->flags&FL_SHOOTABLE)\
return false; \
} \
}

This will make it so that whereever there's object code 1, enemies will never walk over, and will never open a door that it's over. Use this to prevent enemies from opening locked doors for the player, although you can use it for other things, too.

That's all! If you have problems with "Abnormal program termination" errors, use Ripper's tutorial on "removing the scalers".

NOTE: I have updated this AI code, but I haven't updated this tutorial yet. View this for information on what changes to make.