xref: /dragonfly/games/larn/monster.c (revision 13fca3df026b870c4d7c27704ee872f20e9f885b)
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
createmonster(int mon)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 (mon < MAXMONST && monster[mon].genocided)  /* 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
cgood(int x,int y,int itm,int monst)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
createitem(int it,int arg)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
cast(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
speldamage(int x)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
loseint(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
isconfuse(void)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
nospell(int x,int monst)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
fullhit(int xx)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
direct(int spnum,int dam,const char * str,int arg)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
godirect(int spnum,int dam,const char * str,int delay,char cshow)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
ifblind(int x,int y)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
tdirect(int spnum)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
omnidirect(int spnum,int dam,const char * str)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
dirsub(int * x,int * y)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                               /* FALLTHROUGH */
995                     case 'n':
996                               i++;
997                               /* FALLTHROUGH */
998                     case 'y':
999                               i++;
1000                               /* FALLTHROUGH */
1001                     case 'u':
1002                               i++;
1003                               /* FALLTHROUGH */
1004                     case 'h':
1005                               i++;
1006                               /* FALLTHROUGH */
1007                     case 'k':
1008                               i++;
1009                               /* FALLTHROUGH */
1010                     case 'l':
1011                               i++;
1012                               /* FALLTHROUGH */
1013                     case 'j':
1014                               i++;
1015                               goto out;
1016                     }
1017 out:
1018           *x = playerx + diroffx[i];
1019           *y = playery + diroffy[i];
1020           vxy(x, y);
1021           return (i);
1022 }
1023 
1024 /*
1025  *        vxy(x,y)     Routine to verify/fix coordinates for being within bounds
1026  *                  int *x,*y;
1027  *
1028  *        Function to verify x & y are within the bounds for a level
1029  *        If *x or *y is not within the absolute bounds for a level, fix them so that
1030  *          they are on the level.
1031  *        Returns TRUE if it was out of bounds, and the *x & *y in the calling
1032  *        routine are affected.
1033  */
1034 int
vxy(int * x,int * y)1035 vxy(int *x, int *y)
1036 {
1037           int flag = 0;
1038           if (*x < 0) {
1039                     *x = 0;
1040                     flag++;
1041           }
1042           if (*y < 0) {
1043                     *y = 0;
1044                     flag++;
1045           }
1046           if (*x >= MAXX) {
1047                     *x = MAXX - 1;
1048                     flag++;
1049           }
1050           if (*y >= MAXY) {
1051                     *y = MAXY - 1;
1052                     flag++;
1053           }
1054           return (flag);
1055 }
1056 
1057 /*
1058  *        dirpoly(spnum)                Routine to ask for a direction and polymorph a monst
1059  *                  int spnum;
1060  *
1061  *        Subroutine to polymorph a monster and ask for the direction its in
1062  *        Enter with the spell number in spmun.
1063  *        Returns no value.
1064  */
1065 static void
dirpoly(int spnum)1066 dirpoly(int spnum)
1067 {
1068           int x, y, m;
1069           if (spnum < 0 || spnum >= SPNUM)        /* bad args */
1070                     return;
1071           if (isconfuse())    /* if he is confused, he can't aim his magic */
1072                     return;
1073           dirsub(&x, &y);
1074           if (mitem[x][y] == 0) {
1075                     lprcat("  There wasn't anything there!");
1076                     return;
1077           }
1078           ifblind(x, y);
1079           if (nospell(spnum, mitem[x][y])) {
1080                     lasthx = x;
1081                     lasthy = y;
1082                     return;
1083           }
1084           while (monster[m = mitem[x][y] = rnd(MAXMONST + 7)].genocided)
1085                     ;
1086           hitp[x][y] = monster[m].hitpoints;
1087           show1cell(x, y);    /* show the new monster */
1088 }
1089 
1090 /*
1091  *        hitmonster(x,y)     Function to hit a monster at the designated coordinates
1092  *                  int x,y;
1093  *
1094  *        This routine is used for a bash & slash type attack on a monster
1095  *        Enter with the coordinates of the monster in (x,y).
1096  *        Returns no value.
1097  */
1098 void
hitmonster(int x,int y)1099 hitmonster(int x, int y)
1100 {
1101           int tmp, monst, damag = 0, flag;
1102           if (c[TIMESTOP])    /* not if time stopped */
1103                     return;
1104           vxy(&x, &y);                  /* verify coordinates are within range */
1105           if ((monst = mitem[x][y]) == 0)
1106                     return;
1107           hit3flag = 1;
1108           ifblind(x, y);
1109           tmp = monster[monst].armorclass + c[LEVEL] + c[DEXTERITY] +
1110               c[WCLASS] / 4 - 12;
1111           cursors();
1112           /* need at least random chance to hit */
1113           if ((rnd(20) < tmp - c[HARDGAME]) || (rnd(71) < 5)) {
1114                     lprcat("\nYou hit");
1115                     flag = 1;
1116                     damag = fullhit(1);
1117                     if (damag < 9999)
1118                               damag = rnd(damag) + 1;
1119           } else {
1120                     lprcat("\nYou missed");
1121                     flag = 0;
1122           }
1123           lprcat(" the ");
1124           lprcat(lastmonst);
1125           if (flag)           /* if the monster was hit */
1126                     if ((monst == RUSTMONSTER) || (monst == DISENCHANTRESS) || (monst == CUBE))
1127                               if (c[WIELD] > 0)
1128                                         if (ivenarg[c[WIELD]] > -10) {
1129                                                   lprintf("\nYour weapon is dulled by the %s", lastmonst);
1130                                                   beep();
1131                                                   --ivenarg[c[WIELD]];
1132                                         }
1133           if (flag)
1134                     hitm(x, y, damag);
1135           if (monst == VAMPIRE)
1136                     if (hitp[x][y] < 25) {
1137                               mitem[x][y] = BAT;
1138                               know[x][y] = 0;
1139                     }
1140 }
1141 
1142 /*
1143  *        hitm(x,y,amt)                 Function to just hit a monster at a given coordinates
1144  *                  int x,y,amt;
1145  *
1146  *        Returns the number of hitpoints the monster absorbed
1147  *        This routine is used to specifically damage a monster at a location (x,y)
1148  *        Called by hitmonster(x,y)
1149  */
1150 int
hitm(int x,int y,int amt)1151 hitm(int x, int y, int amt)
1152 {
1153           int monst;
1154           int hpoints, amt2;
1155           vxy(&x, &y);                  /* verify coordinates are within range */
1156           amt2 = amt;                   /* save initial damage so we can return it */
1157           monst = mitem[x][y];
1158           if (c[HALFDAM])               /* if half damage curse adjust damage points */
1159                     amt >>= 1;
1160           if (amt <= 0)
1161                     amt2 = amt = 1;
1162           lasthx = x;
1163           lasthy = y;
1164           stealth[x][y] = 1;  /* make sure hitting monst breaks stealth condition */
1165           c[HOLDMONST] = 0;   /* hit a monster breaks hold monster spell */
1166           switch (monst) {    /* if a dragon and orb(s) of dragon slaying */
1167           case WHITEDRAGON:
1168           case REDDRAGON:
1169           case GREENDRAGON:
1170           case BRONZEDRAGON:
1171           case PLATINUMDRAGON:
1172           case SILVERDRAGON:
1173                     amt *= 1 + (c[SLAYING] << 1);
1174                     break;
1175           }
1176           /* invincible monster fix is here */
1177           if (hitp[x][y] > monster[monst].hitpoints)
1178                     hitp[x][y] = monster[monst].hitpoints;
1179           if ((hpoints = hitp[x][y]) <= amt) {
1180 #ifdef EXTRA
1181                     c[MONSTKILLED]++;
1182 #endif
1183                     lprintf("\nThe %s died!", lastmonst);
1184                     raiseexperience((long)monster[monst].experience);
1185                     amt = monster[monst].gold;
1186                     if (amt > 0)
1187                               dropgold(rnd(amt) + amt);
1188                     dropsomething(monst);
1189                     disappear(x, y);
1190                     bottomline();
1191                     return (hpoints);
1192           }
1193           hitp[x][y] = hpoints - amt;
1194           return (amt2);
1195 }
1196 
1197 /*
1198  *        hitplayer(x,y)                Function for the monster to hit the player from (x,y)
1199  *                  int x,y;
1200  *
1201  *        Function for the monster to hit the player with monster at location x,y
1202  *        Returns nothing of value.
1203  */
1204 void
hitplayer(int x,int y)1205 hitplayer(int x, int y)
1206 {
1207           int dam, tmp, mster, bias;
1208           vxy(&x, &y);                  /* verify coordinates are within range */
1209           lastnum = mster = mitem[x][y];
1210           /* spirit naga's and poltergeist's do nothing if scarab of negate spirit */
1211           if (c[NEGATESPIRIT] || c[SPIRITPRO])
1212                     if ((mster == POLTERGEIST) || (mster == SPIRITNAGA))
1213                               return;
1214           /* if undead and cube of undead control */
1215           if (c[CUBEofUNDEAD] || c[UNDEADPRO])
1216                     if ((mster == VAMPIRE) || (mster == WRAITH) || (mster == ZOMBIE))
1217                               return;
1218           if ((know[x][y] & 1) == 0) {
1219                     know[x][y] = 1;
1220                     show1cell(x, y);
1221           }
1222           bias = (c[HARDGAME]) + 1;
1223           hitflag = hit2flag = hit3flag = 1;
1224           yrepcount = 0;
1225           cursors();
1226           ifblind(x, y);
1227           if (c[INVISIBILITY])
1228                     if (rnd(33) < 20) {
1229                               lprintf("\nThe %s misses wildly", lastmonst);
1230                               return;
1231                     }
1232           if (c[CHARMCOUNT])
1233                     if (rnd(30) + 5 * monster[mster].level - c[CHARISMA] < 30) {
1234                               lprintf("\nThe %s is awestruck at your magnificence!", lastmonst);
1235                               return;
1236                     }
1237           if (mster == BAT)
1238                     dam = 1;
1239           else {
1240                     dam = monster[mster].damage;
1241                     dam += rnd((int)((dam < 1) ? 1 : dam)) + monster[mster].level;
1242           }
1243           tmp = 0;
1244           if (monster[mster].attack > 0)
1245                     if (((dam + bias + 8) > c[AC]) || (rnd((int)((c[AC] > 0) ? c[AC] : 1)) == 1)) {
1246                               if (spattack(monster[mster].attack, x, y)) {
1247                                         flushall();
1248                                         return;
1249                               }
1250                               tmp = 1;
1251                               bias -= 2;
1252                               cursors();
1253                     }
1254           if (((dam + bias) > c[AC]) || (rnd((int)((c[AC] > 0) ? c[AC] : 1)) == 1)) {
1255                     lprintf("\n  The %s hit you ", lastmonst);
1256                     tmp = 1;
1257                     if ((dam -= c[AC]) < 0)
1258                               dam = 0;
1259                     if (dam > 0) {
1260                               losehp(dam);
1261                               bottomhp();
1262                               flushall();
1263                     }
1264           }
1265           if (tmp == 0)
1266                     lprintf("\n  The %s missed ", lastmonst);
1267 }
1268 
1269 /*
1270  *        dropsomething(monst)          Function to create an object when a monster dies
1271  *                  int monst;
1272  *
1273  *        Function to create an object near the player when certain monsters are killed
1274  *        Enter with the monster number
1275  *        Returns nothing of value.
1276  */
1277 static void
dropsomething(int monst)1278 dropsomething(int monst)
1279 {
1280           switch (monst) {
1281           case ORC:
1282           case NYMPH:
1283           case ELF:
1284           case TROGLODYTE:
1285           case TROLL:
1286           case ROTHE:
1287           case VIOLETFUNGI:
1288           case PLATINUMDRAGON:
1289           case GNOMEKING:
1290           case REDDRAGON:
1291                     something(level);
1292                     return;
1293 
1294           case LEPRECHAUN:
1295                     if (rnd(101) >= 75)
1296                               creategem();
1297                     if (rnd(5) == 1)
1298                               dropsomething(LEPRECHAUN);
1299                     return;
1300           }
1301 }
1302 
1303 /*
1304  *        dropgold(amount)    Function to drop some gold around player
1305  *                  int amount;
1306  *
1307  *        Enter with the number of gold pieces to drop
1308  *        Returns nothing of value.
1309  */
1310 void
dropgold(int amount)1311 dropgold(int amount)
1312 {
1313           if (amount > 250)
1314                     createitem(OMAXGOLD, amount / 100);
1315           else
1316                     createitem(OGOLDPILE, amount);
1317 }
1318 
1319 /*
1320  *        something(lvl)      Function to create a random item around player
1321  *                  int lvl;
1322  *
1323  *        Function to create an item from a designed probability around player
1324  *        Enter with the cave level on which something is to be dropped
1325  *        Returns nothing of value.
1326  */
1327 void
something(int lvl)1328 something(int lvl)
1329 {
1330           int j;
1331           int i;
1332           if (lvl < 0 || lvl > MAXLEVEL + MAXVLEVEL)        /* correct level? */
1333                     return;
1334           if (rnd(101) < 8)   /* possibly more than one item */
1335                     something(lvl);
1336           j = newobject(lvl, &i);
1337           createitem(j, i);
1338 }
1339 
1340 /*
1341  *        newobject(lev,i)    Routine to return a randomly selected new object
1342  *                  int lev,*i;
1343  *
1344  *        Routine to return a randomly selected object to be created
1345  *        Returns the object number created, and sets *i for its argument
1346  *        Enter with the cave level and a pointer to the items arg
1347  */
1348 static char nobjtab[] = { 0, OSCROLL, OSCROLL, OSCROLL, OSCROLL, OPOTION,
1349           OPOTION, OPOTION, OPOTION, OGOLDPILE, OGOLDPILE, OGOLDPILE, OGOLDPILE,
1350           OBOOK, OBOOK, OBOOK, OBOOK, ODAGGER, ODAGGER, ODAGGER, OLEATHER, OLEATHER,
1351           OLEATHER, OREGENRING, OPROTRING, OENERGYRING, ODEXRING, OSTRRING, OSPEAR,
1352           OBELT, ORING, OSTUDLEATHER, OSHIELD, OFLAIL, OCHAIN, O2SWORD, OPLATE,
1353           OLONGSWORD };
1354 
1355 int
newobject(int lev,int * i)1356 newobject(int lev, int *i)
1357 {
1358           int tmp = 32, j;
1359           if (level < 0 || level > MAXLEVEL + MAXVLEVEL)    /* correct level? */
1360                     return (0);
1361           if (lev > 6)
1362                     tmp = 37;
1363           else if (lev > 4)
1364                     tmp = 35;
1365           j = nobjtab[tmp = rnd(tmp)];  /* the object type */
1366           switch (tmp) {
1367           case 1:
1368           case 2:
1369           case 3:
1370           case 4:
1371                     *i = newscroll();
1372                     break;
1373           case 5:
1374           case 6:
1375           case 7:
1376           case 8:
1377                     *i = newpotion();
1378                     break;
1379           case 9:
1380           case 10:
1381           case 11:
1382           case 12:
1383                     *i = rnd((lev + 1) * 10) + lev * 10 + 10;
1384                     break;
1385           case 13:
1386           case 14:
1387           case 15:
1388           case 16:
1389                     *i = lev;
1390                     break;
1391           case 17:
1392           case 18:
1393           case 19:
1394                     if (!(*i = newdagger()))
1395                               return (0);
1396                     break;
1397           case 20:
1398           case 21:
1399           case 22:
1400                     if (!(*i = newleather()))
1401                               return (0);
1402                     break;
1403           case 23:
1404           case 32:
1405           case 35:
1406                     *i = rund(lev / 3 + 1);
1407                     break;
1408           case 24:
1409           case 26:
1410                     *i = rnd(lev / 4 + 1);
1411                     break;
1412           case 25:
1413                     *i = rund(lev / 4 + 1);
1414                     break;
1415           case 27:
1416                     *i = rnd(lev / 2 + 1);
1417                     break;
1418           case 30:
1419           case 33:
1420                     *i = rund(lev / 2 + 1);
1421                     break;
1422           case 28:
1423                     *i = rund(lev / 3 + 1);
1424                     if (*i == 0)
1425                               return (0);
1426                     break;
1427           case 29:
1428           case 31:
1429                     *i = rund(lev / 2 + 1);
1430                     if (*i == 0)
1431                               return (0);
1432                     break;
1433           case 34:
1434                     *i = newchain();
1435                     break;
1436           case 36:
1437                     *i = newplate();
1438                     break;
1439           case 37:
1440                     *i = newsword();
1441                     break;
1442           }
1443           return (j);
1444 }
1445 
1446 /*
1447  *  spattack(atckno,xx,yy)    Function to process special attacks from monsters
1448  *        int atckno,xx,yy;
1449  *
1450  *        Enter with the special attack number, and the coordinates (xx,yy)
1451  *                  of the monster that is special attacking
1452  *        Returns 1 if must do a show1cell(xx,yy) upon return, 0 otherwise
1453  *
1454  * atckno   monster     effect
1455  * ---------------------------------------------------
1456  *        0         none
1457  *        1         rust monster        eat armor
1458  *        2         hell hound          breathe light fire
1459  *        3         dragon              breathe fire
1460  *        4         giant centipede     weakening sing
1461  *        5         white dragon        cold breath
1462  *        6         wraith              drain level
1463  *        7         waterlord water gusher
1464  *        8         leprechaun          steal gold
1465  *        9         disenchantress      disenchant weapon or armor
1466  *        10        ice lizard          hits with barbed tail
1467  *        11        umber hulk          confusion
1468  *        12        spirit naga         cast spells         taken from special attacks
1469  *        13        platinum dragon     psionics
1470  *        14        nymph               steal objects
1471  *        15        bugbear             bite
1472  *        16        osequip             bite
1473  *
1474  *        char rustarm[ARMORTYPES][2];
1475  *        special array for maximum rust damage to armor from rustmonster
1476  *        format is: { armor type , minimum attribute
1477  */
1478 #define ARMORTYPES 6
1479 static char rustarm[ARMORTYPES][2] = {
1480           { OSTUDLEATHER, -2 },
1481           { ORING, -4 },
1482           { OCHAIN, -5 },
1483           { OSPLINT, -6 },
1484           { OPLATE, -8 },
1485           { OPLATEARMOR, -9 }
1486 };
1487 static char spsel[] = { 1, 2, 3, 5, 6, 8, 9, 11, 13, 14 };
1488 
1489 static int
spattack(int x,int xx,int yy)1490 spattack(int x, int xx, int yy)
1491 {
1492           int i, j = 0, k, m;
1493           const char *p = NULL;
1494 
1495           if (c[CANCELLATION])
1496                     return (0);
1497           vxy(&xx, &yy);                /* verify x & y coordinates */
1498           switch (x) {
1499           case 1:             /* rust your armor, j=1 when rusting has occurred */
1500                     m = k = c[WEAR];
1501                     if ((i = c[SHIELD]) != -1) {
1502                               if (--ivenarg[i] < -1)
1503                                         ivenarg[i] = -1;
1504                               else
1505                                         j = 1;
1506                     }
1507                     if ((j == 0) && (k != -1)) {
1508                               m = iven[k];
1509                               for (i = 0; i < ARMORTYPES; i++)
1510                                         /* find his armor in table */
1511                                         if (m == rustarm[i][0]) {
1512                                                   if (--ivenarg[k] < rustarm[i][1])
1513                                                             ivenarg[k] = rustarm[i][1];
1514                                                   else
1515                                                             j = 1;
1516                                                   break;
1517                                         }
1518                     }
1519                     if (j == 0)         /* if rusting did not occur */
1520                               switch (m) {
1521                               case OLEATHER:
1522                                         p = "\nThe %s hit you -- Your lucky you have leather on";
1523                                         break;
1524                               case OSSPLATE:
1525                                         p = "\nThe %s hit you -- Your fortunate to have stainless steel armor!";
1526                                         break;
1527                               }
1528                     else {
1529                               beep();
1530                               p = "\nThe %s hit you -- your armor feels weaker";
1531                     }
1532                     break;
1533 
1534           case 2:
1535                     i = rnd(15) + 8 - c[AC];
1536 spout:              p = "\nThe %s breathes fire at you!";
1537                     if (c[FIRERESISTANCE])
1538                               p = "\nThe %s's flame doesn't phase you!";
1539                     else
1540 spout2:             if (p) {
1541                               lprintf(p, lastmonst);
1542                               beep();
1543                     }
1544                     checkloss(i);
1545                     return (0);
1546 
1547           case 3:
1548                     i = rnd(20) + 25 - c[AC];
1549                     goto spout;
1550 
1551           case 4:
1552                     if (c[STRENGTH] > 3) {
1553                               p = "\nThe %s stung you!  You feel weaker";
1554                               beep();
1555                               --c[STRENGTH];
1556                     } else
1557                               p = "\nThe %s stung you!";
1558                     break;
1559 
1560           case 5:
1561                     p = "\nThe %s blasts you with his cold breath";
1562                     i = rnd(15) + 18 - c[AC];
1563                     goto spout2;
1564 
1565           case 6:
1566                     lprintf("\nThe %s drains you of your life energy!", lastmonst);
1567                     loselevel();
1568                     beep();
1569                     return (0);
1570 
1571           case 7:
1572                     p = "\nThe %s got you with a gusher!";
1573                     i = rnd(15) + 25 - c[AC];
1574                     goto spout2;
1575 
1576           case 8:
1577                     if (c[NOTHEFT])               /* he has a device of no theft */
1578                               return (0);
1579                     if (c[GOLD]) {
1580                               p = "\nThe %s hit you -- Your purse feels lighter";
1581                               if (c[GOLD] > 32767)
1582                                         c[GOLD] >>= 1;
1583                               else
1584                                         c[GOLD] -= rnd((int)(1 + (c[GOLD] >> 1)));
1585                               if (c[GOLD] < 0)
1586                                         c[GOLD] = 0;
1587                     } else
1588                               p = "\nThe %s couldn't find any gold to steal";
1589                     lprintf(p, lastmonst);
1590                     disappear(xx, yy);
1591                     beep();
1592                     bottomgold();
1593                     return (1);
1594 
1595           case 9:
1596                     for (j = 50;;) {    /* disenchant */
1597                               i = rund(26);
1598                               m = iven[i];        /* randomly select item */
1599                               if (m > 0 && ivenarg[i] > 0 && m != OSCROLL && m != OPOTION) {
1600                                         if ((ivenarg[i] -= 3) < 0)
1601                                                   ivenarg[i] = 0;
1602                                         lprintf("\nThe %s hits you -- you feel a sense of loss", lastmonst);
1603                                         srcount = 0;
1604                                         beep();
1605                                         show3(i);
1606                                         bottomline();
1607                                         return (0);
1608                               }
1609                               if (--j <= 0) {
1610                                         p = "\nThe %s nearly misses";
1611                                         break;
1612                               }
1613                               break;
1614                     }
1615                     break;
1616 
1617           case 10:
1618                     p = "\nThe %s hit you with his barbed tail";
1619                     i = rnd(25) - c[AC];
1620                     goto spout2;
1621 
1622           case 11:
1623                     p = "\nThe %s has confused you";
1624                     beep();
1625                     c[CONFUSE] += 10 + rnd(10);
1626                     break;
1627 
1628           case 12:  /* performs any number of other special attacks */
1629                     return (spattack(spsel[rund(10)], xx, yy));
1630 
1631           case 13:
1632                     p = "\nThe %s flattens you with his psionics!";
1633                     i = rnd(15) + 30 - c[AC];
1634                     goto spout2;
1635 
1636           case 14:
1637                     if (c[NOTHEFT])               /* he has device of no theft */
1638                               return (0);
1639                     if (emptyhanded() == 1) {
1640                               p = "\nThe %s couldn't find anything to steal";
1641                               break;
1642                     }
1643                     lprintf("\nThe %s picks your pocket and takes:", lastmonst);
1644                     beep();
1645                     if (stealsomething() == 0)
1646                               lprcat("  nothing");
1647                     disappear(xx, yy);
1648                     bottomline();
1649                     return (1);
1650 
1651           case 15:
1652                     i = rnd(10) + 5 - c[AC];
1653 spout3:   p = "\nThe %s bit you!";
1654                     goto spout2;
1655 
1656           case 16:
1657                     i = rnd(15) + 10 - c[AC];
1658                     goto spout3;
1659           }
1660           if (p) {
1661                     lprintf(p, lastmonst);
1662                     bottomline();
1663           }
1664           return (0);
1665 }
1666 
1667 /*
1668  *        checkloss(x)        Routine to subtract hp from user and flag bottomline display
1669  *                  int x;
1670  *
1671  *        Routine to subtract hitpoints from the user and flag the bottomline display
1672  *        Enter with the number of hit points to lose
1673  *        Note: if x > c[HP] this routine could kill the player!
1674  */
1675 void
checkloss(int x)1676 checkloss(int x)
1677 {
1678           if (x > 0) {
1679                     losehp(x);
1680                     bottomhp();
1681           }
1682 }
1683 
1684 /*
1685  *        annihilate()        Routine to annihilate all monsters around player (playerx,playery)
1686  *
1687  *        Gives player experience, but no dropped objects
1688  *        Returns the experience gained from all monsters killed
1689  */
1690 long
annihilate(void)1691 annihilate(void)
1692 {
1693           int i, j;
1694           long k;
1695           char *p;
1696           for (k = 0, i = playerx - 1; i <= playerx + 1; i++)
1697                     for (j = playery - 1; j <= playery + 1; j++)
1698                               if (!vxy(&i, &j)) { /* if not out of bounds */
1699                                         if (*(p = &mitem[i][j])) {    /* if a monster there */
1700                                                   if (*p < DEMONLORD + 2) {
1701                                                             k += monster[(int)*p].experience;
1702                                                             *p = know[i][j] = 0;
1703                                                   } else {
1704                                                             lprintf("\nThe %s barely escapes being annihilated!", monster[(int)*p].name);
1705                                                             hitp[i][j] = (hitp[i][j] >> 1) + 1;     /* lose half hit points*/
1706                                                   }
1707                                         }
1708                               }
1709           if (k > 0) {
1710                     lprcat("\nYou hear loud screams of agony!");
1711                     raiseexperience((long)k);
1712           }
1713           return (k);
1714 }
1715 
1716 /*
1717  *        newsphere(x,y,dir,lifetime)  Function to create a new sphere of annihilation
1718  *                  int x,y,dir,lifetime;
1719  *
1720  *        Enter with the coordinates of the sphere in x,y
1721  *          the direction (0-8 diroffx format) in dir, and the lifespan of the
1722  *          sphere in lifetime (in turns)
1723  *        Returns the number of spheres currently in existence
1724  */
1725 long
newsphere(int x,int y,int dir,int life)1726 newsphere(int x, int y, int dir, int life)
1727 {
1728           int m;
1729           struct sphere *sp;
1730           if (((sp = malloc(sizeof(struct sphere)))) == NULL)
1731                     return (c[SPHCAST]);          /* can't malloc, therefore failure */
1732           if (dir >= 9)                 /* no movement if direction not found */
1733                     dir = 0;
1734           if (level == 0)               /* don't go out of bounds */
1735                     vxy(&x, &y);
1736           else {
1737                     if (x < 1)
1738                               x = 1;
1739                     if (x >= MAXX - 1)
1740                               x = MAXX - 2;
1741                     if (y < 1)
1742                               y = 1;
1743                     if (y >= MAXY - 1)
1744                               y = MAXY - 2;
1745           }
1746           if ((m = mitem[x][y]) >= DEMONLORD + 4) {         /* demons dispel spheres */
1747                     know[x][y] = 1;
1748                     show1cell(x, y);    /* show the demon (ha ha) */
1749                     cursors();
1750                     lprintf("\nThe %s dispels the sphere!", monster[m].name);
1751                     beep();
1752                     rmsphere(x, y);               /* remove any spheres that are here */
1753                     return (c[SPHCAST]);
1754           }
1755           if (m == DISENCHANTRESS) {    /* disenchantress cancels spheres */
1756                     cursors();
1757                     lprintf("\nThe %s causes cancellation of the sphere!", monster[m].name);
1758                     beep();
1759 boom:               sphboom(x, y);                /* blow up stuff around sphere */
1760                     rmsphere(x, y);               /* remove any spheres that are here */
1761                     return (c[SPHCAST]);
1762           }
1763           if (c[CANCELLATION]) {                  /* cancellation cancels spheres */
1764                     cursors();
1765                     lprcat("\nAs the cancellation takes effect, you hear a great earth shaking blast!");
1766                     beep();
1767                     goto boom;
1768           }
1769           if (item[x][y] == OANNIHILATION) {      /* collision of spheres detonates spheres */
1770                     cursors();
1771                     lprcat("\nTwo spheres of annihilation collide! You hear a great earth shaking blast!");
1772                     beep();
1773                     rmsphere(x, y);
1774                     goto boom;
1775           }
1776           if (playerx == x && playery == y) {     /* collision of sphere and player! */
1777                     cursors();
1778                     lprcat("\nYou have been enveloped by the zone of nothingness!\n");
1779                     beep();
1780                     rmsphere(x, y);               /* remove any spheres that are here */
1781                     nap(4000);
1782                     died(258);
1783           }
1784           item[x][y] = OANNIHILATION;
1785           mitem[x][y] = 0;
1786           know[x][y] = 1;
1787           show1cell(x, y);    /* show the new sphere */
1788           sp->x = x;
1789           sp->y = y;
1790           sp->lev = level;
1791           sp->dir = dir;
1792           sp->lifetime = life;
1793           sp->p = 0;
1794           if (spheres == 0)   /* if first node in the sphere list */
1795                     spheres = sp;
1796           else {              /* add sphere to beginning of linked list */
1797                     sp->p = spheres;
1798                     spheres = sp;
1799           }
1800           return (++c[SPHCAST]);        /* one more sphere in the world */
1801 }
1802 
1803 /*
1804  *        rmsphere(x,y)                 Function to delete a sphere of annihilation from list
1805  *                  int x,y;
1806  *
1807  *        Enter with the coordinates of the sphere (on current level)
1808  *        Returns the number of spheres currently in existence
1809  */
1810 long
rmsphere(int x,int y)1811 rmsphere(int x, int y)
1812 {
1813           struct sphere *sp, *sp2 = NULL;
1814           for (sp = spheres; sp; sp2 = sp, sp = sp->p)
1815                     if (level == sp->lev)         /* is sphere on this level? */
1816                               if ((x == sp->x) && (y == sp->y)) {     /* locate sphere at this location */
1817                                         item[x][y] = mitem[x][y] = 0;
1818                                         know[x][y] = 1;
1819                                         show1cell(x, y);    /* show the now missing sphere */
1820                                         --c[SPHCAST];
1821                                         if (sp == spheres) {
1822                                                   sp2 = sp;
1823                                                   spheres = sp->p;
1824                                                   free(sp2);
1825                                         } else {
1826                                                   sp2->p = sp->p;
1827                                                   free(sp);
1828                                         }
1829                                         break;
1830                               }
1831           return (c[SPHCAST]);          /* return number of spheres in the world */
1832 }
1833 
1834 /*
1835  *        sphboom(x,y)        Function to perform the effects of a sphere detonation
1836  *                  int x,y;
1837  *
1838  *        Enter with the coordinates of the blast, Returns no value
1839  */
1840 static void
sphboom(int x,int y)1841 sphboom(int x, int y)
1842 {
1843           int i, j;
1844           if (c[HOLDMONST])
1845                     c[HOLDMONST] = 1;
1846           if (c[CANCELLATION])
1847                     c[CANCELLATION] = 1;
1848           for (j = max(1, x - 2); j < min(x + 3, MAXX - 1); j++)
1849                     for (i = max(1, y - 2); i < min(y + 3, MAXY - 1); i++) {
1850                               item[j][i] = mitem[j][i] = 0;
1851                               show1cell(j, i);
1852                               if (playerx == j && playery == i) {
1853                                         cursors();
1854                                         beep();
1855                                         lprcat("\nYou were too close to the sphere!");
1856                                         nap(3000);
1857                                         died(283);          /* player killed in explosion */
1858                               }
1859                     }
1860 }
1861 
1862 /*
1863  *        genmonst()                    Function to ask for monster and genocide from game
1864  *
1865  *        This is done by setting a flag in the monster[] structure
1866  */
1867 static void
genmonst(void)1868 genmonst(void)
1869 {
1870           int i, j;
1871           cursors();
1872           lprcat("\nGenocide what monster? ");
1873           for (i = 0; (!isalpha(i)) && (i != ' '); i = getchr())
1874                     ;
1875           lprc(i);
1876           for (j = 0; j < MAXMONST; j++)          /* search for the monster type */
1877                     if (monstnamelist[j] == i) {  /* have we found it? */
1878                               monster[j].genocided = 1;     /* genocided from game */
1879                               lprintf("  There will be no more %s's", monster[j].name);
1880                               /* now wipe out monsters on this level */
1881                               newcavelevel(level);
1882                               draws(0, MAXX, 0, MAXY);
1883                               bot_linex();
1884                               return;
1885                     }
1886           lprcat("  You sense failure!");
1887 }
1888