DieHard Wolfers Forum Index DieHard Wolfers
A Wolfenstein 3d Fan Community


  Hosted by: MCS & Areyep.com - Designed by: BrotherTank

Original Yahoo Forum - Die Hard Archives

AReyeP HomepageAreyep Homepage DieHard Wolfenstein BunkerDieHard Wolfenstein Bunker Log inLog in RegisterRegister Banlist FAQFAQ Search ForumsSearch

  Username:    Password:      Remember me       

[Tutorial] Three Solutions in One - Enemy AI
Page 1 of 1
DieHard Wolfers Forum Index -> SDL Code Crackers View Previous TopicRefresh this PageAdd Topic to your Browser FavoritesSearch ForumsPrint this TopicE-mail TopicGoto Page BottomView Next Topic
Post new topicReply to topic
Author Message
BrotherTank
Forum Administrator
<B>Forum Administrator</B>


Joined: 01 Mar 2003
Last Visit: 13 Sep 2017

Topics: 153
Posts: 2255
Location: Ontario
canada.gif

PostPosted: Wed Feb 11, 2009 9:45 pm
   Subject: [Tutorial] Three Solutions in One - Enemy AI
   [ IP : Logged ]
Reply with quote
Goto Top of PostsGoto Bottom of Posts

This tutorial and code gives you these 3 items:

Darkone's AI Fix,
MCS's Block Enemy Tile,
and BrotherTank's Map Control Blocking

all in one package, and all compressed into very compact and logical routines. This is one of my sort of pet peeves or should I say compulsive need to make the code as tight and efficient as possible due to the memory limitations in the original Dos source. It's a habbit that I haven't been able to break.

With that said... Here is the repaired code for making Darkone's AI Fix for Wolfenstein work in the SDL version of the code. The changes are minor, but what the code does for the game is fix something that Id really blew when programming the AI. Maybe it was an oversight on their part?? I don't know, but by making the simple changes that Darkone did, makes all the difference to the game.

Enemies will no longer try to find the player and end up stuck in corners. You will also find that by using these changes that the enemies will react differently and be at different places in the game than what you have become used to (those that get alerted to your pressence).

To explain the difference, besides the compression of the code, if you look at the difference between Darkone's AI and the Original AI is the loop in which it decides which path the enemy will follow and the choices given to the enemy... To make it make sense... and see why Darkone made these changes we first have to look at this line of code:

::: CODE :::
typedef enum { east, northeast, north, northwest, west, southwest, south, southeast,  nodir } dirtype;


Now you can see that when it gets to the loops for checking which directions the enemy may go, the orginal AI says we start with north and go to west, or we start with west and go to north. 3 choices... This is the reason that they get stuck.. So what Darkone did is have it check from East to South and check only the cardinal directions... This gives the enemy 4 choices... But by counting by 2 (up or down) he achieves the cardinal direction check, so now the enemy can never get stuck, because 2 more of the directions are available now, that were not available in the original code.

Also the long capital names can be defined anywhere in your code or you can hardcode the spots by changing the capital name to a spot on the map... ie: in my code I have

#define GUARDBLOCKTILE tilemap[16][0]

in my wl_def.h file. What that does is that if you put a wall tile on that spot, then the guards can cross the blocking code. So you can change it to any tilemap spot you want for all of the different blocks...

Anyhow... here's the new code... Just replace the routines in WL_STATE.CPP with the code below:

::: CODE :::


// Enemy AI
char    dx8dir[9]= { 1, 1, 0, -1, -1, -1,  0,  1, 0};  // dx & dy based on direction
char    dy8dir[9]= { 0, 1, 1,  1,  0, -1, -1, -1, 0};  // used mainly in wl_enemy_ai.cpp


boolean CheckMovementOK(int x, int y, int *doornum, objtype *ob)
{
  int temp = (int)actorat[x][y];

    // Blocking Tile Check - Enemy Can't Cross
  if (MAPSPOT(x,y,1) == BLOCKTILE)
  {
#ifndef MAPBLOCKINGCONTROL
    return true;
#else
    switch (((objtype *)temp)->obclass)
    {
      case dogobj:         if (DOGBLOCKTILE)     return true; break;                                           
      case guardobj:       if (GUARDBLOCKTILE)   return true; break;                                           
      case ssobj:          if (SSBLOCKTILE)      return true; break;                                           
      case officerobj:     if (OFFICERBLOCKTILE) return true; break;                                           
      case mutantobj:      if (MUTANTBLOCKTILE)  return true; break;                                           
    #ifdef SPEAR
      case spectreobj:     if (SPECTREBLOCKTILE) return true; break;                                           
      case angelobj:       if (ANGELBLOCKTILE)   return true; break;                                           
      case transobj:       if (TRANSBLOCKTILE)   return true; break;                                           
      case uberobj:        if (UBERBLOCKTILE)    return true; break;                                           
      case willobj:        if (WILLBLOCKTILE)    return true; break;                                           
      case deathobj:       if (DEATHBLOCKTILE)   return true; break;                                           
    #else
      case bossobj:        if (HANSBLOCKTILE)    return true; break;                                           
     #ifndef UPLOAD
      case schabbobj:      if (SCHABBSBLOCKTILE) return true; break;                                           
      case fakeobj:        if (FAKEBLOCKTILE)    return true; break;                                           
      case mechahitlerobj: if (MECHABLOCKTILE)   return true; break;                                           
      case gretelobj:      if (GRETELBLOCKTILE)  return true; break;                                           
      case giftobj:        if (GIFTBLOCKTILE)    return true; break;                                           
      case fatobj:         if (FATBLOCKTILE)     return true; break;                                           
      case ghostobj:       if (GHOSTBLOCKTILE)   return true; break;                                           
      case realhitlerobj:  if (HITLERBLOCKTILE)  return true; break;                                           
     #endif
    #endif
      default:             if (OTHERBLOCKTILE)   return true; break;                                           
    }
#endif
  }
  if (temp)
  {
    if (temp<128 && doornum != NULL) return true;
    if (temp<256 && doornum == NULL) return true;
    else if (temp<256 && doornum != NULL)
    {
#ifdef PLAYDEMOLIKEORIGINAL       
      if(DEMOCOND_ORIG)
        doornum = temp & 63;
      else
#endif         
      *doornum = temp & 127;
    }
    else if (((objtype *)temp)->flags&FL_SHOOTABLE) return true;
  }
  return false;
}

boolean TryWalk (objtype *ob)
{
  int     oldx, oldy, newx, newy, doornum = -1;
 
   if(ob->dir == nodir) return false;
   oldx=ob->tilex;
   oldy=ob->tiley;
   newx= oldx + dx8dir[ob->dir];
   newy= oldy - dy8dir[ob->dir];   
  if (ob->dir & 1)  // Check Diagonal Movement
  {
      if(CheckMovementOK(newx, oldy, NULL, ob) ||
          CheckMovementOK(oldx, newy, NULL, ob) ||
          CheckMovementOK(newx, newy, NULL, ob)) return false;
   }
   else                   // Check Cardinal Movement
   {
      switch (ob->obclass)
      {
#ifndef SPEAR            
         case fakeobj: // they can't open doors
#else
      case spectreobj:
#endif            
      case dogobj:
            if(CheckMovementOK(newx, newy, NULL, ob)) return false;
            break;
        default:
           if(CheckMovementOK(newx, newy, &doornum, ob)) return false;
           break;   
      }
  }
  if (doornum != -1)
  {
    OpenDoor(doornum);
    ob->distance = -doornum-1;
    return true;
  }
  else ob->distance = TILEGLOBAL; // go ahead, its clear
  ob->tilex=newx;
  ob->tiley=newy;
  ob->areanumber=MAPSPOT(newx, newy, 0)-AREATILE;
  return true;
}

/* =============== Swap 2 Direction Values ================== */
dirtype SwapVal (dirtype *value1, dirtype *value2)
{
  dirtype temp = *value1;
  *value1 = *value2;
  *value2 = temp;
  return *value1, *value2;
}

/*
==================================
=
= SelectDodgeDir
=
= Attempts to choose and initiate a movement for ob that sends it towards
= the player while dodging
=
= If there is no possible move (ob is totally surrounded)
=
= ob->dir           =       nodir
=
= Otherwise
=
= ob->dir           = new direction to follow
= ob->distance      = TILEGLOBAL or -doornumber
= ob->tilex         = new destination
= ob->tiley
= ob->areanumber    = the floor tile number (0-(NUMAREAS-1)) of destination
=
==================================
*/
void SelectDodgeDir (objtype *ob)
{
  int     i;
  dirtype dirtry[5], turnaround;
  // turning around is only ok the very first time
  // after noticing the player
  if (ob->flags & FL_FIRSTATTACK) { turnaround = nodir; ob->flags &= ~FL_FIRSTATTACK; }
  else turnaround=opposite[ob->dir];
  // arange 5 direction choices in order of preference
  // the four cardinal directions plus the diagonal
  // straight towards the player
  if (player->tilex - ob->tilex > 0) { dirtry[1]= east;  dirtry[3]= west; }
  else                               { dirtry[1]= west;  dirtry[3]= east; }
  if (player->tiley - ob->tiley > 0) { dirtry[2]= south; dirtry[4]= north; }
  else                               { dirtry[2]= north; dirtry[4]= south; }
  // randomize a bit for dodging
  if (abs(player->tilex - ob->tilex) > abs(player->tiley - ob->tiley))
  { SwapVal (&dirtry[1], &dirtry[2]); SwapVal (&dirtry[3], &dirtry[4]); }
  if (US_RndT() < 128)
  { SwapVal (&dirtry[1], &dirtry[2]); SwapVal (&dirtry[3], &dirtry[4]); }
  dirtry[0] = diagonal[dirtry[1]][dirtry[2]];
  for (i=0;i<5;i++) // try the directions util one works
  {
    if ( dirtry[i] == nodir || dirtry[i] == turnaround) continue;
    ob->dir = dirtry[i];
    if (TryWalk(ob)) return;
  }
  if (turnaround != nodir) // turn around only as a last resort
  { ob->dir = turnaround; if (TryWalk(ob)) return; }
  ob->dir = nodir;
}

/*
============================
=
= SelectChaseDir
=
= As SelectDodgeDir, but doesn't try to dodge
=
============================
*/
void SelectChaseDir (objtype *ob)
{
  dirtype d[3];
  dirtype tdir, olddir, turnaround;

  olddir=ob->dir;
  turnaround=opposite[olddir];
  d[1]=nodir;
  d[2]=nodir;
  if (player->tilex - ob->tilex != 0)
    d[1] = (player->tilex - ob->tilex > 0 ? east:west);
  if (player->tiley - ob->tiley != 0)
    d[2] = (player->tiley - ob->tiley > 0 ? south:north);
  if (abs(player->tiley - ob->tiley) > abs(player->tilex - ob->tilex))
    SwapVal ( &d[1], &d[2] );
  if (d[1] == turnaround) d[1]=nodir;
  if (d[2] == turnaround) d[2]=nodir;
  if (d[1]!=nodir) /*either moved forward or attacked*/
  { ob->dir=d[1]; if (TryWalk(ob)) return; }
  if (d[2]!=nodir)
  { ob->dir=d[2]; if (TryWalk(ob)) return; }
  /* there is no direct path to the player, so pick another direction */
  if (olddir!=nodir)
  { ob->dir=olddir; if (TryWalk(ob)) return; }
  if (US_RndT()>128)      /*randomly determine direction of search*/
  {
#ifdef DARKONEAI
    for (tdir=east; tdir<=south; tdir=(dirtype)(tdir+2))
#else
    for (tdir=north; tdir<=west; tdir=(dirtype)(tdir+1))
#endif
    {
      if (tdir!=turnaround) { ob->dir=tdir; if ( TryWalk(ob) ) return; }
    }
  }
  else
  {
#ifdef DARKONEAI
    for (tdir=south; tdir>=east; tdir=(dirtype)(tdir-2))
#else
    for (tdir=west; tdir>=north; tdir=(dirtype)(tdir-1))
#endif
    {
      if (tdir!=turnaround) { ob->dir=tdir; if ( TryWalk(ob) ) return; }
    }
  }
  if (turnaround != nodir)
  {
    ob->dir=turnaround;
    if (ob->dir != nodir) { if ( TryWalk(ob) ) return; }
  }
  ob->dir = nodir; // can't move
}

/*
============================
=
= SelectRunDir - Retreat
=
= Run Away from player
=
============================
*/
void SelectRunDir (objtype *ob)
{
  dirtype d[3];
  dirtype tdir;
  if (player->tilex - ob->tilex < 0) d[1]= east; else d[1]= west;
  if (player->tiley - ob->tiley < 0) d[2]=south; else d[2]=north;
  if (abs(player->tiley - ob->tiley) > abs(player->tilex - ob->tilex))
  { SwapVal (&d[1], &d[2]); }
  ob->dir=d[1];
  if (TryWalk(ob)) return; /*either moved forward or attacked*/
  ob->dir=d[2];
  if (TryWalk(ob)) return;
  /* there is no direct path to the player, so pick another direction */
  if (US_RndT()>128)      /*randomly determine direction of search*/
  {
#ifdef DARKONEAI
    for (tdir=east; tdir<=south; tdir=(dirtype)(tdir+2))
#else
    for (tdir=north; tdir<=west; tdir=(dirtype)(tdir+1))
#endif
    { ob->dir=tdir; if ( TryWalk(ob) ) return; }
  }
  else
  {
#ifdef DARKONEAI
    for (tdir=south; tdir>=east; tdir=(dirtype)(tdir-2))
#else
    for (tdir=west; tdir>=north; tdir=(dirtype)(tdir-1))
#endif   
    { ob->dir=tdir; if ( TryWalk(ob) ) return; }
  }
  ob->dir = nodir;                // can't move
}


Hope you find this Helpfull...

Greg
BrotherTank
Display posts from previous:   
Post new topicReply to topic Time synchronized with the forum server time
DieHard Wolfers Forum Index -> SDL Code Crackers View Previous TopicRefresh this PageAdd Topic to your Browser FavoritesSearch ForumsPrint this TopicE-mail TopicGoto Page TopView Next Topic
Page 1 of 1
Jump to:  

Related topics
 Topics   Replies   Views   Last Post 
No new posts Announcement: Wolf3d & Spear of Destiny Shareware SDL Downloads
Author: BrotherTank
10 14640 Tue Aug 24, 2010 10:18 am
dcbasic View latest post
No new posts Sticky: [Tutorial] Compiling Wolf4SDL with Code::Blocks
Author: dcbasic
90 22956 Wed May 24, 2017 7:53 pm
Falcon*93 View latest post
No new posts [WOLF4SDL Help] Blake Stone Style doors
Author: Haasboy
3 2836 Sun Jul 20, 2008 7:56 am
Haasboy View latest post
No new posts [SDL-Help] Wall patches fix
Author: insurrectionman
0 1101 Tue Jul 15, 2008 11:12 am
insurrectionman View latest post
No new posts [SDL] Apply 2nd ScaleFactor to selected (hi-res) vga gfx
Author: Andy_Nonymous
9 4247 Sun Apr 27, 2008 2:30 am
AlumiuN View latest post
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
   You cannot delete your posts in this forum
You cannot vote in polls in this forum


Copyright ©2003-2008 DieHard Wolfers
A Modified subBunker Theme by BrotherTank
Powered by phpBB © 2001, 2005 phpBB Group