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