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