1 /*        $NetBSD: monster.c,v 1.20 2021/05/02 12:50:45 rillig Exp $  */
2 
3 /*
4  * monster.c        Larn is copyrighted 1986 by Noah Morgan.
5  *
6  * This file contains the following functions:
7  * ----------------------------------------------------------------------------
8  *
9  * createmonster(monstno)     Function to create a monster next to the player
10  * int monstno;
11  *
12  * int cgood(x,y,itm,monst)Function to check location for emptiness
13  * int x,y,itm,monst;
14  *
15  * createitem(it,arg)                   Routine to place an item next to the player
16  * int it,arg;
17  *
18  * cast()                               Subroutine called by parse to cast a spell for the user
19  *
20  * speldamage(x)    Function to perform spell functions cast by the player
21  * int x;
22  *
23  * loseint()                  Routine to decrement your int (intelligence) if > 3
24  *
25  * isconfuse()      Routine to check to see if player is confused
26  *
27  * nospell(x,monst)Routine to return 1 if a spell doesn't affect a monster
28  * int x,monst;
29  *
30  * fullhit(xx)                Function to return full damage against a monst (aka web)
31  * int xx;
32  *
33  * direct(spnum,dam,str,arg)Routine to direct spell damage 1 square in 1 dir
34  * int spnum,dam,arg;
35  * char *str;
36  *
37  * godirect(spnum,dam,str,delay,cshow)  Function to perform missile attacks
38  * int spnum,dam,delay;
39  * char *str,cshow;
40  *
41  * ifblind(x,y)Routine to put "monster" or the monster name into lastmosnt
42  * int x,y;
43  *
44  * tdirect(spnum)             Routine to teleport away a monster
45  * int spnum;
46  *
47  * omnidirect(sp,dam,str)  Routine to damage all monsters 1 square from player
48  * int sp,dam;
49  * char *str;
50  *
51  * dirsub(x,y)                Routine to ask for direction, then modify x,y for it
52  * int *x,*y;
53  *
54  * vxy(x,y)                   Routine to verify/fix (*x,*y) for being within bounds
55  * int *x,*y;
56  *
57  * dirpoly(spnum)   Routine to ask for a direction and polymorph a monst
58  * int spnum;
59  *
60  * hitmonster(x,y) Function to hit a monster at the designated coordinates
61  * int x,y;
62  *
63  * hitm(x,y,amt)    Function to just hit a monster at a given coordinates
64  * int x,y,amt;
65  *
66  * hitplayer(x,y)   Function for the monster to hit the player from (x,y)
67  * int x,y;
68  *
69  * dropsomething(monst) Function to create an object when a monster dies
70  * int monst;
71  *
72  * dropgold(amount)           Function to drop some gold around player
73  * int amount;
74  *
75  * something(level)           Function to create a random item around player
76  * int level;
77  *
78  * newobject(lev,i)           Routine to return a randomly selected new object
79  * int lev,*i;
80  *
81  *  spattack(atckno,xx,yy)  Function to process special attacks from monsters
82  *   int atckno,xx,yy;
83  *
84  * checkloss(x) Routine to subtract hp from user and flag bottomline display
85  * int x;
86  *
87  * annihilate()   Routine to annihilate monsters around player, playerx,playery
88  *
89  * newsphere(x,y,dir,lifetime)  Function to create a new sphere of annihilation
90  * int x,y,dir,lifetime;
91  *
92  * rmsphere(x,y)    Function to delete a sphere of annihilation from list
93  * int x,y;
94  *
95  * sphboom(x,y)     Function to perform the effects of a sphere detonation
96  * int x,y;
97  *
98  * genmonst()                 Function to ask for monster and genocide from game
99  *
100  */
101 #include <sys/cdefs.h>
102 #ifndef lint
103 __RCSID("$NetBSD: monster.c,v 1.20 2021/05/02 12:50:45 rillig Exp $");
104 #endif                                  /* not lint */
105 
106 #include <string.h>
107 #include <stdlib.h>
108 #include <ctype.h>
109 #include "header.h"
110 #include "extern.h"
111 
112 struct isave {                          /* used for altar reality */
113           char            type;         /* 0=item,  1=monster */
114           char            id; /* item number or monster number */
115           short           arg;          /* the type of item or hitpoints of monster */
116 };
117 
118 static int cgood(int, int, int, int);
119 static void speldamage(int);
120 static void loseint(void);
121 static int isconfuse(void);
122 static int nospell(int, int);
123 static int fullhit(int);
124 static void direct(int, int, const char *, int);
125 static void ifblind(int, int);
126 static void tdirect(int);
127 static void omnidirect(int, int, const char *);
128 static int dirsub(int *, int *);
129 static void dirpoly(int);
130 static int hitm(int, int, int);
131 static void dropsomething(int);
132 static int spattack(int, int, int);
133 static void sphboom(int, int);
134 static void genmonst(void);
135 
136 /*
137  * createmonster(monstno)     Function to create a monster next to the player
138  *        int monstno;
139  *
140  * Enter with the monster number (1 to MAXMONST+8)
141  * Returns no value.
142  */
143 void
createmonster(int mon)144 createmonster(int mon)
145 {
146           int    x, y, k, i;
147           if (mon < 1 || mon > MAXMONST + 8) {    /* check for monster number
148                                                              * out of bounds */
149                     beep();
150                     lprintf("\ncan't createmonst(%ld)\n", (long) mon);
151                     nap(3000);
152                     return;
153           }
154           while (monster[mon].genocided && mon < MAXMONST)
155                     mon++;              /* genocided? */
156           for (k = rnd(8), i = -8; i < 0; i++, k++) {       /* choose direction,
157                                                                        * then try all */
158                     if (k > 8)
159                               k = 1;    /* wraparound the diroff arrays */
160                     x = playerx + diroffx[k];
161                     y = playery + diroffy[k];
162                     if (cgood(x, y, 0, 1)) {      /* if we can create here */
163                               mitem[x][y] = mon;
164                               hitp[x][y] = monster[mon].hitpoints;
165                               stealth[x][y] = know[x][y] = 0;
166                               switch (mon) {
167                               case ROTHE:
168                               case POLTERGEIST:
169                               case VAMPIRE:
170                                         stealth[x][y] = 1;
171                               };
172                               return;
173                     }
174           }
175 }
176 
177 /*
178  * int cgood(x,y,itm,monst)     Function to check location for emptiness
179  *        int x,y,itm,monst;
180  *
181  * Routine to return TRUE if a location does not have itm or monst there
182  * returns FALSE (0) otherwise
183  * Enter with itm or monst TRUE or FALSE if checking it
184  * Example:  if itm==TRUE check for no item at this location
185  *                    if monst==TRUE check for no monster at this location
186  * This routine will return FALSE if at a wall or the dungeon exit on level 1
187  */
188 static int
cgood(int x,int y,int theitem,int monst)189 cgood(int x, int y, int theitem, int monst)
190 {
191 #define itm __lose
192           if ((y >= 0) && (y <= MAXY - 1) && (x >= 0) && (x <= MAXX - 1))
193                     /* within bounds? */
194                     if (item[x][y] != OWALL) /* can't make anything on walls */
195                               /* is it free of items? */
196                               if (theitem == 0 || (item[x][y] == 0))
197                                         /* is it free of monsters? */
198                                         if (monst == 0 || (mitem[x][y] == 0))
199                                                   if ((level != 1) || (x != 33) ||
200                                                       (y != MAXY - 1))
201                                                             /* not exit to level 1 */
202                                                             return (1);
203           return (0);
204 }
205 
206 /*
207  * createitem(it,arg)         Routine to place an item next to the player
208  *        int it,arg;
209  *
210  * Enter with the item number and its argument (iven[], ivenarg[])
211  * Returns no value, thus we don't know about createitem() failures.
212  */
213 void
createitem(int it,int arg)214 createitem(int it, int arg)
215 {
216           int    x, y, k, i;
217           if (it >= MAXOBJ)
218                     return;             /* no such object */
219           for (k = rnd(8), i = -8; i < 0; i++, k++) {       /* choose direction,
220                                                                        * then try all */
221                     if (k > 8)
222                               k = 1;    /* wraparound the diroff arrays */
223                     x = playerx + diroffx[k];
224                     y = playery + diroffy[k];
225                     if (cgood(x, y, 1, 0)) {      /* if we can create here */
226                               item[x][y] = it;
227                               know[x][y] = 0;
228                               iarg[x][y] = arg;
229                               return;
230                     }
231           }
232 }
233 
234 /*
235  * cast()                     Subroutine called by parse to cast a spell for the user
236  *
237  * No arguments and no return value.
238  */
239 static char     eys[] = "\nEnter your spell: ";
240 void
cast(void)241 cast(void)
242 {
243           int    i, j, a, b, d;
244           cursors();
245           if (c[SPELLS] <= 0) {
246                     lprcat("\nYou don't have any spells!");
247                     return;
248           }
249           lprcat(eys);
250           --c[SPELLS];
251           while ((a = ttgetch()) == 'D') {
252                     seemagic(-1);
253                     cursors();
254                     lprcat(eys);
255           }
256           if (a == '\33')
257                     goto over;          /* to escape casting a spell   */
258           if ((b = ttgetch()) == '\33')
259                     goto over;          /* to escape casting a spell   */
260           if ((d = ttgetch()) == '\33') {
261 over:               lprcat(aborted);
262                     c[SPELLS]++;
263                     return;
264           }                             /* to escape casting a spell   */
265 #ifdef EXTRA
266           c[SPELLSCAST]++;
267 #endif
268           for (lprc('\n'), j = -1, i = 0; i < SPNUM; i++)   /* seq search for his
269                                                                        * spell, hash? */
270                     if ((spelcode[i][0] == a) && (spelcode[i][1] == b) && (spelcode[i][2] == d))
271                               if (spelknow[i]) {
272                                         speldamage(i);
273                                         j = 1;
274                                         i = SPNUM;
275                               }
276           if (j == -1)
277                     lprcat("  Nothing Happened ");
278           bottomline();
279 }
280 
281 /*
282  * speldamage(x)              Function to perform spell functions cast by the player
283  *        int x;
284  *
285  * Enter with the spell number, returns no value.
286  * Please insure that there are 2 spaces before all messages here
287  */
288 static void
speldamage(int x)289 speldamage(int x)
290 {
291           int    i, j, clev;
292           int             xl, xh, yl, yh;
293           u_char  *p, *kn, *pm;
294 
295           if (x >= SPNUM)
296                     return;             /* no such spell */
297           if (c[TIMESTOP]) {
298                     lprcat("  It didn't seem to work");
299                     return;
300           }                             /* not if time stopped */
301           clev = c[LEVEL];
302           if ((rnd(23) == 7) || (rnd(18) > c[INTELLIGENCE])) {
303                     lprcat("  It didn't work!");
304                     return;
305           }
306           if (clev * 3 + 2 < x) {
307                     lprcat("  Nothing happens.  You seem inexperienced at this");
308                     return;
309           }
310           switch (x) {
311                     /* ----- LEVEL 1 SPELLS ----- */
312 
313           case 0:
314                     if (c[PROTECTIONTIME] == 0)
315                               c[MOREDEFENSES] += 2;         /* protection field +2 */
316                     c[PROTECTIONTIME] += 250;
317                     return;
318 
319           case 1:
320                     i = rnd(((clev + 1) << 1)) + clev + 3;
321                     godirect(x, i, (clev >= 2) ? "  Your missiles hit the %s" : "  Your missile hit the %s", 100, '+'); /* magic missile */
322 
323                     return;
324 
325           case 2:
326                     if (c[DEXCOUNT] == 0)
327                               c[DEXTERITY] += 3;  /* dexterity         */
328                     c[DEXCOUNT] += 400;
329                     return;
330 
331           case 3:             /* sleep             */
332                     i = rnd(3) + 1;
333                     direct(x, fullhit(i),
334                            "  While the %s slept, you smashed it %ld times", i);
335                     return;
336 
337           case 4:             /* charm monster     */
338                     c[CHARMCOUNT] += c[CHARISMA] << 1;
339                     return;
340 
341           case 5:
342                     godirect(x, rnd(10) + 15 + clev, "  The sound damages the %s", 70, '@');        /* sonic spear */
343                     return;
344 
345                     /* ----- LEVEL 2 SPELLS ----- */
346 
347           case 6:             /* web                        */
348                     i = rnd(3) + 2;
349                     direct(x, fullhit(i),
350                            "  While the %s is entangled, you hit %ld times", i);
351                     return;
352 
353           case 7:
354                     if (c[STRCOUNT] == 0)
355                               c[STREXTRA] += 3;   /* strength          */
356                     c[STRCOUNT] += 150 + rnd(100);
357                     return;
358 
359           case 8:
360                     yl = playery - 5;   /* enlightenment */
361                     yh = playery + 6;
362                     xl = playerx - 15;
363                     xh = playerx + 16;
364                     vxy(&xl, &yl);
365                     vxy(&xh, &yh);      /* check bounds */
366                     for (i = yl; i <= yh; i++)    /* enlightenment     */
367                               for (j = xl; j <= xh; j++)
368                                         know[j][i] = 1;
369                     draws(xl, xh + 1, yl, yh + 1);
370                     return;
371 
372           case 9:
373                     raisehp(20 + (clev << 1));
374                     return;             /* healing */
375 
376           case 10:
377                     c[BLINDCOUNT] = 0;
378                     return;             /* cure blindness    */
379 
380           case 11:
381                     createmonster(makemonst(level + 1) + 8);
382                     return;
383 
384           case 12:
385                     if (rnd(11) + 7 <= c[WISDOM])
386                               direct(x, rnd(20) + 20 + clev, "  The %s believed!", 0);
387                     else
388                               lprcat("  It didn't believe the illusions!");
389                     return;
390 
391           case 13:            /* if he has the amulet of invisibility then
392                                          * add more time */
393                     for (j = i = 0; i < 26; i++)
394                               if (iven[i] == OAMULET)
395                                         j += 1 + ivenarg[i];
396                     c[INVISIBILITY] += (j << 7) + 12;
397                     return;
398 
399                     /* ----- LEVEL 3 SPELLS ----- */
400 
401           case 14:
402                     godirect(x, rnd(25 + clev) + 25 + clev, "  The fireball hits the %s", 40, '*');
403                     return;             /* fireball */
404 
405           case 15:
406                     godirect(x, rnd(25) + 20 + clev, "  Your cone of cold strikes the %s", 60, 'O');          /* cold */
407                     return;
408 
409           case 16:
410                     dirpoly(x);
411                     return;             /* polymorph */
412 
413           case 17:
414                     c[CANCELLATION] += 5 + clev;
415                     return;             /* cancellation      */
416 
417           case 18:
418                     c[HASTESELF] += 7 + clev;
419                     return;             /* haste self        */
420 
421           case 19:
422                     omnidirect(x, 30 + rnd(10), "  The %s gasps for air");      /* cloud kill */
423                     return;
424 
425           case 20:
426                     xh = min(playerx + 1, MAXX - 2);
427                     yh = min(playery + 1, MAXY - 2);
428                     for (i = max(playerx - 1, 1); i <= xh; i++)       /* vaporize rock */
429                               for (j = max(playery - 1, 1); j <= yh; j++) {
430                                         kn = &know[i][j];
431                                         pm = &mitem[i][j];
432                                         switch (*(p = &item[i][j])) {
433                                         case OWALL:
434                                                   if (level < MAXLEVEL + MAXVLEVEL - 1)
435                                                             *p = *kn = 0;
436                                                   break;
437 
438                                         case OSTATUE:
439                                                   if (c[HARDGAME] < 3) {
440                                                             *p = OBOOK;
441                                                             iarg[i][j] = level;
442                                                             *kn = 0;
443                                                   }
444                                                   break;
445 
446                                         case OTHRONE:
447                                                   *pm = GNOMEKING;
448                                                   *kn = 0;
449                                                   *p = OTHRONE2;
450                                                   hitp[i][j] = monster[GNOMEKING].hitpoints;
451                                                   break;
452 
453                                         case OALTAR:
454                                                   *pm = DEMONPRINCE;
455                                                   *kn = 0;
456                                                   hitp[i][j] = monster[DEMONPRINCE].hitpoints;
457                                                   break;
458                                         };
459                                         switch (*pm) {
460                                         case XORN:
461                                                   ifblind(i, j);
462                                                   hitm(i, j, 200);
463                                                   break;    /* Xorn takes damage from vpr */
464                                         }
465                               }
466                     return;
467 
468                     /* ----- LEVEL 4 SPELLS ----- */
469 
470           case 21:
471                     direct(x, 100 + clev, "  The %s shrivels up", 0); /* dehydration */
472                     return;
473 
474           case 22:
475                     godirect(x, rnd(25) + 20 + (clev << 1), "  A lightning bolt hits the %s", 1, '~');        /* lightning */
476                     return;
477 
478           case 23:
479                     i = min(c[HP] - 1, c[HPMAX] / 2);       /* drain life */
480                     direct(x, i + i, "", 0);
481                     c[HP] -= i;
482                     return;
483 
484           case 24:
485                     if (c[GLOBE] == 0)
486                               c[MOREDEFENSES] += 10;
487                     c[GLOBE] += 200;
488                     loseint();          /* globe of invulnerability */
489                     return;
490 
491           case 25:
492                     omnidirect(x, 32 + clev, "  The %s struggles for air in your flood!");          /* flood */
493                     return;
494 
495           case 26:
496                     if (rnd(151) == 63) {
497                               beep();
498                               lprcat("\nYour heart stopped!\n");
499                               nap(4000);
500                               died(270);
501                               return;
502                     }
503                     if (c[WISDOM] > rnd(10) + 10)
504                               direct(x, 2000, "  The %s's heart stopped", 0);   /* finger of death */
505                     else
506                               lprcat("  It didn't work");
507                     return;
508 
509                     /* ----- LEVEL 5 SPELLS ----- */
510 
511           case 27:
512                     c[SCAREMONST] += rnd(10) + clev;
513                     return;             /* scare monster */
514 
515           case 28:
516                     c[HOLDMONST] += rnd(10) + clev;
517                     return;             /* hold monster */
518 
519           case 29:
520                     c[TIMESTOP] += rnd(20) + (clev << 1);
521                     return;             /* time stop */
522 
523           case 30:
524                     tdirect(x);
525                     return;             /* teleport away */
526 
527           case 31:
528                     omnidirect(x, 35 + rnd(10) + clev, "  The %s cringes from the flame");          /* magic fire */
529                     return;
530 
531                     /* ----- LEVEL 6 SPELLS ----- */
532 
533           case 32:
534                     if ((rnd(23) == 5) && (wizard == 0)) {  /* sphere of
535                                                                        * annihilation */
536                               beep();
537                               lprcat("\nYou have been enveloped by the zone of nothingness!\n");
538                               nap(4000);
539                               died(258);
540                               return;
541                     }
542                     xl = playerx;
543                     yl = playery;
544                     loseint();
545                     i = dirsub(&xl, &yl);         /* get direction of sphere */
546                     newsphere(xl, yl, i, rnd(20) + 11);     /* make a sphere */
547                     return;
548 
549           case 33:
550                     genmonst();
551                     spelknow[33] = 0;   /* genocide */
552                     loseint();
553                     return;
554 
555           case 34:            /* summon demon */
556                     if (rnd(100) > 30) {
557                               direct(x, 150, "  The demon strikes at the %s", 0);
558                               return;
559                     }
560                     if (rnd(100) > 15) {
561                               lprcat("  Nothing seems to have happened");
562                               return;
563                     }
564                     lprcat("  The demon turned on you and vanished!");
565                     beep();
566                     i = rnd(40) + 30;
567                     lastnum = 277;
568                     losehp(i);          /* must say killed by a demon */
569                     return;
570 
571           case 35:            /* walk through walls */
572                     c[WTW] += rnd(10) + 5;
573                     return;
574 
575           case 36:            /* alter reality */
576                     {
577                               struct isave   *save;         /* pointer to item save
578                                                              * structure */
579                               int             sc;
580                               sc = 0;   /* # items saved */
581                               save = (struct isave *) malloc(sizeof(struct isave) * MAXX * MAXY * 2);
582                               for (j = 0; j < MAXY; j++)
583                                         for (i = 0; i < MAXX; i++) {  /* save all items and
584                                                                                  * monsters */
585                                                   xl = item[i][j];
586                                                   if (xl && xl != OWALL && xl != OANNIHILATION) {
587                                                             save[sc].type = 0;
588                                                             save[sc].id = item[i][j];
589                                                             save[sc++].arg = iarg[i][j];
590                                                   }
591                                                   if (mitem[i][j]) {
592                                                             save[sc].type = 1;
593                                                             save[sc].id = mitem[i][j];
594                                                             save[sc++].arg = hitp[i][j];
595                                                   }
596                                                   item[i][j] = OWALL;
597                                                   mitem[i][j] = 0;
598                                                   if (wizard)
599                                                             know[i][j] = 1;
600                                                   else
601                                                             know[i][j] = 0;
602                                         }
603                               eat(1, 1);
604                               if (level == 1)
605                                         item[33][MAXY - 1] = 0;
606                               for (j = rnd(MAXY - 2), i = 1; i < MAXX - 1; i++)
607                                         item[i][j] = 0;
608                               while (sc > 0) {    /* put objects back in level */
609                                         --sc;
610                                         if (save[sc].type == 0) {
611                                                   int             trys;
612                                                   for (trys = 100, i = j = 1; --trys > 0 && item[i][j]; i = rnd(MAXX - 1), j = rnd(MAXY - 1));
613                                                   if (trys) {
614                                                             item[i][j] = save[sc].id;
615                                                             iarg[i][j] = save[sc].arg;
616                                                   }
617                                         } else {  /* put monsters back in */
618                                                   int             trys;
619                                                   for (trys = 100, i = j = 1; --trys > 0 && (item[i][j] == OWALL || mitem[i][j]); i = rnd(MAXX - 1), j = rnd(MAXY - 1));
620                                                   if (trys) {
621                                                             mitem[i][j] = save[sc].id;
622                                                             hitp[i][j] = save[sc].arg;
623                                                   }
624                                         }
625                               }
626                               loseint();
627                               draws(0, MAXX, 0, MAXY);
628                               if (wizard == 0)
629                                         spelknow[36] = 0;
630                               free((char *) save);
631                               positionplayer();
632                               return;
633                     }
634 
635           case 37:            /* permanence */
636                     adjusttime(-99999L);
637                     spelknow[37] = 0;   /* forget */
638                     loseint();
639                     return;
640 
641           default:
642                     lprintf("  spell %ld not available!", (long) x);
643                     beep();
644                     return;
645           };
646 }
647 
648 /*
649  * loseint()                  Routine to subtract 1 from your int (intelligence) if > 3
650  *
651  * No arguments and no return value
652  */
653 static void
loseint(void)654 loseint(void)
655 {
656           if (--c[INTELLIGENCE] < 3)
657                     c[INTELLIGENCE] = 3;
658 }
659 
660 /*
661  * isconfuse()                Routine to check to see if player is confused
662  *
663  * This routine prints out a message saying "You can't aim your magic!"
664  * returns 0 if not confused, non-zero (time remaining confused) if confused
665  */
666 static int
isconfuse(void)667 isconfuse(void)
668 {
669           if (c[CONFUSE]) {
670                     lprcat(" You can't aim your magic!");
671                     beep();
672           }
673           return (c[CONFUSE]);
674 }
675 
676 /*
677  * nospell(x,monst) Routine to return 1 if a spell doesn't affect a monster
678  *        int x,monst;
679  *
680  * Subroutine to return 1 if the spell can't affect the monster
681  *   otherwise returns 0
682  * Enter with the spell number in x, and the monster number in monst.
683  */
684 static int
nospell(int x,int monst)685 nospell(int x, int monst)
686 {
687           int    tmp;
688           if (x >= SPNUM || monst >= MAXMONST + 8 || monst < 0 || x < 0)
689                     return (0);         /* bad spell or monst */
690           if ((tmp = spelweird[monst - 1][x]) == 0)
691                     return (0);
692           cursors();
693           lprc('\n');
694           lprintf(spelmes[tmp], monster[monst].name);
695           return (1);
696 }
697 
698 /*
699  * fullhit(xx)                Function to return full damage against a monster (aka web)
700  *        int xx;
701  *
702  * Function to return hp damage to monster due to a number of full hits
703  * Enter with the number of full hits being done
704  */
705 static int
fullhit(int xx)706 fullhit(int xx)
707 {
708           int    i;
709           if (xx < 0 || xx > 20)
710                     return (0);         /* fullhits are out of range */
711           if (c[LANCEDEATH])
712                     return (10000);     /* lance of death */
713           i = xx * ((c[WCLASS] >> 1) + c[STRENGTH] + c[STREXTRA] - c[HARDGAME] - 12 + c[MOREDAM]);
714           return ((i >= 1) ? i : xx);
715 }
716 
717 /*
718  * direct(spnum,dam,str,arg)  Routine to direct spell damage 1 square in 1 dir
719  *        int spnum,dam,arg;
720  *        char *str;
721  *
722  * Routine to ask for a direction to a spell and then hit the monster
723  * Enter with the spell number in spnum, the damage to be done in dam,
724  *   lprintf format string in str, and lprintf's argument in arg.
725  * Returns no value.
726  */
727 static void
direct(int spnum,int dam,const char * str,int arg)728 direct(int spnum, int dam, const char *str, int arg)
729 {
730           int             x, y;
731           int    m;
732           if (spnum < 0 || spnum >= SPNUM || str == 0)
733                     return;             /* bad arguments */
734           if (isconfuse())
735                     return;
736           dirsub(&x, &y);
737           m = mitem[x][y];
738           if (item[x][y] == OMIRROR) {
739                     if (spnum == 3) {   /* sleep */
740                               lprcat("You fall asleep! ");
741                               beep();
742           fool:
743                               arg += 2;
744                               while (arg-- > 0) {
745                                         parse2();
746                                         nap(1000);
747                               }
748                               return;
749                     } else if (spnum == 6) {      /* web */
750                               lprcat("You get stuck in your own web! ");
751                               beep();
752                               goto fool;
753                     } else {
754                               lastnum = 278;
755                               lprintf(str, "spell caster (that's you)", (long) arg);
756                               beep();
757                               losehp(dam);
758                               return;
759                     }
760           }
761           if (m == 0) {
762                     lprcat("  There wasn't anything there!");
763                     return;
764           }
765           ifblind(x, y);
766           if (nospell(spnum, m)) {
767                     lasthx = x;
768                     lasthy = y;
769                     return;
770           }
771           lprintf(str, lastmonst, (long) arg);
772           hitm(x, y, dam);
773 }
774 
775 /*
776  * godirect(spnum,dam,str,delay,cshow)            Function to perform missile attacks
777  *        int spnum,dam,delay;
778  *        char *str,cshow;
779  *
780  * Function to hit in a direction from a missile weapon and have it keep
781  * on going in that direction until its power is exhausted
782  * Enter with the spell number in spnum, the power of the weapon in hp,
783  *   lprintf format string in str, the # of milliseconds to delay between
784  *   locations in delay, and the character to represent the weapon in cshow.
785  * Returns no value.
786  */
787 void
godirect(int spnum,int dam,const char * str,int delay,int cshow_i)788 godirect(int spnum, int dam, const char *str, int delay, int cshow_i)
789 {
790           u_char  *p;
791           int    x, y, m;
792           int             dx, dy;
793           char cshow;
794 
795           /* truncate to char width in case it matters */
796           cshow = (char)cshow_i;
797 
798           if (spnum < 0 || spnum >= SPNUM || str == 0 || delay < 0)
799                     return;             /* bad args */
800           if (isconfuse())
801                     return;
802           dirsub(&dx, &dy);
803           x = dx;
804           y = dy;
805           dx = x - playerx;
806           dy = y - playery;
807           x = playerx;
808           y = playery;
809           while (dam > 0) {
810                     x += dx;
811                     y += dy;
812                     if ((x > MAXX - 1) || (y > MAXY - 1) || (x < 0) || (y < 0)) {
813                               dam = 0;
814                               break;    /* out of bounds */
815                     }
816                     if ((x == playerx) && (y == playery)) { /* if energy hits player */
817                               cursors();
818                               lprcat("\nYou are hit by your own magic!");
819                               beep();
820                               lastnum = 278;
821                               losehp(dam);
822                               return;
823                     }
824                     if (c[BLINDCOUNT] == 0) {     /* if not blind show effect */
825                               cursor(x + 1, y + 1);
826                               lprc(cshow);
827                               nap(delay);
828                               show1cell(x, y);
829                     }
830                     if ((m = mitem[x][y])) {      /* is there a monster there? */
831                               ifblind(x, y);
832                               if (nospell(spnum, m)) {
833                                         lasthx = x;
834                                         lasthy = y;
835                                         return;
836                               }
837                               cursors();
838                               lprc('\n');
839                               lprintf(str, lastmonst);
840                               dam -= hitm(x, y, dam);
841                               show1cell(x, y);
842                               nap(1000);
843                               x -= dx;
844                               y -= dy;
845                     } else
846                               switch (*(p = &item[x][y])) {
847                               case OWALL:
848                                         cursors();
849                                         lprc('\n');
850                                         lprintf(str, "wall");
851                                         if (dam >= 50 + c[HARDGAME])  /* enough damage? */
852                                                   if (level < MAXLEVEL + MAXVLEVEL - 1)   /* not on V3 */
853                                                             if ((x < MAXX - 1) && (y < MAXY - 1) && (x) && (y)) {
854                                                                       lprcat("  The wall crumbles");
855                                                   god3:               *p = 0;
856                                                   god:                know[x][y] = 0;
857                                                                       show1cell(x, y);
858                                                             }
859                     god2:               dam = 0;
860                                         break;
861 
862                               case OCLOSEDDOOR:
863                                         cursors();
864                                         lprc('\n');
865                                         lprintf(str, "door");
866                                         if (dam >= 40) {
867                                                   lprcat("  The door is blasted apart");
868                                                   goto god3;
869                                         }
870                                         goto god2;
871 
872                               case OSTATUE:
873                                         cursors();
874                                         lprc('\n');
875                                         lprintf(str, "statue");
876                                         if (c[HARDGAME] < 3)
877                                                   if (dam > 44) {
878                                                             lprcat("  The statue crumbles");
879                                                             *p = OBOOK;
880                                                             iarg[x][y] = level;
881                                                             goto god;
882                                                   }
883                                         goto god2;
884 
885                               case OTHRONE:
886                                         cursors();
887                                         lprc('\n');
888                                         lprintf(str, "throne");
889                                         if (dam > 39) {
890                                                   mitem[x][y] = GNOMEKING;
891                                                   hitp[x][y] = monster[GNOMEKING].hitpoints;
892                                                   *p = OTHRONE2;
893                                                   goto god;
894                                         }
895                                         goto god2;
896 
897                               case OMIRROR:
898                                         dx *= -1;
899                                         dy *= -1;
900                                         break;
901                               };
902                     dam -= 3 + (c[HARDGAME] >> 1);
903           }
904 }
905 
906 /*
907  * ifblind(x,y)     Routine to put "monster" or the monster name into lastmosnt
908  *        int x,y;
909  *
910  * Subroutine to copy the word "monster" into lastmonst if the player is blind
911  * Enter with the coordinates (x,y) of the monster
912  * Returns no value.
913  */
914 static void
ifblind(int x,int y)915 ifblind(int x, int y)
916 {
917           const char *p;
918 
919           vxy(&x, &y);                  /* verify correct x,y coordinates */
920           if (c[BLINDCOUNT]) {
921                     lastnum = 279;
922                     p = "monster";
923           } else {
924                     lastnum = mitem[x][y];
925                     p = monster[lastnum].name;
926           }
927           strcpy(lastmonst, p);
928 }
929 
930 /*
931  * tdirect(spnum)             Routine to teleport away a monster
932  *        int spnum;
933  *
934  * Routine to ask for a direction to a spell and then teleport away monster
935  * Enter with the spell number that wants to teleport away
936  * Returns no value.
937  */
938 static void
tdirect(int spnum)939 tdirect(int spnum)
940 {
941           int             x, y;
942           int    m;
943           if (spnum < 0 || spnum >= SPNUM)
944                     return;             /* bad args */
945           if (isconfuse())
946                     return;
947           dirsub(&x, &y);
948           if ((m = mitem[x][y]) == 0) {
949                     lprcat("  There wasn't anything there!");
950                     return;
951           }
952           ifblind(x, y);
953           if (nospell(spnum, m)) {
954                     lasthx = x;
955                     lasthy = y;
956                     return;
957           }
958           fillmonst(m);
959           mitem[x][y] = know[x][y] = 0;
960 }
961 
962 /*
963  * omnidirect(sp,dam,str)   Routine to damage all monsters 1 square from player
964  *        int sp,dam;
965  *        char *str;
966  *
967  * Routine to cast a spell and then hit the monster in all directions
968  * Enter with the spell number in sp, the damage done to wach square in dam,
969  *   and the lprintf string to identify the spell in str.
970  * Returns no value.
971  */
972 static void
omnidirect(int spnum,int dam,const char * str)973 omnidirect(int spnum, int dam, const char *str)
974 {
975           int    x, y, m;
976 
977           if (spnum < 0 || spnum >= SPNUM || str == 0)
978                     return;             /* bad args */
979           for (x = playerx - 1; x < playerx + 2; x++)
980                     for (y = playery - 1; y < playery + 2; y++) {
981                               if ((m = mitem[x][y]) != 0) {
982                                         if (nospell(spnum, m) == 0) {
983                                                   ifblind(x, y);
984                                                   cursors();
985                                                   lprc('\n');
986                                                   lprintf(str, lastmonst);
987                                                   hitm(x, y, dam);
988                                                   nap(800);
989                                         } else {
990                                                   lasthx = x;
991                                                   lasthy = y;
992                                         }
993                               }
994                     }
995 }
996 
997 /*
998  * static dirsub(x,y)                   Routine to ask for direction, then modify x,y for it
999  *        int *x,*y;
1000  *
1001  * Function to ask for a direction and modify an x,y for that direction
1002  * Enter with the origination coordinates in (x,y).
1003  * Returns index into diroffx[] (0-8).
1004  */
1005 static int
dirsub(int * x,int * y)1006 dirsub(int *x, int *y)
1007 {
1008           int    i;
1009           lprcat("\nIn What Direction? ");
1010           for (i = 0;;)
1011                     switch (ttgetch()) {
1012                     case 'b':
1013                               i++;
1014                               /* FALLTHROUGH */
1015                     case 'n':
1016                               i++;
1017                               /* FALLTHROUGH */
1018                     case 'y':
1019                               i++;
1020                               /* FALLTHROUGH */
1021                     case 'u':
1022                               i++;
1023                               /* FALLTHROUGH */
1024                     case 'h':
1025                               i++;
1026                               /* FALLTHROUGH */
1027                     case 'k':
1028                               i++;
1029                               /* FALLTHROUGH */
1030                     case 'l':
1031                               i++;
1032                               /* FALLTHROUGH */
1033                     case 'j':
1034                               i++;
1035                               /* FALLTHROUGH */
1036                               goto out;
1037                     };
1038 out:
1039           *x = playerx + diroffx[i];
1040           *y = playery + diroffy[i];
1041           vxy(x, y);
1042           return (i);
1043 }
1044 
1045 /*
1046  * vxy(x,y)            Routine to verify/fix coordinates for being within bounds
1047  *        int *x,*y;
1048  *
1049  * Function to verify x & y are within the bounds for a level
1050  * If *x or *y is not within the absolute bounds for a level, fix them so that
1051  *   they are on the level.
1052  * Returns TRUE if it was out of bounds, and the *x & *y in the calling
1053  * routine are affected.
1054  */
1055 int
vxy(int * x,int * y)1056 vxy(int *x, int *y)
1057 {
1058           int             flag = 0;
1059           if (*x < 0) {
1060                     *x = 0;
1061                     flag++;
1062           }
1063           if (*y < 0) {
1064                     *y = 0;
1065                     flag++;
1066           }
1067           if (*x >= MAXX) {
1068                     *x = MAXX - 1;
1069                     flag++;
1070           }
1071           if (*y >= MAXY) {
1072                     *y = MAXY - 1;
1073                     flag++;
1074           }
1075           return (flag);
1076 }
1077 
1078 /*
1079  * dirpoly(spnum)   Routine to ask for a direction and polymorph a monst
1080  *        int spnum;
1081  *
1082  * Subroutine to polymorph a monster and ask for the direction its in
1083  * Enter with the spell number in spmun.
1084  * Returns no value.
1085  */
1086 static void
dirpoly(int spnum)1087 dirpoly(int spnum)
1088 {
1089           int             x, y, m;
1090           if (spnum < 0 || spnum >= SPNUM)
1091                     return;             /* bad args */
1092           if (isconfuse())
1093                     return;             /* if he is confused, he can't aim his magic */
1094           dirsub(&x, &y);
1095           if (mitem[x][y] == 0) {
1096                     lprcat("  There wasn't anything there!");
1097                     return;
1098           }
1099           ifblind(x, y);
1100           if (nospell(spnum, mitem[x][y])) {
1101                     lasthx = x;
1102                     lasthy = y;
1103                     return;
1104           }
1105           while (monster[m = mitem[x][y] = rnd(MAXMONST + 7)].genocided);
1106           hitp[x][y] = monster[m].hitpoints;
1107           show1cell(x, y);    /* show the new monster */
1108 }
1109 
1110 /*
1111  * hitmonster(x,y)  Function to hit a monster at the designated coordinates
1112  *        int x,y;
1113  *
1114  * This routine is used for a bash & slash type attack on a monster
1115  * Enter with the coordinates of the monster in (x,y).
1116  * Returns no value.
1117  */
1118 void
hitmonster(int x,int y)1119 hitmonster(int x, int y)
1120 {
1121           int    tmp, monst, damag = 0, flag;
1122           if (c[TIMESTOP])
1123                     return;             /* not if time stopped */
1124           vxy(&x, &y);                  /* verify coordinates are within range */
1125           if ((monst = mitem[x][y]) == 0)
1126                     return;
1127           hit3flag = 1;
1128           ifblind(x, y);
1129           tmp = monster[monst].armorclass + c[LEVEL] + c[DEXTERITY] +
1130               c[WCLASS] / 4 - 12;
1131           cursors();
1132           /* need at least random chance to hit */
1133           if ((rnd(20) < tmp - c[HARDGAME]) || (rnd(71) < 5)) {
1134                     lprcat("\nYou hit");
1135                     flag = 1;
1136                     damag = fullhit(1);
1137                     if (damag < 9999)
1138                               damag = rnd(damag) + 1;
1139           } else {
1140                     lprcat("\nYou missed");
1141                     flag = 0;
1142           }
1143           lprcat(" the ");
1144           lprcat(lastmonst);
1145           if (flag)           /* if the monster was hit */
1146                     if ((monst == RUSTMONSTER) || (monst == DISENCHANTRESS) || (monst == CUBE))
1147                               if (c[WIELD] > 0)
1148                                         if (ivenarg[c[WIELD]] > -10) {
1149                                                   lprintf("\nYour weapon is dulled by the %s", lastmonst);
1150                                                   beep();
1151                                                   --ivenarg[c[WIELD]];
1152                                         }
1153           if (flag)
1154                     hitm(x, y, damag);
1155           if (monst == VAMPIRE)
1156                     if (hitp[x][y] < 25) {
1157                               mitem[x][y] = BAT;
1158                               know[x][y] = 0;
1159                     }
1160 }
1161 
1162 /*
1163  * hitm(x,y,amt)    Function to just hit a monster at a given coordinates
1164  *        int x,y,amt;
1165  *
1166  * Returns the number of hitpoints the monster absorbed
1167  * This routine is used to specifically damage a monster at a location (x,y)
1168  * Called by hitmonster(x,y)
1169  */
1170 static int
hitm(int x,int y,int amt)1171 hitm(int x, int y, int amt)
1172 {
1173           int    monst;
1174           int    hpoints, amt2;
1175           vxy(&x, &y);                  /* verify coordinates are within range */
1176           amt2 = amt;                   /* save initial damage so we can return it */
1177           monst = mitem[x][y];
1178           if (c[HALFDAM])
1179                     amt >>= 1;          /* if half damage curse adjust damage points */
1180           if (amt <= 0)
1181                     amt2 = amt = 1;
1182           lasthx = x;
1183           lasthy = y;
1184           stealth[x][y] = 1;  /* make sure hitting monst breaks stealth
1185                                          * condition */
1186           c[HOLDMONST] = 0;   /* hit a monster breaks hold monster spell         */
1187           switch (monst) {    /* if a dragon and orb(s) of dragon slaying        */
1188           case WHITEDRAGON:
1189           case REDDRAGON:
1190           case GREENDRAGON:
1191           case BRONZEDRAGON:
1192           case PLATINUMDRAGON:
1193           case SILVERDRAGON:
1194                     amt *= 1 + (c[SLAYING] << 1);
1195                     break;
1196           }
1197           /* invincible monster fix is here */
1198           if (hitp[x][y] > monster[monst].hitpoints)
1199                     hitp[x][y] = monster[monst].hitpoints;
1200           if ((hpoints = hitp[x][y]) <= amt) {
1201 #ifdef EXTRA
1202                     c[MONSTKILLED]++;
1203 #endif
1204                     lprintf("\nThe %s died!", lastmonst);
1205                     raiseexperience((long) monster[monst].experience);
1206                     amt = monster[monst].gold;
1207                     if (amt > 0)
1208                               dropgold(rnd(amt) + amt);
1209                     dropsomething(monst);
1210                     disappear(x, y);
1211                     bottomline();
1212                     return (hpoints);
1213           }
1214           hitp[x][y] = hpoints - amt;
1215           return (amt2);
1216 }
1217 
1218 /*
1219  * hitplayer(x,y)   Function for the monster to hit the player from (x,y)
1220  *        int x,y;
1221  *
1222  * Function for the monster to hit the player with monster at location x,y
1223  * Returns nothing of value.
1224  */
1225 void
hitplayer(int x,int y)1226 hitplayer(int x, int y)
1227 {
1228           int    dam, tmp, mster, bias;
1229           vxy(&x, &y);                  /* verify coordinates are within range */
1230           lastnum = mster = mitem[x][y];
1231           /*
1232            * spirit nagas and poltergeists do nothing if scarab of negate
1233            * spirit
1234            */
1235           if (c[NEGATESPIRIT] || c[SPIRITPRO])
1236                     if ((mster == POLTERGEIST) || (mster == SPIRITNAGA))
1237                               return;
1238           /* if undead and cube of undead control  */
1239           if (c[CUBEofUNDEAD] || c[UNDEADPRO])
1240                     if ((mster == VAMPIRE) || (mster == WRAITH) || (mster == ZOMBIE))
1241                               return;
1242           if ((know[x][y] & 1) == 0) {
1243                     know[x][y] = 1;
1244                     show1cell(x, y);
1245           }
1246           bias = (c[HARDGAME]) + 1;
1247           hitflag = hit2flag = hit3flag = 1;
1248           yrepcount = 0;
1249           cursors();
1250           ifblind(x, y);
1251           if (c[INVISIBILITY])
1252                     if (rnd(33) < 20) {
1253                               lprintf("\nThe %s misses wildly", lastmonst);
1254                               return;
1255                     }
1256           if (c[CHARMCOUNT])
1257                     if (rnd(30) + 5 * monster[mster].level - c[CHARISMA] < 30) {
1258                               lprintf("\nThe %s is awestruck at your magnificence!", lastmonst);
1259                               return;
1260                     }
1261           if (mster == BAT)
1262                     dam = 1;
1263           else {
1264                     dam = monster[mster].damage;
1265                     dam += rnd((int) ((dam < 1) ? 1 : dam)) + monster[mster].level;
1266           }
1267           tmp = 0;
1268           if (monster[mster].attack > 0)
1269                     if (((dam + bias + 8) > c[AC]) || (rnd((int) ((c[AC] > 0) ? c[AC] : 1)) == 1)) {
1270                               if (spattack(monster[mster].attack, x, y)) {
1271                                         flushall();
1272                                         return;
1273                               }
1274                               tmp = 1;
1275                               bias -= 2;
1276                               cursors();
1277                     }
1278           if (((dam + bias) > c[AC]) || (rnd((int) ((c[AC] > 0) ? c[AC] : 1)) == 1)) {
1279                     lprintf("\n  The %s hit you ", lastmonst);
1280                     tmp = 1;
1281                     if ((dam -= c[AC]) < 0)
1282                               dam = 0;
1283                     if (dam > 0) {
1284                               losehp(dam);
1285                               bottomhp();
1286                               flushall();
1287                     }
1288           }
1289           if (tmp == 0)
1290                     lprintf("\n  The %s missed ", lastmonst);
1291 }
1292 
1293 /*
1294  * dropsomething(monst)       Function to create an object when a monster dies
1295  *        int monst;
1296  *
1297  * Function to create an object near the player when certain monsters are killed
1298  * Enter with the monster number
1299  * Returns nothing of value.
1300  */
1301 static void
dropsomething(int monst)1302 dropsomething(int monst)
1303 {
1304           switch (monst) {
1305           case ORC:
1306           case NYMPH:
1307           case ELF:
1308           case TROGLODYTE:
1309           case TROLL:
1310           case ROTHE:
1311           case VIOLETFUNGI:
1312           case PLATINUMDRAGON:
1313           case GNOMEKING:
1314           case REDDRAGON:
1315                     something(level);
1316                     return;
1317 
1318           case LEPRECHAUN:
1319                     if (rnd(101) >= 75)
1320                               creategem();
1321                     if (rnd(5) == 1)
1322                               dropsomething(LEPRECHAUN);
1323                     return;
1324           }
1325 }
1326 
1327 /*
1328  * dropgold(amount)           Function to drop some gold around player
1329  *        int amount;
1330  *
1331  * Enter with the number of gold pieces to drop
1332  * Returns nothing of value.
1333  */
1334 void
dropgold(int amount)1335 dropgold(int amount)
1336 {
1337           if (amount > 250)
1338                     createitem(OMAXGOLD, amount / 100);
1339           else
1340                     createitem(OGOLDPILE, amount);
1341 }
1342 
1343 /*
1344  * something(level)           Function to create a random item around player
1345  *        int level;
1346  *
1347  * Function to create an item from a designed probability around player
1348  * Enter with the cave level on which something is to be dropped
1349  * Returns nothing of value.
1350  */
1351 void
something(int cavelevel)1352 something(int cavelevel)
1353 {
1354           int    j;
1355           int             i;
1356           if (cavelevel < 0 || cavelevel > MAXLEVEL + MAXVLEVEL)
1357                     return;             /* correct level? */
1358           if (rnd(101) < 8)
1359                     something(cavelevel);         /* possibly more than one item */
1360           j = newobject(cavelevel, &i);
1361           createitem(j, i);
1362 }
1363 
1364 /*
1365  * newobject(lev,i)           Routine to return a randomly selected new object
1366  *        int lev,*i;
1367  *
1368  * Routine to return a randomly selected object to be created
1369  * Returns the object number created, and sets *i for its argument
1370  * Enter with the cave level and a pointer to the items arg
1371  */
1372 static char     nobjtab[] = {
1373           0, OSCROLL, OSCROLL, OSCROLL, OSCROLL, OPOTION, OPOTION,
1374           OPOTION, OPOTION, OGOLDPILE, OGOLDPILE, OGOLDPILE, OGOLDPILE,
1375           OBOOK, OBOOK, OBOOK, OBOOK, ODAGGER, ODAGGER, ODAGGER,
1376           OLEATHER, OLEATHER, OLEATHER, OREGENRING, OPROTRING,
1377           OENERGYRING, ODEXRING, OSTRRING, OSPEAR, OBELT, ORING,
1378           OSTUDLEATHER, OSHIELD, OFLAIL, OCHAIN, O2SWORD, OPLATE,
1379           OLONGSWORD};
1380 
1381 int
newobject(int lev,int * i)1382 newobject(int lev, int *i)
1383 {
1384           int    tmp = 32, j;
1385           if (level < 0 || level > MAXLEVEL + MAXVLEVEL)
1386                     return (0);         /* correct level? */
1387           if (lev > 6)
1388                     tmp = 37;
1389           else if (lev > 4)
1390                     tmp = 35;
1391           j = nobjtab[tmp = rnd(tmp)];  /* the object type */
1392           switch (tmp) {
1393           case 1:
1394           case 2:
1395           case 3:
1396           case 4:
1397                     *i = newscroll();
1398                     break;
1399           case 5:
1400           case 6:
1401           case 7:
1402           case 8:
1403                     *i = newpotion();
1404                     break;
1405           case 9:
1406           case 10:
1407           case 11:
1408           case 12:
1409                     *i = rnd((lev + 1) * 10) + lev * 10 + 10;
1410                     break;
1411           case 13:
1412           case 14:
1413           case 15:
1414           case 16:
1415                     *i = lev;
1416                     break;
1417           case 17:
1418           case 18:
1419           case 19:
1420                     if (!(*i = newdagger()))
1421                               return (0);
1422                     break;
1423           case 20:
1424           case 21:
1425           case 22:
1426                     if (!(*i = newleather()))
1427                               return (0);
1428                     break;
1429           case 23:
1430           case 32:
1431           case 35:
1432                     *i = rund(lev / 3 + 1);
1433                     break;
1434           case 24:
1435           case 26:
1436                     *i = rnd(lev / 4 + 1);
1437                     break;
1438           case 25:
1439                     *i = rund(lev / 4 + 1);
1440                     break;
1441           case 27:
1442                     *i = rnd(lev / 2 + 1);
1443                     break;
1444           case 30:
1445           case 33:
1446                     *i = rund(lev / 2 + 1);
1447                     break;
1448           case 28:
1449                     *i = rund(lev / 3 + 1);
1450                     if (*i == 0)
1451                               return (0);
1452                     break;
1453           case 29:
1454           case 31:
1455                     *i = rund(lev / 2 + 1);
1456                     if (*i == 0)
1457                               return (0);
1458                     break;
1459           case 34:
1460                     *i = newchain();
1461                     break;
1462           case 36:
1463                     *i = newplate();
1464                     break;
1465           case 37:
1466                     *i = newsword();
1467                     break;
1468           }
1469           return (j);
1470 }
1471 
1472 /*
1473  *  spattack(atckno,xx,yy) Function to process special attacks from monsters
1474  *        int atckno,xx,yy;
1475  *
1476  * Enter with the special attack number, and the coordinates (xx,yy)
1477  *        of the monster that is special attacking
1478  * Returns 1 if must do a show1cell(xx,yy) upon return, 0 otherwise
1479  *
1480  * atckno   monster     effect
1481  * ---------------------------------------------------
1482  * 0      none
1483  * 1      rust monster        eat armor
1484  * 2      hell hound          breathe light fire
1485  * 3      dragon              breathe fire
1486  * 4      giant centipede     weakening sing
1487  * 5      white dragon        cold breath
1488  * 6      wraith              drain level
1489  * 7      waterlord water gusher
1490  * 8      leprechaun          steal gold
1491  * 9      disenchantress      disenchant weapon or armor
1492  * 10     ice lizard          hits with barbed tail
1493  * 11     umber hulk          confusion
1494  * 12     spirit naga         cast spells         taken from special attacks
1495  * 13     platinum dragon     psionics
1496  * 14     nymph               steal objects
1497  * 15     bugbear             bite
1498  * 16     osequip             bite
1499  *
1500  * char rustarm[ARMORTYPES][2];
1501  * special array for maximum rust damage to armor from rustmonster
1502  * format is: { armor type , minimum attribute
1503  */
1504 #define ARMORTYPES 6
1505 static char     rustarm[ARMORTYPES][2] = {
1506           { OSTUDLEATHER, -2 },
1507           { ORING, -4 },
1508           { OCHAIN, -5 },
1509           { OSPLINT, -6 },
1510           { OPLATE, -8 },
1511           { OPLATEARMOR, -9}
1512 };
1513 static char     spsel[] = {1, 2, 3, 5, 6, 8, 9, 11, 13, 14};
1514 static int
spattack(int x,int xx,int yy)1515 spattack(int x, int xx, int yy)
1516 {
1517           int    i, j = 0, k, m;
1518           const char *p = NULL;
1519 
1520           if (c[CANCELLATION])
1521                     return (0);
1522           vxy(&xx, &yy);                /* verify x & y coordinates */
1523           switch (x) {
1524           case 1:             /* rust your armor, j=1 when rusting has occurred */
1525                     m = k = c[WEAR];
1526                     if ((i = c[SHIELD]) != -1) {
1527                               if (--ivenarg[i] < -1)
1528                                         ivenarg[i] = -1;
1529                               else
1530                                         j = 1;
1531                     }
1532                     if ((j == 0) && (k != -1)) {
1533                               m = iven[k];
1534                               for (i = 0; i < ARMORTYPES; i++)
1535                                         /* find his armor in table */
1536                                         if (m == rustarm[i][0]) {
1537                                                   if (--ivenarg[k] < rustarm[i][1])
1538                                                             ivenarg[k] = rustarm[i][1];
1539                                                   else
1540                                                             j = 1;
1541                                                   break;
1542                                         }
1543                     }
1544                     if (j == 0)         /* if rusting did not occur */
1545                               switch (m) {
1546                               case OLEATHER:
1547                                         p = "\nThe %s hit you -- You're lucky you have leather on";
1548                                         break;
1549                               case OSSPLATE:
1550                                         p = "\nThe %s hit you -- You're fortunate to have stainless steel armor!";
1551                                         break;
1552                               }
1553                     else {
1554                               beep();
1555                               p = "\nThe %s hit you -- your armor feels weaker";
1556                     }
1557                     break;
1558 
1559           case 2:
1560                     i = rnd(15) + 8 - c[AC];
1561 spout:              p = "\nThe %s breathes fire at you!";
1562                     if (c[FIRERESISTANCE])
1563                               p = "\nThe %s's flame doesn't faze you!";
1564                     else
1565 spout2:   if (p) {
1566                               lprintf(p, lastmonst);
1567                               beep();
1568                     }
1569                     checkloss(i);
1570                     return (0);
1571 
1572           case 3:
1573                     i = rnd(20) + 25 - c[AC];
1574                     goto spout;
1575 
1576           case 4:
1577                     if (c[STRENGTH] > 3) {
1578                               p = "\nThe %s stung you!  You feel weaker";
1579                               beep();
1580                               --c[STRENGTH];
1581                     } else
1582                               p = "\nThe %s stung you!";
1583                     break;
1584 
1585           case 5:
1586                     p = "\nThe %s blasts you with his cold breath";
1587                     i = rnd(15) + 18 - c[AC];
1588                     goto spout2;
1589 
1590           case 6:
1591                     lprintf("\nThe %s drains you of your life energy!", lastmonst);
1592                     loselevel();
1593                     beep();
1594                     return (0);
1595 
1596           case 7:
1597                     p = "\nThe %s got you with a gusher!";
1598                     i = rnd(15) + 25 - c[AC];
1599                     goto spout2;
1600 
1601           case 8:
1602                     if (c[NOTHEFT])
1603                               return (0);         /* he has a device of no theft */
1604                     if (c[GOLD]) {
1605                               p = "\nThe %s hit you -- Your purse feels lighter";
1606                               if (c[GOLD] > 32767)
1607                                         c[GOLD] >>= 1;
1608                               else
1609                                         c[GOLD] -= rnd((int) (1 + (c[GOLD] >> 1)));
1610                               if (c[GOLD] < 0)
1611                                         c[GOLD] = 0;
1612                     } else
1613                               p = "\nThe %s couldn't find any gold to steal";
1614                     lprintf(p, lastmonst);
1615                     disappear(xx, yy);
1616                     beep();
1617                     bottomgold();
1618                     return (1);
1619 
1620           case 9:
1621                     for (j = 50;;) {/* disenchant */
1622                               i = rund(26);
1623                               m = iven[i];        /* randomly select item */
1624                               if (m > 0 && ivenarg[i] > 0 && m != OSCROLL && m != OPOTION) {
1625                                         if ((ivenarg[i] -= 3) < 0)
1626                                                   ivenarg[i] = 0;
1627                                         lprintf("\nThe %s hits you -- you feel a sense of loss", lastmonst);
1628                                         srcount = 0;
1629                                         beep();
1630                                         show3(i);
1631                                         bottomline();
1632                                         return (0);
1633                               }
1634                               if (--j <= 0) {
1635                                         p = "\nThe %s nearly misses";
1636                                         break;
1637                               }
1638                               break;
1639                     }
1640                     break;
1641 
1642           case 10:
1643                     p = "\nThe %s hit you with his barbed tail";
1644                     i = rnd(25) - c[AC];
1645                     goto spout2;
1646 
1647           case 11:
1648                     p = "\nThe %s has confused you";
1649                     beep();
1650                     c[CONFUSE] += 10 + rnd(10);
1651                     break;
1652 
1653           case 12:            /* performs any number of other special
1654                                          * attacks           */
1655                     return (spattack(spsel[rund(10)], xx, yy));
1656 
1657           case 13:
1658                     p = "\nThe %s flattens you with his psionics!";
1659                     i = rnd(15) + 30 - c[AC];
1660                     goto spout2;
1661 
1662           case 14:
1663                     if (c[NOTHEFT])
1664                               return (0);         /* he has device of no theft */
1665                     if (emptyhanded() == 1) {
1666                               p = "\nThe %s couldn't find anything to steal";
1667                               break;
1668                     }
1669                     lprintf("\nThe %s picks your pocket and takes:", lastmonst);
1670                     beep();
1671                     if (stealsomething() == 0)
1672                               lprcat("  nothing");
1673                     disappear(xx, yy);
1674                     bottomline();
1675                     return (1);
1676 
1677           case 15:
1678                     i = rnd(10) + 5 - c[AC];
1679 spout3:   p = "\nThe %s bit you!";
1680                     goto spout2;
1681 
1682           case 16:
1683                     i = rnd(15) + 10 - c[AC];
1684                     goto spout3;
1685           };
1686           if (p) {
1687                     lprintf(p, lastmonst);
1688                     bottomline();
1689           }
1690           return (0);
1691 }
1692 
1693 /*
1694  * checkloss(x) Routine to subtract hp from user and flag bottomline display
1695  *        int x;
1696  *
1697  * Routine to subtract hitpoints from the user and flag the bottomline display
1698  * Enter with the number of hit points to lose
1699  * Note: if x > c[HP] this routine could kill the player!
1700  */
1701 void
checkloss(int x)1702 checkloss(int x)
1703 {
1704           if (x > 0) {
1705                     losehp(x);
1706                     bottomhp();
1707           }
1708 }
1709 
1710 /*
1711  * annihilate()     Routine to annihilate all monsters around player (playerx,playery)
1712  *
1713  * Gives player experience, but no dropped objects
1714  * Returns the experience gained from all monsters killed
1715  */
1716 int
annihilate(void)1717 annihilate(void)
1718 {
1719           int             i, j;
1720           long   k;
1721           u_char  *p;
1722           for (k = 0, i = playerx - 1; i <= playerx + 1; i++)
1723                     for (j = playery - 1; j <= playery + 1; j++)
1724                               if (!vxy(&i, &j)) { /* if not out of bounds */
1725                                         if (*(p = &mitem[i][j])) {    /* if a monster there */
1726                                                   if (*p < DEMONLORD + 2) {
1727                                                             k += monster[*p].experience;
1728                                                             *p = know[i][j] = 0;
1729                                                   } else {
1730                                                             lprintf("\nThe %s barely escapes being annihilated!", monster[*p].name);
1731                                                             hitp[i][j] = (hitp[i][j] >> 1) + 1;     /* lose half hit points */
1732                                                   }
1733                                         }
1734                               }
1735           if (k > 0) {
1736                     lprcat("\nYou hear loud screams of agony!");
1737                     raiseexperience((long) k);
1738           }
1739           return (k);
1740 }
1741 
1742 /*
1743  * newsphere(x,y,dir,lifetime)  Function to create a new sphere of annihilation
1744  *        int x,y,dir,lifetime;
1745  *
1746  * Enter with the coordinates of the sphere in x,y
1747  *   the direction (0-8 diroffx format) in dir, and the lifespan of the
1748  *   sphere in lifetime (in turns)
1749  * Returns the number of spheres currently in existence
1750  */
1751 int
newsphere(int x,int y,int dir,int life)1752 newsphere(int x, int y, int dir, int life)
1753 {
1754           int             m;
1755           struct sphere  *sp;
1756           if (((sp = (struct sphere *) malloc(sizeof(struct sphere)))) == 0)
1757                     return (c[SPHCAST]);          /* can't malloc, therefore failure */
1758           if (dir >= 9)
1759                     dir = 0;  /* no movement if direction not found */
1760           if (level == 0)
1761                     vxy(&x, &y);        /* don't go out of bounds */
1762           else {
1763                     if (x < 1)
1764                               x = 1;
1765                     if (x >= MAXX - 1)
1766                               x = MAXX - 2;
1767                     if (y < 1)
1768                               y = 1;
1769                     if (y >= MAXY - 1)
1770                               y = MAXY - 2;
1771           }
1772           if ((m = mitem[x][y]) >= DEMONLORD + 4) {         /* demons dispel spheres */
1773                     know[x][y] = 1;
1774                     show1cell(x, y);/* show the demon (ha ha) */
1775                     cursors();
1776                     lprintf("\nThe %s dispels the sphere!", monster[m].name);
1777                     beep();
1778                     rmsphere(x, y);     /* remove any spheres that are here */
1779                     free(sp);
1780                     return (c[SPHCAST]);
1781           }
1782           if (m == DISENCHANTRESS) {    /* disenchantress cancels spheres */
1783                     cursors();
1784                     lprintf("\nThe %s causes cancellation of the sphere!", monster[m].name);
1785                     beep();
1786 boom:               sphboom(x, y);      /* blow up stuff around sphere */
1787                     rmsphere(x, y);     /* remove any spheres that are here */
1788                     free(sp);
1789                     return (c[SPHCAST]);
1790           }
1791           if (c[CANCELLATION]) {        /* cancellation cancels spheres */
1792                     cursors();
1793                     lprcat("\nAs the cancellation takes effect, you hear a great earth shaking blast!");
1794                     beep();
1795                     goto boom;
1796           }
1797           if (item[x][y] == OANNIHILATION) {      /* collision of spheres
1798                                                              * detonates spheres */
1799                     cursors();
1800                     lprcat("\nTwo spheres of annihilation collide! You hear a great earth shaking blast!");
1801                     beep();
1802                     rmsphere(x, y);
1803                     goto boom;
1804           }
1805           if (playerx == x && playery == y) {     /* collision of sphere and
1806                                                              * player! */
1807                     cursors();
1808                     lprcat("\nYou have been enveloped by the zone of nothingness!\n");
1809                     beep();
1810                     rmsphere(x, y);     /* remove any spheres that are here */
1811                     nap(4000);
1812                     died(258);
1813           }
1814           item[x][y] = OANNIHILATION;
1815           mitem[x][y] = 0;
1816           know[x][y] = 1;
1817           show1cell(x, y);    /* show the new sphere */
1818           sp->x = x;
1819           sp->y = y;
1820           sp->lev = level;
1821           sp->dir = dir;
1822           sp->lifetime = life;
1823           sp->p = 0;
1824           if (spheres == 0)
1825                     spheres = sp;       /* if first node in the sphere list */
1826           else {                        /* add sphere to beginning of linked list */
1827                     sp->p = spheres;
1828                     spheres = sp;
1829           }
1830           return (++c[SPHCAST]);        /* one more sphere in the world */
1831 }
1832 
1833 /*
1834  * rmsphere(x,y)              Function to delete a sphere of annihilation from list
1835  *        int x,y;
1836  *
1837  * Enter with the coordinates of the sphere (on current level)
1838  * Returns the number of spheres currently in existence
1839  */
1840 int
rmsphere(int x,int y)1841 rmsphere(int x, int y)
1842 {
1843           struct sphere *sp, *sp2 = 0;
1844           for (sp = spheres; sp; sp2 = sp, sp = sp->p)
1845                     if (level == sp->lev)         /* is sphere on this level? */
1846                               if ((x == sp->x) && (y == sp->y)) {     /* locate sphere at this
1847                                                                                  * location */
1848                                         item[x][y] = mitem[x][y] = 0;
1849                                         know[x][y] = 1;
1850                                         show1cell(x, y);    /* show the now missing
1851                                                                        * sphere */
1852                                         --c[SPHCAST];
1853                                         if (sp == spheres) {
1854                                                   sp2 = sp;
1855                                                   spheres = sp->p;
1856                                                   free((char *) sp2);
1857                                         } else {
1858                                                   if (sp2)
1859                                                             sp2->p = sp->p;
1860                                                   free((char *) sp);
1861                                         }
1862                                         break;
1863                               }
1864           return (c[SPHCAST]);          /* return number of spheres in the world */
1865 }
1866 
1867 /*
1868  * sphboom(x,y)     Function to perform the effects of a sphere detonation
1869  *        int x,y;
1870  *
1871  * Enter with the coordinates of the blast, Returns no value
1872  */
1873 static void
sphboom(int x,int y)1874 sphboom(int x, int y)
1875 {
1876           int    i, j;
1877           if (c[HOLDMONST])
1878                     c[HOLDMONST] = 1;
1879           if (c[CANCELLATION])
1880                     c[CANCELLATION] = 1;
1881           for (j = max(1, x - 2); j < min(x + 3, MAXX - 1); j++)
1882                     for (i = max(1, y - 2); i < min(y + 3, MAXY - 1); i++) {
1883                               item[j][i] = mitem[j][i] = 0;
1884                               show1cell(j, i);
1885                               if (playerx == j && playery == i) {
1886                                         cursors();
1887                                         beep();
1888                                         lprcat("\nYou were too close to the sphere!");
1889                                         nap(3000);
1890                                         died(283);          /* player killed in explosion */
1891                               }
1892                     }
1893 }
1894 
1895 /*
1896  * genmonst()                 Function to ask for monster and genocide from game
1897  *
1898  * This is done by setting a flag in the monster[] structure
1899  */
1900 static void
genmonst(void)1901 genmonst(void)
1902 {
1903           int    i, j;
1904           cursors();
1905           lprcat("\nGenocide what monster? ");
1906           for (i = 0; (!isalpha(i)) && (i != ' '); i = ttgetch());
1907           lprc(i);
1908           for (j = 0; j < MAXMONST; j++)          /* search for the monster type */
1909                     if (monstnamelist[j] == i) {  /* have we found it? */
1910                               monster[j].genocided = 1;     /* genocided from game */
1911                               lprintf("  There will be no more %s's", monster[j].name);
1912                               /* now wipe out monsters on this level */
1913                               newcavelevel(level);
1914                               draws(0, MAXX, 0, MAXY);
1915                               bot_linex();
1916                               return;
1917                     }
1918           lprcat("  You sense failure!");
1919 }
1920