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