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