home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / games / volume11 / larn / part05 / movem.c < prev    next >
C/C++ Source or Header  |  1991-01-03  |  19KB  |  581 lines

  1. /*
  2.  *  movem.c (move monster)      Larn is copyrighted 1986 by Noah Morgan.
  3.  *
  4.  *  Here are the functions in this file:
  5.  *
  6.  *  movemonst()     Routine to move the monsters toward the player
  7.  *  build_proximity_ripple()  Build proximity ripple for smart monster move
  8.  *  move_scared()   Move scared monsters
  9.  *  move_smart()    Move smart monsters
  10.  *  move_dumb()     Move dumb monsters
  11.  *  mmove(x,y,xd,yd)    Function to actually perform the monster movement
  12.  */
  13. #include "header.h"
  14. #define min(x,y) (((x)>(y))?(y):(x))
  15. #define max(x,y) (((x)>(y))?(x):(y))
  16.  
  17. void movemonst();
  18. void build_proximity_ripple();
  19. void move_scared();
  20. void move_smart();
  21. void move_dumb();
  22. void mmove();
  23.  
  24. # define IDISTNORM   8  /* was 17 - dgk */
  25. # define IDISTAGGR  20  /* was 40 - dgk */
  26.  
  27. static short w1[9],w1x[9],w1y[9];
  28. static int tmp1,tmp2,tmp3,tmp4,distance;
  29.  
  30. /* list of monsters to move */
  31. static struct foo { char x ; char y; char smart; } movelist[250] ;
  32.  
  33. /*
  34.  *  movemonst()     Routine to move the monsters toward the player
  35.  *
  36.  *  This routine has the responsibility to determine which monsters are to
  37.  *  move, and call movemt() to do the move.
  38.  *  Returns no value.
  39.  */
  40. void movemonst()
  41.     {
  42.     register int i,j,movecnt=0, smart_count, min_int ;
  43.  
  44.     if (c[TIMESTOP]) return;    /* no action if time is stopped */
  45.     if (c[HASTESELF])  if ((c[HASTESELF]&1)==0)  return;
  46.     if (spheres) movsphere();   /* move the spheres of annihilation if any */
  47.     if (c[HOLDMONST])  return;  /* no action if monsters are held */
  48.  
  49.     if (c[AGGRAVATE])   /* determine window of monsters to move */
  50.       {
  51.       tmp1=playery-5; tmp2=playery+6; tmp3=playerx-10; tmp4=playerx+11;
  52.       distance=IDISTAGGR; /* depth of intelligent monster movement */
  53.       }
  54.     else
  55.       {
  56.       tmp1=playery-3; tmp2=playery+4; tmp3=playerx-5; tmp4=playerx+6;
  57.       distance=IDISTNORM; /* depth of intelligent monster movement */
  58.       }
  59.  
  60.     if (level == 0) /* if on outside level monsters can move in perimeter */
  61.         {
  62.         if (tmp1 < 0) tmp1=0;        if (tmp2 > MAXY) tmp2=MAXY;
  63.         if (tmp3 < 0) tmp3=0;        if (tmp4 > MAXX) tmp4=MAXX;
  64.         }
  65.     else /* if in a dungeon monsters can't be on the perimeter (wall there) */
  66.         {
  67.         if (tmp1 < 1) tmp1=1;        if (tmp2 > MAXY-1) tmp2=MAXY-1;
  68.         if (tmp3 < 1) tmp3=1;        if (tmp4 > MAXX-1) tmp4=MAXX-1;
  69.         }
  70.  
  71.     /* We now have a window in which to move monsters.  First find all
  72.        monsters in the window, then decide whether or not to move them.
  73.        Its faster that way since the size of the window is usually larger
  74.        than the # of monsters in that window.
  75.  
  76.        Find all monsters in the window.  The only time a monster cannot
  77.        move is if: monsters are not aggrevated, AND player is stealthed,
  78.        AND the monster is asleep due to stealth.  Split into two
  79.        separate loops in order to simplify the if statement inside the
  80.        loop for the most common case.
  81.  
  82.        Also count # of smart monsters.
  83.     */
  84.     smart_count = 0 ;
  85.     min_int = 10 - c[HARDGAME] ;    /* minimum monster intelligence to move smart */
  86.     if ( c[AGGRAVATE] || !c[STEALTH] )
  87.         {
  88.         for ( j = tmp1 ; j < tmp2 ; j++ )
  89.             for ( i = tmp3 ; i < tmp4 ; i++ )
  90.                 if (mitem[i][j])
  91.                     {
  92.                     movelist[movecnt].x = i;
  93.                     movelist[movecnt].y = j ;
  94.                     if ( monster[mitem[i][j]].intelligence > min_int )
  95.                         {
  96.                         movelist[movecnt].smart = TRUE ;
  97.                         smart_count++;
  98.                         }
  99.                     else
  100.                         movelist[movecnt].smart = FALSE ;
  101.                     movecnt++;
  102.                     }
  103.         }
  104.     else
  105.         {
  106.         for ( j = tmp1; j < tmp2 ; j++ )
  107.             for ( i = tmp3 ; i < tmp4 ; i++ )
  108.                 if ( mitem[i][j] && stealth[i][j] )   /* stealth[x][y] = 1 when AWAKE! */
  109.                     {
  110.                     movelist[movecnt].x = i;
  111.                     movelist[movecnt].y = j ;
  112.                     if ( monster[mitem[i][j]].intelligence > min_int )
  113.                         {
  114.                         movelist[movecnt].smart = TRUE ;
  115.                         smart_count++;
  116.                         }
  117.                     else
  118.                         movelist[movecnt].smart = FALSE ;
  119.                     movecnt++;
  120.                     }
  121.         }
  122.  
  123.     /* now move the monsters in the movelist.  If we have at least one
  124.        smart monster, build a proximity ripple and use it for all smart
  125.        monster movement.
  126.     */
  127.     if (movecnt > 0 )
  128.         {
  129.         if ( c[SCAREMONST] )
  130.             for ( i = 0 ; i < movecnt ; i++ )
  131.                 move_scared( movelist[i].x, movelist[i].y );
  132.         else
  133.             {
  134.             if ( smart_count > 0 )
  135.                 {
  136.                 /* I was going to put in code that prevented the rebuilding
  137.                    of the proximity ripple if the player had not moved since
  138.                    the last turn.  Unfortunately, this permits the player to
  139.                    blast down doors to treasure rooms and not have a single
  140.                    intelligent monster move.
  141.                 */
  142.                 build_proximity_ripple();
  143.                 for ( i = 0 ; i < movecnt ; i++ )
  144.                     if ( movelist[i].smart )
  145.                         move_smart( movelist[i].x, movelist[i].y );
  146.                     else
  147.                         move_dumb( movelist[i].x, movelist[i].y );
  148.                 }
  149.             else
  150.                 for ( i = 0 ; i < movecnt ; i++ )
  151.                     move_dumb( movelist[i].x, movelist[i].y );
  152.             }
  153.         }
  154.  
  155.     /* Also check for the last monster hit.  This is necessary to prevent
  156.        the player from getting free hits on a monster with long range
  157.        spells or when stealthed.
  158.     */
  159.     if ( c[AGGRAVATE] || !c[STEALTH] )
  160.         {
  161.         /* If the last monster hit is within the move window, its already
  162.            been moved.
  163.         */
  164.     if ( ( ( lasthx < tmp3 || lasthx >= tmp4 ) ||
  165.            ( lasthy < tmp1 || lasthy >= tmp2 ) ) &&
  166.            mitem[lasthx][lasthy] )
  167.             {
  168.         if ( c[SCAREMONST] )
  169.                 move_scared( lasthx, lasthy );
  170.             else
  171.         if ( monster[mitem[lasthx][lasthy]].intelligence > min_int )
  172.                     {
  173.             if ( smart_count == 0 )
  174.                         build_proximity_ripple( );
  175.                     move_smart( lasthx, lasthy );
  176.                     }
  177.                 else
  178.                     move_dumb( lasthx, lasthy );
  179.             lasthx = w1x[0];   /* make sure the monster gets moved again */
  180.             lasthy = w1y[0];
  181.             }
  182.         }
  183.     else
  184.         {
  185.         /* If the last monster hit is within the move window, and not
  186.            asleep due to stealth, then it has already been moved.
  187.        Otherwise (monster outside window, asleep due to stealth),
  188.        move the monster and update the lasthit x,y position.
  189.         */
  190.     if ( ( lasthx < tmp3 || lasthx >= tmp4 ) ||
  191.              ( lasthy < tmp1 || lasthy >= tmp2 ) &&
  192.        mitem[lasthx][lasthy] || !stealth[lasthx][lasthy] )
  193.             {
  194.         if ( c[SCAREMONST] )
  195.                 move_scared( lasthx, lasthy );
  196.             else
  197.         if ( monster[mitem[lasthx][lasthy]].intelligence > min_int )
  198.                     {
  199.             if ( smart_count == 0 )
  200.                         build_proximity_ripple( );
  201.                     move_smart( lasthx, lasthy );
  202.                     }
  203.                 else
  204.                     move_dumb( lasthx, lasthy );
  205.             lasthx = w1x[0];   /* make sure the monster gets moved again */
  206.             lasthy = w1y[0];
  207.             }
  208.         }
  209.     }
  210.  
  211. static char screen[MAXX][MAXY];    /* proximity ripple storage */
  212.  
  213. /* queue for breadth-first 'search' build of proximity ripple.
  214. */
  215. #define MAX_QUEUE 100
  216.     struct queue_entry
  217.         {
  218.         char x ;
  219.         char y ;
  220.         char distance ;
  221.         } queue[MAX_QUEUE];
  222.     int queue_head = 0 ;
  223.     int queue_tail = 0 ;
  224.  
  225. /* put a location on the proximity ripple queue
  226. */
  227. #define PUTQUEUE( _x, _y, _d )          \
  228.     {                                   \
  229.     queue[queue_tail].x = (_x) ;        \
  230.     queue[queue_tail].y = (_y) ;        \
  231.     queue[queue_tail].distance = (_d);  \
  232.     queue_tail++;                       \
  233.     if (queue_tail == MAX_QUEUE)        \
  234.         queue_tail = 0 ;                \
  235.     }
  236.  
  237. /* take a location from the proximity ripple queue
  238. */
  239. #define GETQUEUE( _x, _y, _d )          \
  240.     {                                   \
  241.     (_x) = queue[queue_head].x ;        \
  242.     (_y) = queue[queue_head].y ;        \
  243.     (_d) = queue[queue_head].distance ; \
  244.     queue_head++;                       \
  245.     if (queue_head == MAX_QUEUE)        \
  246.         queue_head = 0 ;                \
  247.     }
  248.  
  249. /* check for the proximity ripple queue being empty
  250. */
  251. #define QUEUEEMPTY() (queue_head == queue_tail)
  252.  
  253. /*
  254.     For smart monster movement, build a proximity ripple from the player's
  255.     position, out to a 'distance' of 20.  For example:
  256.  
  257.     W 5 4 4 W W X    Player is at position marked 1
  258.     W 5 W 3 3 W W    W is a wall.  Monsters will attempt
  259.     W 6 W 2 W 4 W    to move to a location with a smaller
  260.     W 7 W 1 W 5 W    value than their current position.
  261.     W 8 W W W 6 W    Note that a monster at location X
  262.     W 9 9 8 7 7 7    will not move at all.
  263.     W W W 8 W W W
  264. */
  265. void build_proximity_ripple()
  266.     {
  267.     int xl, yl, xh, yh ;
  268.     int k, m, z, tmpx, tmpy;
  269.     int curx, cury, curdist;
  270.  
  271.     xl=tmp3-2; yl=tmp1-2; xh=tmp4+2;  yh=tmp2+2;
  272.     vxy(&xl,&yl);  vxy(&xh,&yh);
  273.     for (k=yl; k<=yh; k++)
  274.     for (m=xl; m<=xh; m++)
  275.         {
  276.         switch(item[m][k])
  277.         {
  278.         case OWALL:
  279.         case OPIT:
  280.         case OTRAPARROW:
  281.         case ODARTRAP:
  282.         case OCLOSEDDOOR:
  283.         case OTRAPDOOR:
  284.         case OTELEPORTER:
  285.             screen[m][k]=127;
  286.             break;
  287.         default:
  288.             screen[m][k] = 0;
  289.             break;
  290.         };
  291.           }
  292.       screen[playerx][playery]=1;
  293.  
  294. /* now perform proximity ripple from playerx,playery to monster */
  295.       xl=tmp3-1; yl=tmp1-1; xh=tmp4+1;  yh=tmp2+1;
  296.       vxy(&xl,&yl);  vxy(&xh,&yh);
  297.  
  298.       PUTQUEUE( playerx, playery, 1 );
  299.       do
  300.       {
  301.       GETQUEUE( curx, cury, curdist );
  302.  
  303.       /* test all spots around the current one being looked at.
  304.       */
  305.       if ( ( curx >= xl && curx < xh ) &&
  306.            ( cury >= yl && cury < yh ) )
  307.           {
  308.           for (z=1; z<9; z++)
  309.           {
  310.           tmpx = curx + diroffx[z] ;
  311.           tmpy = cury + diroffy[z] ;
  312.           if (screen[tmpx][tmpy] == 0 )
  313.               {
  314.               screen[tmpx][tmpy] = curdist + 1;
  315.               PUTQUEUE( tmpx, tmpy, curdist + 1 );
  316.               }
  317.           }
  318.           }
  319.       }
  320.       while (!QUEUEEMPTY());
  321.  
  322.     }
  323.  
  324. /*
  325.     Move scared monsters randomly away from the player position.
  326. */
  327. void move_scared( i, j )
  328. int i, j ;
  329.     {
  330.     int xl, yl, tmp, tmpitem ;
  331.  
  332.     /* check for a half-speed monster, and check if not to move.  Could be
  333.        done in the monster list build.
  334.     */
  335.     switch(mitem[i][j])
  336.         {
  337.         case TROGLODYTE:  case HOBGOBLIN:  case METAMORPH:  case XVART:
  338.         case INVISIBLESTALKER:  case ICELIZARD: if ((gtime & 1) == 1) return;
  339.         };
  340.  
  341.     if ((xl = i+rnd(3)-2) < 0)
  342.     xl=0;
  343.     if (xl >= MAXX)
  344.     xl=MAXX-1;
  345.     if ((yl = j+rnd(3)-2) < 0)
  346.     yl=0;
  347.     if (yl >= MAXY)
  348.     yl=MAXY-1;
  349.  
  350.     if ((tmp=item[xl][yl]) != OWALL)
  351.     if (mitem[xl][yl] == 0)
  352.         if ((mitem[i][j] != VAMPIRE) || (tmp != OMIRROR))
  353.         if (tmp != OCLOSEDDOOR)
  354.             mmove(i,j,xl,yl);
  355.     }
  356.  
  357. /*
  358.     Move monsters that are moving intelligently, using the proximity
  359.     ripple.  Attempt to move to a position in the proximity ripple
  360.     that is closer to the player.
  361.  
  362.     Parameters: the X,Y position of the monster to be moved.
  363. */
  364. void move_smart( i, j )
  365. int i,j ;
  366.     {
  367.     int x,y,z ;
  368.  
  369.     /* check for a half-speed monster, and check if not to move.  Could be
  370.        done in the monster list build.
  371.     */
  372.     switch(mitem[i][j])
  373.         {
  374.         case TROGLODYTE:  case HOBGOBLIN:  case METAMORPH:  case XVART:
  375.         case INVISIBLESTALKER:  case ICELIZARD: if ((gtime & 1) == 1) return;
  376.         };
  377.  
  378.     /* find an adjoining location in the proximity ripple that is
  379.        closer to the player (has a lower value) than the monster's
  380.        current position.
  381.     */
  382.     if (mitem[i][j] != VAMPIRE)
  383.     for (z=1; z<9; z++) /* go around in a circle */
  384.         {
  385.         x = i + diroffx[z] ;
  386.         y = j + diroffy[z] ;
  387.         if ( screen[x][y] < screen[i][j] )
  388.         if ( !mitem[x][y] )
  389.             {
  390.             mmove(i,j,w1x[0]=x,w1y[0]=y);
  391.             return;
  392.             }
  393.         }
  394.     else
  395.     /* prevent vampires from moving onto mirrors
  396.     */
  397.     for (z=1; z<9; z++) /* go around in a circle */
  398.         {
  399.         x = i + diroffx[z] ;
  400.         y = j + diroffy[z] ;
  401.         if (( screen[x][y] < screen[i][j] ) &&
  402.         ( item[x][y] != OMIRROR ))
  403.         if ( !mitem[x][y] )
  404.             {
  405.             mmove(i,j,w1x[0]=x,w1y[0]=y);
  406.             return;
  407.             }
  408.         }
  409.  
  410.     }
  411.  
  412. /*
  413.    For monsters that are not moving in an intelligent fashion.  Move
  414.    in a direct fashion toward the player's current position.
  415.  
  416.    Parameters: the X,Y position of the monster to move.
  417. */
  418. void move_dumb( i, j )
  419. int i, j ;
  420.     {
  421.     int xl, yl, xh, yh ;
  422.     int k, m, tmp, tmpd, tmpx, tmpy ;
  423.  
  424.     /* check for a half-speed monster, and check if not to move.  Could be
  425.        done in the monster list build.
  426.     */
  427.     switch(mitem[i][j])
  428.         {
  429.         case TROGLODYTE:  case HOBGOBLIN:  case METAMORPH:  case XVART:
  430.         case INVISIBLESTALKER:  case ICELIZARD: if ((gtime & 1) == 1) return;
  431.         };
  432.  
  433.     /* dumb monsters move here */
  434.     /* set up range of spots to check.  instead of checking all points
  435.        around the monster, only check those closest to the player.  For
  436.        example, if the player is up and right of the monster, check only
  437.        the three spots up and right of the monster.
  438.     */
  439.     xl=i-1;  yl=j-1;  xh=i+2;  yh=j+2;
  440.     if (i<playerx) xl++; else if (i>playerx) --xh;
  441.     if (j<playery) yl++; else if (j>playery) --yh;
  442.  
  443.     /* check all spots in the range.  find the one that is closest to
  444.        the player.  if the monster is already next to the player, exit
  445.        the check immediately.
  446.     */
  447.     tmpd = 10000 ;
  448.     tmpx = i ;  tmpy = j ;
  449.     for ( k = xl ; k < xh ; k++ )
  450.     for ( m = yl ; m < yh ; m++ )
  451.         if ( k == playerx && m == playery )
  452.         {
  453.         tmpd = 1 ;
  454.         tmpx = k ;
  455.         tmpy = m ;
  456.         break;       /* exitloop */
  457.         }
  458.         else if ((item[k][m] != OWALL) &&
  459.              (item[k][m] != OCLOSEDDOOR) &&
  460.              ((mitem[k][m] == 0 ) || (( k == i ) && ( m == j ))) &&
  461.              ((mitem[i][j] != VAMPIRE) || (item[k][m] != OMIRROR)))
  462.         {
  463.         tmp = (playerx-k)*(playerx-k)+(playery-m)*(playery-m);
  464.         if (tmp < tmpd)
  465.             {
  466.             tmpd = tmp;
  467.             tmpx = k;
  468.             tmpy = m;
  469.             }  /* end if */
  470.         }  /* end if */
  471.  
  472.     /* we have finished checking the spaces around the monster.  if
  473.        any can be moved on and are closer to the player than the
  474.        current location, move the monster.
  475.     */
  476.     if ((tmpd < 10000) && ((tmpx != i) || (tmpy != j)))
  477.     {
  478.     mmove( i, j, tmpx, tmpy );
  479.     w1x[0] = tmpx ;              /* for last monster hit */
  480.     w1y[0] = tmpy ;
  481.     }
  482.     else
  483.     {
  484.     w1x[0] = i ;              /* for last monster hit */
  485.     w1y[0] = j ;
  486.     }
  487.     }  /* end move_dumb() */
  488.  
  489. /*
  490.  *  mmove(x,y,xd,yd)    Function to actually perform the monster movement
  491.  *      int x,y,xd,yd;
  492.  *
  493.  *  Enter with the from coordinates in (x,y) and the destination coordinates
  494.  *  in (xd,yd).
  495.  */
  496. void mmove(aa,bb,cc,dd)
  497.     int aa,bb,cc,dd;
  498.     {
  499.     register int tmp,i,flag;
  500.     char *who,*p;
  501.     flag=0; /* set to 1 if monster hit by arrow trap */
  502.     if ((cc==playerx) && (dd==playery))
  503.         {
  504.         hitplayer(aa,bb);
  505.         return;
  506.         }
  507.     i=item[cc][dd];
  508.     if ((i==OPIT) || (i==OTRAPDOOR))
  509.       switch(mitem[aa][bb])
  510.         {
  511.     case BAT:           case EYE:
  512.     case SPIRITNAGA:    case PLATINUMDRAGON:    case WRAITH:
  513.         case VAMPIRE:       case SILVERDRAGON:      case POLTERGEIST:
  514.         case DEMONLORD:     case DEMONLORD+1:       case DEMONLORD+2:
  515.         case DEMONLORD+3:   case DEMONLORD+4:       case DEMONLORD+5:
  516.         case DEMONLORD+6:   case DEMONPRINCE:   break;
  517.  
  518.         default:    mitem[aa][bb]=0; /* fell in a pit or trapdoor */
  519.         };
  520.     tmp = mitem[aa][bb];
  521.     mitem[cc][dd] = tmp;
  522.     if (i==OANNIHILATION)
  523.         {
  524.         if (tmp>=DEMONLORD+3) /* demons dispel spheres */
  525.             {
  526.             cursors();
  527.             lprintf("\nThe %s dispels the sphere!",monster[tmp].name);
  528.             rmsphere(cc,dd);    /* delete the sphere */
  529.             }
  530.         else mitem[cc][dd]=i=tmp=0;
  531.         }
  532.     stealth[cc][dd]=1;
  533.     if ((hitp[cc][dd] = hitp[aa][bb]) < 0) hitp[cc][dd]=1;
  534.     mitem[aa][bb] = 0;              
  535.     if (tmp == LEPRECHAUN)
  536.         switch(i)
  537.             {
  538.             case OGOLDPILE:  case OMAXGOLD:  case OKGOLD:  case ODGOLD:
  539.             case ODIAMOND:   case ORUBY:     case OEMERALD: case OSAPPHIRE:
  540.                     item[cc][dd] = 0; /* leprechaun takes gold */
  541.             };
  542.  
  543.     if (tmp == TROLL)  /* if a troll regenerate him */
  544.         if ((gtime & 1) == 0)
  545.             if (monster[tmp].hitpoints > hitp[cc][dd])  hitp[cc][dd]++;
  546.  
  547.     if (i==OTRAPARROW)  /* arrow hits monster */
  548.         { who = "An arrow";  if ((hitp[cc][dd] -= rnd(10)+level) <= 0)
  549.             { mitem[cc][dd]=0;  flag=2; } else flag=1; }
  550.     if (i==ODARTRAP)    /* dart hits monster */
  551.         { who = "A dart";  if ((hitp[cc][dd] -= rnd(6)) <= 0)
  552.             { mitem[cc][dd]=0;  flag=2; } else flag=1; }
  553.     if (i==OTELEPORTER) /* monster hits teleport trap */
  554.         { flag=3; fillmonst(mitem[cc][dd]);  mitem[cc][dd]=0; }
  555.     if (c[BLINDCOUNT]) return;  /* if blind don't show where monsters are   */
  556. # ifdef DGK
  557.     if (know[cc][dd] & HAVESEEN) 
  558. # else
  559.     if (know[cc][dd] & 1) 
  560. # endif
  561.         {
  562.         p=0;
  563.         if (flag) cursors();
  564.         switch(flag)
  565.           {
  566.           case 1: p="\n%s hits the %s";  break;
  567.           case 2: p="\n%s hits and kills the %s";  break;
  568.           case 3: p="\nThe %s%s gets teleported"; who="";  break;
  569.           };
  570.         if (p) { lprintf(p,who,monster[tmp].name); beep(); }
  571.         }
  572. /*  if (yrepcount>1) { know[aa][bb] &= 2;  know[cc][dd] &= 2; return; } */
  573. # ifdef DGK
  574.     if (know[aa][bb] & HAVESEEN)   show1cell(aa,bb);
  575.     if (know[cc][dd] & HAVESEEN)   show1cell(cc,dd);
  576. # else
  577.     if (know[aa][bb] & 1)   show1cell(aa,bb);
  578.     if (know[cc][dd] & 1)   show1cell(cc,dd);
  579. # endif
  580.     }
  581.