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