1 /*        $NetBSD: movem.c,v 1.10 2019/02/03 03:19:25 mrg Exp $       */
2 
3 /*
4  * movem.c (move monster)               Larn is copyrighted 1986 by Noah Morgan.
5  *
6  * Here are the functions in this file:
7  *
8  * movemonst()                Routine to move the monsters toward the player
9  * movemt(x,y)                Function to move a monster at (x,y) -- must determine where
10  * mmove(x,y,xd,yd) Function to actually perform the monster movement
11  * movsphere()                Function to look for and move spheres of annihilation
12  */
13 #include <sys/cdefs.h>
14 #ifndef lint
15 __RCSID("$NetBSD: movem.c,v 1.10 2019/02/03 03:19:25 mrg Exp $");
16 #endif                                  /* not lint */
17 
18 #include "header.h"
19 #include "extern.h"
20 
21 static void movemt(int, int);
22 static void mmove(int, int, int, int);
23 static void movsphere(void);
24 
25 /*
26  * movemonst()                Routine to move the monsters toward the player
27  *
28  * This routine has the responsibility to determine which monsters are to
29  * move, and call movemt() to do the move.
30  * Returns no value.
31  */
32 static short    w1[9], w1x[9], w1y[9];
33 static int      tmp1, tmp2, tmp3, tmp4, distance;
34 void
movemonst(void)35 movemonst(void)
36 {
37           int    i, j;
38           if (c[TIMESTOP])
39                     return;             /* no action if time is stopped */
40           if (c[HASTESELF])
41                     if ((c[HASTESELF] & 1) == 0)
42                               return;
43           if (spheres)
44                     movsphere();        /* move the spheres of annihilation if any */
45           if (c[HOLDMONST])
46                     return;             /* no action if monsters are held */
47 
48           if (c[AGGRAVATE]) { /* determine window of monsters to move */
49                     tmp1 = playery - 5;
50                     tmp2 = playery + 6;
51                     tmp3 = playerx - 10;
52                     tmp4 = playerx + 11;
53                     distance = 40;      /* depth of intelligent monster movement */
54           } else {
55                     tmp1 = playery - 3;
56                     tmp2 = playery + 4;
57                     tmp3 = playerx - 5;
58                     tmp4 = playerx + 6;
59                     distance = 17;      /* depth of intelligent monster movement */
60           }
61 
62           if (level == 0) {   /* if on outside level monsters can move in
63                                          * perimeter */
64                     if (tmp1 < 0)
65                               tmp1 = 0;
66                     if (tmp2 > MAXY)
67                               tmp2 = MAXY;
68                     if (tmp3 < 0)
69                               tmp3 = 0;
70                     if (tmp4 > MAXX)
71                               tmp4 = MAXX;
72           } else {            /* if in a dungeon monsters can't be on the
73                                          * perimeter (wall there) */
74                     if (tmp1 < 1)
75                               tmp1 = 1;
76                     if (tmp2 > MAXY - 1)
77                               tmp2 = MAXY - 1;
78                     if (tmp3 < 1)
79                               tmp3 = 1;
80                     if (tmp4 > MAXX - 1)
81                               tmp4 = MAXX - 1;
82           }
83 
84           for (j = tmp1; j < tmp2; j++) /* now reset monster moved flags */
85                     for (i = tmp3; i < tmp4; i++)
86                               moved[i][j] = 0;
87           moved[lasthx][lasthy] = 0;
88 
89           if (c[AGGRAVATE] || !c[STEALTH]) {      /* who gets moved? split for
90                                                              * efficiency */
91                     for (j = tmp1; j < tmp2; j++) /* look thru all locations in
92                                                              * window */
93                               for (i = tmp3; i < tmp4; i++)
94                                         if (mitem[i][j])    /* if there is a monster
95                                                                        * to move */
96                                                   if (moved[i][j] == 0)         /* if it has not already
97                                                                                  * been moved */
98                                                             movemt(i, j);       /* go and move the
99                                                                                  * monster */
100           } else {            /* not aggravated and not stealth */
101                     for (j = tmp1; j < tmp2; j++) /* look thru all locations in
102                                                              * window */
103                               for (i = tmp3; i < tmp4; i++)
104                                         if (mitem[i][j])    /* if there is a monster
105                                                                        * to move */
106                                                   if (moved[i][j] == 0)         /* if it has not already
107                                                                                  * been moved */
108                                                             if (stealth[i][j])  /* if it is asleep due
109                                                                                            * to stealth */
110                                                                       movemt(i, j);       /* go and move the
111                                                                                            * monster */
112           }
113 
114           if (mitem[lasthx][lasthy]) {  /* now move monster last hit by
115                                                    * player if not already moved */
116                     if (moved[lasthx][lasthy] == 0) {       /* if it has not already
117                                                                        * been moved */
118                               movemt(lasthx, lasthy);
119                               lasthx = w1x[0];
120                               lasthy = w1y[0];
121                     }
122           }
123 }
124 
125 /*
126  * movemt(x,y)                Function to move a monster at (x,y) -- must determine where
127  *        int x,y;
128  *
129  * This routine is responsible for determining where one monster at (x,y) will
130  * move to.  Enter with the monsters coordinates in (x,y).
131  * Returns no value.
132  */
133 static int      tmpitem, xl, xh, yl, yh;
134 static void
movemt(int i,int j)135 movemt(int i, int j)
136 {
137           int    k, m, z, tmp, xtmp, ytmp, monst;
138           switch (monst = mitem[i][j]) {          /* for half speed monsters */
139           case TROGLODYTE:
140           case HOBGOBLIN:
141           case METAMORPH:
142           case XVART:
143           case INVISIBLESTALKER:
144           case ICELIZARD:
145                     if ((gltime & 1) == 1)
146                               return;
147           };
148 
149           if (c[SCAREMONST]) {          /* choose destination randomly if scared */
150                     if ((xl = i + rnd(3) - 2) < 0)
151                               xl = 0;
152                     if (xl >= MAXX)
153                               xl = MAXX - 1;
154                     if ((yl = j + rnd(3) - 2) < 0)
155                               yl = 0;
156                     if (yl >= MAXY)
157                               yl = MAXY - 1;
158                     if ((tmp = item[xl][yl]) != OWALL)
159                               if (mitem[xl][yl] == 0)
160                                         if ((mitem[i][j] != VAMPIRE) || (tmpitem != OMIRROR))
161                                                   if (tmp != OCLOSEDDOOR)
162                                                             mmove(i, j, xl, yl);
163                     return;
164           }
165           if (monster[monst].intelligence > 10 - c[HARDGAME]) {       /* if smart monster */
166                     /* intelligent movement here -- first setup screen array */
167                     xl = tmp3 - 2;
168                     yl = tmp1 - 2;
169                     xh = tmp4 + 2;
170                     yh = tmp2 + 2;
171                     vxy(&xl, &yl);
172                     vxy(&xh, &yh);
173                     for (k = yl; k < yh; k++)
174                               for (m = xl; m < xh; m++) {
175                                         switch (item[m][k]) {
176                                         case OWALL:
177                                         case OPIT:
178                                         case OTRAPARROW:
179                                         case ODARTRAP:
180                                         case OCLOSEDDOOR:
181                                         case OTRAPDOOR:
182                                         case OTELEPORTER:
183                               smm:                screen[m][k] = 127;
184                                                   break;
185                                         case OMIRROR:
186                                                   if (mitem[m][k] == VAMPIRE)
187                                                             goto smm;
188                                                   /* FALLTHROUGH */
189                                         default:
190                                                   screen[m][k] = 0;
191                                                   break;
192                                         };
193                               }
194                     screen[playerx][playery] = 1;
195 
196                     /*
197                      * now perform proximity ripple from playerx,playery to
198                      * monster
199                      */
200                     xl = tmp3 - 1;
201                     yl = tmp1 - 1;
202                     xh = tmp4 + 1;
203                     yh = tmp2 + 1;
204                     vxy(&xl, &yl);
205                     vxy(&xh, &yh);
206                     for (tmp = 1; tmp < distance; tmp++)    /* only up to 20 squares
207                                                                        * away */
208                               for (k = yl; k < yh; k++)
209                                         for (m = xl; m < xh; m++)
210                                                   if (screen[m][k] == tmp)      /* if find proximity n
211                                                                                            * advance it */
212                                                             for (z = 1; z < 9; z++) {     /* go around in a circle */
213                                                                       if (screen[xtmp = m + diroffx[z]][ytmp = k + diroffy[z]] == 0)
214                                                                                 screen[xtmp][ytmp] = tmp + 1;
215                                                                       if (xtmp == i && ytmp == j)
216                                                                                 goto out;
217                                                             }
218 
219 out:                if (tmp < distance) /* did find connectivity */
220                               /* now select lowest value around playerx,playery */
221                               for (z = 1; z < 9; z++)       /* go around in a circle */
222                                         if (screen[xl = i + diroffx[z]][yl = j + diroffy[z]] == tmp)
223                                                   if (!mitem[xl][yl]) {
224                                                             mmove(i, j, w1x[0] = xl, w1y[0] = yl);
225                                                             return;
226                                                   }
227           }
228           /* dumb monsters move here */
229           xl = i - 1;
230           yl = j - 1;
231           xh = i + 2;
232           yh = j + 2;
233           if (i < playerx)
234                     xl++;
235           else if (i > playerx)
236                     --xh;
237           if (j < playery)
238                     yl++;
239           else if (j > playery)
240                     --yh;
241           for (k = 0; k < 9; k++)
242                     w1[k] = 10000;
243 
244           for (k = xl; k < xh; k++)
245                     for (m = yl; m < yh; m++) {   /* for each square compute
246                                                              * distance to player */
247                               tmp = k - i + 4 + 3 * (m - j);
248                               tmpitem = item[k][m];
249                               if (tmpitem != OWALL || (k == playerx && m == playery))
250                                         if (mitem[k][m] == 0)
251                                                   if ((mitem[i][j] != VAMPIRE) || (tmpitem != OMIRROR))
252                                                             if (tmpitem != OCLOSEDDOOR) {
253                                                                       w1[tmp] = (playerx - k) * (playerx - k) + (playery - m) * (playery - m);
254                                                                       w1x[tmp] = k;
255                                                                       w1y[tmp] = m;
256                                                             }
257                     }
258 
259           tmp = 0;
260           for (k = 1; k < 9; k++)
261                     if (w1[tmp] > w1[k])
262                               tmp = k;
263 
264           if (w1[tmp] < 10000)
265                     if ((i != w1x[tmp]) || (j != w1y[tmp]))
266                               mmove(i, j, w1x[tmp], w1y[tmp]);
267 }
268 
269 /*
270  * mmove(x,y,xd,yd) Function to actually perform the monster movement
271  *        int x,y,xd,yd;
272  *
273  * Enter with the from coordinates in (x,y) and the destination coordinates
274  * in (xd,yd).
275  */
276 static void
mmove(int aa,int bb,int cc,int dd)277 mmove(int aa, int bb, int cc, int dd)
278 {
279           int    tmp, i, flag;
280           const char *who = NULL;
281 
282           flag = 0;           /* set to 1 if monster hit by arrow trap */
283           if ((cc == playerx) && (dd == playery)) {
284                     hitplayer(aa, bb);
285                     moved[aa][bb] = 1;
286                     return;
287           }
288           i = item[cc][dd];
289           if ((i == OPIT) || (i == OTRAPDOOR))
290                     switch (mitem[aa][bb]) {
291                     case SPIRITNAGA:
292                     case PLATINUMDRAGON:
293                     case WRAITH:
294                     case VAMPIRE:
295                     case SILVERDRAGON:
296                     case POLTERGEIST:
297                     case DEMONLORD:
298                     case DEMONLORD + 1:
299                     case DEMONLORD + 2:
300                     case DEMONLORD + 3:
301                     case DEMONLORD + 4:
302                     case DEMONLORD + 5:
303                     case DEMONLORD + 6:
304                     case DEMONPRINCE:
305                               break;
306 
307                     default:
308                               mitem[aa][bb] = 0;  /* fell in a pit or trapdoor */
309                     };
310           tmp = mitem[cc][dd] = mitem[aa][bb];
311           if (i == OANNIHILATION) {
312                     if (tmp >= DEMONLORD + 3) {   /* demons dispel spheres */
313                               cursors();
314                               lprintf("\nThe %s dispels the sphere!", monster[tmp].name);
315                               rmsphere(cc, dd);   /* delete the sphere */
316                     } else
317                               i = tmp = mitem[cc][dd] = 0;
318           }
319           stealth[cc][dd] = 1;
320           if ((hitp[cc][dd] = hitp[aa][bb]) < 0)
321                     hitp[cc][dd] = 1;
322           mitem[aa][bb] = 0;
323           moved[cc][dd] = 1;
324           if (tmp == LEPRECHAUN)
325                     switch (i) {
326                     case OGOLDPILE:
327                     case OMAXGOLD:
328                     case OKGOLD:
329                     case ODGOLD:
330                     case ODIAMOND:
331                     case ORUBY:
332                     case OEMERALD:
333                     case OSAPPHIRE:
334                               item[cc][dd] = 0;   /* leprechaun takes gold */
335                     };
336 
337           if (tmp == TROLL)   /* if a troll regenerate him */
338                     if ((gltime & 1) == 0)
339                               if (monster[tmp].hitpoints > hitp[cc][dd])
340                                         hitp[cc][dd]++;
341 
342           if (i == OTRAPARROW) {        /* arrow hits monster */
343                     who = "An arrow";
344                     if ((hitp[cc][dd] -= rnd(10) + level) <= 0) {
345                               mitem[cc][dd] = 0;
346                               flag = 2;
347                     } else
348                               flag = 1;
349           }
350           if (i == ODARTRAP) {          /* dart hits monster */
351                     who = "A dart";
352                     if ((hitp[cc][dd] -= rnd(6)) <= 0) {
353                               mitem[cc][dd] = 0;
354                               flag = 2;
355                     } else
356                               flag = 1;
357           }
358           if (i == OTELEPORTER) {       /* monster hits teleport trap */
359                     flag = 3;
360                     fillmonst(mitem[cc][dd]);
361                     mitem[cc][dd] = 0;
362           }
363           if (c[BLINDCOUNT])
364                     return;             /* if blind don't show where monsters are          */
365           if (know[cc][dd] & 1) {
366                     if (flag)
367                               cursors();
368                     switch (flag) {
369                     case 1:
370                               lprintf("\n%s hits the %s", who, monster[tmp].name);
371                               beep();
372                               break;
373                     case 2:
374                               lprintf("\n%s hits and kills the %s",
375                                   who, monster[tmp].name);
376                               beep();
377                               break;
378                     case 3:
379                               lprintf("\nThe %s gets teleported", monster[tmp].name);
380                               beep();
381                               break;
382                     }
383           }
384           /*
385            * if (yrepcount>1) { know[aa][bb] &= 2;  know[cc][dd] &= 2; return;
386            * }
387            */
388           if (know[aa][bb] & 1)
389                     show1cell(aa, bb);
390           if (know[cc][dd] & 1)
391                     show1cell(cc, dd);
392 }
393 
394 /*
395  * movsphere()      Function to look for and move spheres of annihilation
396  *
397  * This function works on the sphere linked list, first duplicating the list
398  * (the act of moving changes the list), then processing each sphere in order
399  * to move it.  They eat anything in their way, including stairs, volcanic
400  * shafts, potions, etc, except for upper level demons, who can dispel
401  * spheres.
402  * No value is returned.
403  */
404 #define SPHMAX 20             /* maximum number of spheres movsphere can
405                                          * handle */
406 static void
movsphere(void)407 movsphere(void)
408 {
409           int    x, y, dir, len;
410           struct sphere *sp, *sp2;
411           struct sphere   sph[SPHMAX];
412 
413           /* first duplicate sphere list */
414           for (sp = 0, x = 0, sp2 = spheres; sp2; sp2 = sp2->p)       /* look through sphere
415                                                                                  * list */
416                     if (sp2->lev == level) {      /* only if this level */
417                               sph[x] = *sp2;
418                               sph[x++].p = 0;     /* copy the struct */
419                               if (x > 1)
420                                         sph[x - 2].p = &sph[x - 1];   /* link pointers */
421                     }
422           if (x)
423                     sp = sph; /* if any spheres, point to them */
424           else
425                     return;             /* no spheres */
426 
427           for (sp = sph; sp; sp = sp->p) {        /* look through sphere list */
428                     x = sp->x;
429                     y = sp->y;
430                     if (item[x][y] != OANNIHILATION)
431                               continue; /* not really there */
432                     if (--(sp->lifetime) < 0) {   /* has sphere run out of gas? */
433                               rmsphere(x, y);     /* delete sphere */
434                               continue;
435                     }
436                     switch (rnd((int) max(7, c[INTELLIGENCE] >> 1))) {          /* time to move the
437                                                                                            * sphere */
438                     case 1:
439                     case 2:   /* change direction to a random one */
440                               sp->dir = rnd(8);
441                               /* FALLTHROUGH */
442                     default:  /* move in normal direction */
443                               dir = sp->dir;
444                               len = sp->lifetime;
445                               rmsphere(x, y);
446                               newsphere(x + diroffx[dir], y + diroffy[dir], dir, len);
447                     };
448           }
449 }
450