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 $
5 * This file contains the following functions:
6 * ----------------------------------------------------------------------------
8 * createmonster(monstno) Function to create a monster next to the player
11 * int cgood(x,y,itm,monst) Function to check location for emptiness
14 * createitem(it,arg) Routine to place an item next to the player
17 * cast() Subroutine called by parse to cast a spell for the user
19 * speldamage(x) Function to perform spell functions cast by the player
22 * loseint() Routine to decrement your int (intelligence) if > 3
24 * isconfuse() Routine to check to see if player is confused
26 * nospell(x,monst) Routine to return 1 if a spell doesn't affect a monster
29 * fullhit(xx) Function to return full damage against a monst (aka web)
32 * direct(spnum,dam,str,arg) Routine to direct spell damage 1 square in 1 dir
36 * godirect(spnum,dam,str,delay,cshow) Function to perform missile attacks
37 * int spnum,dam,delay;
40 * ifblind(x,y) Routine to put "monster" or the monster name into lastmosnt
43 * tdirect(spnum) Routine to teleport away a monster
46 * omnidirect(sp,dam,str) Routine to damage all monsters 1 square from player
50 * dirsub(x,y) Routine to ask for direction, then modify x,y for it
53 * vxy(x,y) Routine to verify/fix (*x,*y) for being within bounds
56 * dirpoly(spnum) Routine to ask for a direction and polymorph a monst
59 * hitmonster(x,y) Function to hit a monster at the designated coordinates
62 * hitm(x,y,amt) Function to just hit a monster at a given coordinates
65 * hitplayer(x,y) Function for the monster to hit the player from (x,y)
68 * dropsomething(monst) Function to create an object when a monster dies
71 * dropgold(amount) Function to drop some gold around player
74 * something(level) Function to create a random item around player
77 * newobject(lev,i) Routine to return a randomly selected new object
80 * spattack(atckno,xx,yy) Function to process special attacks from monsters
83 * checkloss(x) Routine to subtract hp from user and flag bottomline display
86 * annihilate() Routine to annihilate monsters around player, playerx,playery
88 * newsphere(x,y,dir,lifetime) Function to create a new sphere of annihilation
89 * int x,y,dir,lifetime;
91 * rmsphere(x,y) Function to delete a sphere of annihilation from list
94 * sphboom(x,y) Function to perform the effects of a sphere detonation
97 * genmonst() Function to ask for monster and genocide from game
102 struct isave /* used for altar reality */
104 char type; /* 0=item, 1=monster */
105 char id; /* item number or monster number */
106 short arg; /* the type of item or hitpoints of monster */
110 * createmonster(monstno) Function to create a monster next to the player
113 * Enter with the monster number (1 to MAXMONST+8)
120 if (mon<1 || mon>MAXMONST+8) /* check for monster number out of bounds */
122 beep(); lprintf("\ncan't createmonst(%d)\n",(long)mon); nap(3000); return;
124 while (monster[mon].genocided && mon<MAXMONST) mon++; /* genocided? */
125 for (k=rnd(8), i= -8; i<0; i++,k++) /* choose direction, then try all */
127 if (k>8) k=1; /* wraparound the diroff arrays */
128 x = playerx + diroffx[k]; y = playery + diroffy[k];
129 if (cgood(x,y,0,1)) /* if we can create here */
132 hitp[x][y] = monster[mon].hitpoints;
133 stealth[x][y]=know[x][y]=0;
136 case ROTHE: case POLTERGEIST: case VAMPIRE: stealth[x][y]=1;
144 * int cgood(x,y,itm,monst) Function to check location for emptiness
147 * Routine to return TRUE if a location does not have itm or monst there
148 * returns FALSE (0) otherwise
149 * Enter with itm or monst TRUE or FALSE if checking it
150 * Example: if itm==TRUE check for no item at this location
151 * if monst==TRUE check for no monster at this location
152 * This routine will return FALSE if at a wall or the dungeon exit on level 1
154 int cgood(x,y,itm,monst)
158 if ((y>=0) && (y<=MAXY-1) && (x>=0) && (x<=MAXX-1)) /* within bounds? */
159 if (item[x][y]!=OWALL) /* can't make anything on walls */
160 if (itm==0 || (item[x][y]==0)) /* is it free of items? */
161 if (monst==0 || (mitem[x][y]==0)) /* is it free of monsters? */
162 if ((level!=1) || (x!=33) || (y!=MAXY-1)) /* not exit to level 1 */
168 * createitem(it,arg) Routine to place an item next to the player
171 * Enter with the item number and its argument (iven[], ivenarg[])
172 * Returns no value, thus we don't know about createitem() failures.
178 if (it >= MAXOBJ) return; /* no such object */
179 for (k=rnd(8), i= -8; i<0; i++,k++) /* choose direction, then try all */
181 if (k>8) k=1; /* wraparound the diroff arrays */
182 x = playerx + diroffx[k]; y = playery + diroffy[k];
183 if (cgood(x,y,1,0)) /* if we can create here */
185 item[x][y] = it; know[x][y]=0; iarg[x][y]=arg; return;
191 * cast() Subroutine called by parse to cast a spell for the user
193 * No arguments and no return value.
195 static char eys[] = "\nEnter your spell: ";
200 if (c[SPELLS]<=0) { lprcat("\nYou don't have any spells!"); return; }
201 lprcat(eys); --c[SPELLS];
202 while ((a=getchar())=='D')
203 { seemagic(-1); cursors(); lprcat(eys); }
204 if (a=='\33') goto over; /* to escape casting a spell */
205 if ((b=getchar())=='\33') goto over; /* to escape casting a spell */
206 if ((d=getchar())=='\33')
207 { over: lprcat(aborted); c[SPELLS]++; return; } /* to escape casting a spell */
211 for (lprc('\n'),j= -1,i=0; i<SPNUM; i++) /*seq search for his spell, hash?*/
212 if ((spelcode[i][0]==a) && (spelcode[i][1]==b) && (spelcode[i][2]==d))
214 { speldamage(i); j = 1; i=SPNUM; }
216 if (j == -1) lprcat(" Nothing Happened ");
223 * speldamage(x) Function to perform spell functions cast by the player
226 * Enter with the spell number, returns no value.
227 * Please insure that there are 2 spaces before all messages here
235 if (x>=SPNUM) return; /* no such spell */
236 if (c[TIMESTOP]) { lprcat(" It didn't seem to work"); return; } /* not if time stopped */
238 if ((rnd(23)==7) || (rnd(18) > c[INTELLIGENCE]))
239 { lprcat(" It didn't work!"); return; }
240 if (clev*3+2 < x) { lprcat(" Nothing happens. You seem inexperienced at this"); return; }
244 /* ----- LEVEL 1 SPELLS ----- */
246 case 0: if (c[PROTECTIONTIME]==0) c[MOREDEFENSES]+=2; /* protection field +2 */
247 c[PROTECTIONTIME] += 250; return;
249 case 1: i = rnd(((clev+1)<<1)) + clev + 3;
250 godirect(x,i,(clev>=2)?" Your missiles hit the %s":" Your missile hit the %s",100,'+'); /* magic missile */
254 case 2: if (c[DEXCOUNT]==0) c[DEXTERITY]+=3; /* dexterity */
255 c[DEXCOUNT] += 400; return;
258 p=" While the %s slept, you smashed it %d times";
259 ws: direct(x,fullhit(i),p,i); /* sleep */ return;
261 case 4: /* charm monster */ c[CHARMCOUNT] += c[CHARISMA]<<1; return;
263 case 5: godirect(x,rnd(10)+15+clev," The sound damages the %s",70,'@'); /* sonic spear */
266 /* ----- LEVEL 2 SPELLS ----- */
268 case 6: i=rnd(3)+2; p=" While the %s is entangled, you hit %d times";
271 case 7: if (c[STRCOUNT]==0) c[STREXTRA]+=3; /* strength */
272 c[STRCOUNT] += 150+rnd(100); return;
274 case 8: yl = playery-5; /* enlightenment */
275 yh = playery+6; xl = playerx-15; xh = playerx+16;
276 vxy(&xl,&yl); vxy(&xh,&yh); /* check bounds */
277 for (i=yl; i<=yh; i++) /* enlightenment */
278 for (j=xl; j<=xh; j++) know[j][i]=1;
279 draws(xl,xh+1,yl,yh+1); return;
281 case 9: raisehp(20+(clev<<1)); return; /* healing */
283 case 10: c[BLINDCOUNT]=0; return; /* cure blindness */
285 case 11: createmonster(makemonst(level+1)+8); return;
287 case 12: if (rnd(11)+7 <= c[WISDOM]) direct(x,rnd(20)+20+clev," The %s believed!",0);
288 else lprcat(" It didn't believe the illusions!");
291 case 13: /* if he has the amulet of invisibility then add more time */
292 for (j=i=0; i<26; i++)
293 if (iven[i]==OAMULET) j+= 1+ivenarg[i];
294 c[INVISIBILITY] += (j<<7)+12; return;
296 /* ----- LEVEL 3 SPELLS ----- */
298 case 14: godirect(x,rnd(25+clev)+25+clev," The fireball hits the %s",40,'*'); return; /* fireball */
300 case 15: godirect(x,rnd(25)+20+clev," Your cone of cold strikes the %s",60,'O'); /* cold */
303 case 16: dirpoly(x); return; /* polymorph */
305 case 17: c[CANCELLATION]+= 5+clev; return; /* cancellation */
307 case 18: c[HASTESELF]+= 7+clev; return; /* haste self */
309 case 19: omnidirect(x,30+rnd(10)," The %s gasps for air"); /* cloud kill */
312 case 20: xh = min(playerx+1,MAXX-2); yh = min(playery+1,MAXY-2);
313 for (i=max(playerx-1,1); i<=xh; i++) /* vaporize rock */
314 for (j=max(playery-1,1); j<=yh; j++)
316 kn = &know[i][j]; pm = &mitem[i][j];
317 switch(*(p= &item[i][j]))
319 case OWALL: if (level < MAXLEVEL+MAXVLEVEL-1)
323 case OSTATUE: if (c[HARDGAME]<3)
325 *p=OBOOK; iarg[i][j]=level; *kn=0;
329 case OTHRONE: *pm=GNOMEKING; *kn=0; *p= OTHRONE2;
330 hitp[i][j]=monster[GNOMEKING].hitpoints; break;
332 case OALTAR: *pm=DEMONPRINCE; *kn=0;
333 hitp[i][j]=monster[DEMONPRINCE].hitpoints; break;
337 case XORN: ifblind(i,j); hitm(i,j,200); break; /* Xorn takes damage from vpr */
342 /* ----- LEVEL 4 SPELLS ----- */
344 case 21: direct(x,100+clev," The %s shrivels up",0); /* dehydration */
347 case 22: godirect(x,rnd(25)+20+(clev<<1)," A lightning bolt hits the %s",1,'~'); /* lightning */
350 case 23: i=min(c[HP]-1,c[HPMAX]/2); /* drain life */
351 direct(x,i+i,"",0); c[HP] -= i; return;
353 case 24: if (c[GLOBE]==0) c[MOREDEFENSES] += 10;
354 c[GLOBE] += 200; loseint(); /* globe of invulnerability */
357 case 25: omnidirect(x,32+clev," The %s struggles for air in your flood!"); /* flood */
360 case 26: if (rnd(151)==63) { beep(); lprcat("\nYour heart stopped!\n"); nap(4000); died(270); return; }
361 if (c[WISDOM]>rnd(10)+10) direct(x,2000," The %s's heart stopped",0); /* finger of death */
362 else lprcat(" It didn't work"); return;
364 /* ----- LEVEL 5 SPELLS ----- */
366 case 27: c[SCAREMONST] += rnd(10)+clev; return; /* scare monster */
368 case 28: c[HOLDMONST] += rnd(10)+clev; return; /* hold monster */
370 case 29: c[TIMESTOP] += rnd(20)+(clev<<1); return; /* time stop */
372 case 30: tdirect(x); return; /* teleport away */
374 case 31: omnidirect(x,35+rnd(10)+clev," The %s cringes from the flame"); /* magic fire */
377 /* ----- LEVEL 6 SPELLS ----- */
379 case 32: if ((rnd(23)==5) && (wizard==0)) /* sphere of annihilation */
381 beep(); lprcat("\nYou have been enveloped by the zone of nothingness!\n");
382 nap(4000); died(258); return;
384 xl=playerx; yl=playery;
386 i=dirsub(&xl,&yl); /* get direction of sphere */
387 newsphere(xl,yl,i,rnd(20)+11); /* make a sphere */
390 case 33: genmonst(); spelknow[33]=0; /* genocide */
394 case 34: /* summon demon */
395 if (rnd(100) > 30) { direct(x,150," The demon strikes at the %s",0); return; }
396 if (rnd(100) > 15) { lprcat(" Nothing seems to have happened"); return; }
397 lprcat(" The demon turned on you and vanished!"); beep();
398 i=rnd(40)+30; lastnum=277;
399 losehp(i); /* must say killed by a demon */ return;
401 case 35: /* walk through walls */
402 c[WTW] += rnd(10)+5; return;
404 case 36: /* alter reality */
406 struct isave *save; /* pointer to item save structure */
407 int sc; sc=0; /* # items saved */
408 save = (struct isave *)malloc(sizeof(struct isave)*MAXX*MAXY*2);
409 for (j=0; j<MAXY; j++)
410 for (i=0; i<MAXX; i++) /* save all items and monsters */
413 if (xl && xl!=OWALL && xl!=OANNIHILATION)
415 save[sc].type=0; save[sc].id=item[i][j];
416 save[sc++].arg=iarg[i][j];
420 save[sc].type=1; save[sc].id=mitem[i][j];
421 save[sc++].arg=hitp[i][j];
423 item[i][j]=OWALL; mitem[i][j]=0;
424 if (wizard) know[i][j]=1; else know[i][j]=0;
426 eat(1,1); if (level==1) item[33][MAXY-1]=0;
427 for (j=rnd(MAXY-2), i=1; i<MAXX-1; i++) item[i][j]=0;
428 while (sc>0) /* put objects back in level */
431 if (save[sc].type == 0)
434 for (trys=100, i=j=1; --trys>0 && item[i][j]; i=rnd(MAXX-1), j=rnd(MAXY-1));
435 if (trys) { item[i][j]=save[sc].id; iarg[i][j]=save[sc].arg; }
438 { /* put monsters back in */
440 for (trys=100, i=j=1; --trys>0 && (item[i][j]==OWALL || mitem[i][j]); i=rnd(MAXX-1), j=rnd(MAXY-1));
441 if (trys) { mitem[i][j]=save[sc].id; hitp[i][j]=save[sc].arg; }
445 draws(0,MAXX,0,MAXY); if (wizard==0) spelknow[36]=0;
446 free((char*)save); positionplayer(); return;
449 case 37: /* permanence */ adjtime(-99999L); spelknow[37]=0; /* forget */
453 default: lprintf(" spell %d not available!",(long)x); beep(); return;
458 * loseint() Routine to subtract 1 from your int (intelligence) if > 3
460 * No arguments and no return value
464 if (--c[INTELLIGENCE]<3) c[INTELLIGENCE]=3;
468 * isconfuse() Routine to check to see if player is confused
470 * This routine prints out a message saying "You can't aim your magic!"
471 * returns 0 if not confused, non-zero (time remaining confused) if confused
475 if (c[CONFUSE]) { lprcat(" You can't aim your magic!"); beep(); }
480 * nospell(x,monst) Routine to return 1 if a spell doesn't affect a monster
483 * Subroutine to return 1 if the spell can't affect the monster
484 * otherwise returns 0
485 * Enter with the spell number in x, and the monster number in monst.
491 if (x>=SPNUM || monst>=MAXMONST+8 || monst<0 || x<0) return(0); /* bad spell or monst */
492 if ((tmp=spelweird[monst-1][x])==0) return(0);
493 cursors(); lprc('\n'); lprintf(spelmes[tmp],monster[monst].name); return(1);
497 * fullhit(xx) Function to return full damage against a monster (aka web)
500 * Function to return hp damage to monster due to a number of full hits
501 * Enter with the number of full hits being done
507 if (xx<0 || xx>20) return(0); /* fullhits are out of range */
508 if (c[LANCEDEATH]) return(10000); /* lance of death */
509 i = xx * ((c[WCLASS]>>1)+c[STRENGTH]+c[STREXTRA]-c[HARDGAME]-12+c[MOREDAM]);
510 return( (i>=1) ? i : xx );
514 * direct(spnum,dam,str,arg) Routine to direct spell damage 1 square in 1 dir
518 * Routine to ask for a direction to a spell and then hit the monster
519 * Enter with the spell number in spnum, the damage to be done in dam,
520 * lprintf format string in str, and lprintf's argument in arg.
523 direct(spnum,dam,str,arg)
529 if (spnum<0 || spnum>=SPNUM || str==0) return; /* bad arguments */
530 if (isconfuse()) return;
533 if (item[x][y]==OMIRROR)
535 if (spnum==3) /* sleep */
537 lprcat("You fall asleep! "); beep();
540 while (arg-- > 0) { parse2(); nap(1000); }
543 else if (spnum==6) /* web */
545 lprcat("You get stuck in your own web! "); beep();
551 lprintf(str,"spell caster (thats you)",(long)arg);
552 beep(); losehp(dam); return;
556 { lprcat(" There wasn't anything there!"); return; }
558 if (nospell(spnum,m)) { lasthx=x; lasthy=y; return; }
559 lprintf(str,lastmonst,(long)arg); hitm(x,y,dam);
563 * godirect(spnum,dam,str,delay,cshow) Function to perform missile attacks
564 * int spnum,dam,delay;
567 * Function to hit in a direction from a missile weapon and have it keep
568 * on going in that direction until its power is exhausted
569 * Enter with the spell number in spnum, the power of the weapon in hp,
570 * lprintf format string in str, the # of milliseconds to delay between
571 * locations in delay, and the character to represent the weapon in cshow.
574 godirect(spnum,dam,str,delay,cshow)
581 if (spnum<0 || spnum>=SPNUM || str==0 || delay<0) return; /* bad args */
582 if (isconfuse()) return;
583 dirsub(&dx,&dy); x=dx; y=dy;
584 dx = x-playerx; dy = y-playery; x = playerx; y = playery;
588 if ((x > MAXX-1) || (y > MAXY-1) || (x < 0) || (y < 0))
590 dam=0; break; /* out of bounds */
592 if ((x==playerx) && (y==playery)) /* if energy hits player */
594 cursors(); lprcat("\nYou are hit my your own magic!"); beep();
595 lastnum=278; losehp(dam); return;
597 if (c[BLINDCOUNT]==0) /* if not blind show effect */
599 cursor(x+1,y+1); lprc(cshow); nap(delay); show1cell(x,y);
601 if ((m=mitem[x][y])) /* is there a monster there? */
604 if (nospell(spnum,m)) { lasthx=x; lasthy=y; return; }
605 cursors(); lprc('\n');
606 lprintf(str,lastmonst); dam -= hitm(x,y,dam);
607 show1cell(x,y); nap(1000); x -= dx; y -= dy;
609 else switch (*(p= &item[x][y]))
611 case OWALL: cursors(); lprc('\n'); lprintf(str,"wall");
612 if (dam>=50+c[HARDGAME]) /* enough damage? */
613 if (level<MAXLEVEL+MAXVLEVEL-1) /* not on V3 */
614 if ((x<MAXX-1) && (y<MAXY-1) && (x) && (y))
616 lprcat(" The wall crumbles");
621 god2: dam = 0; break;
623 case OCLOSEDDOOR: cursors(); lprc('\n'); lprintf(str,"door");
626 lprcat(" The door is blasted apart");
631 case OSTATUE: cursors(); lprc('\n'); lprintf(str,"statue");
635 lprcat(" The statue crumbles");
636 *p=OBOOK; iarg[x][y]=level;
641 case OTHRONE: cursors(); lprc('\n'); lprintf(str,"throne");
644 mitem[x][y]=GNOMEKING; hitp[x][y]=monster[GNOMEKING].hitpoints;
650 case OMIRROR: dx *= -1; dy *= -1; break;
652 dam -= 3 + (c[HARDGAME]>>1);
657 * ifblind(x,y) Routine to put "monster" or the monster name into lastmosnt
660 * Subroutine to copy the word "monster" into lastmonst if the player is blind
661 * Enter with the coordinates (x,y) of the monster
668 vxy(&x,&y); /* verify correct x,y coordinates */
669 if (c[BLINDCOUNT]) { lastnum=279; p="monster"; }
670 else { lastnum=mitem[x][y]; p=monster[lastnum].name; }
675 * tdirect(spnum) Routine to teleport away a monster
678 * Routine to ask for a direction to a spell and then teleport away monster
679 * Enter with the spell number that wants to teleport away
687 if (spnum<0 || spnum>=SPNUM) return; /* bad args */
688 if (isconfuse()) return;
690 if ((m=mitem[x][y])==0)
691 { lprcat(" There wasn't anything there!"); return; }
693 if (nospell(spnum,m)) { lasthx=x; lasthy=y; return; }
694 fillmonst(m); mitem[x][y]=know[x][y]=0;
698 * omnidirect(sp,dam,str) Routine to damage all monsters 1 square from player
702 * Routine to cast a spell and then hit the monster in all directions
703 * Enter with the spell number in sp, the damage done to wach square in dam,
704 * and the lprintf string to identify the spell in str.
707 omnidirect(spnum,dam,str)
712 if (spnum<0 || spnum>=SPNUM || str==0) return; /* bad args */
713 for (x=playerx-1; x<playerx+2; x++)
714 for (y=playery-1; y<playery+2; y++)
718 if (nospell(spnum,m) == 0)
721 cursors(); lprc('\n'); lprintf(str,lastmonst);
722 hitm(x,y,dam); nap(800);
724 else { lasthx=x; lasthy=y; }
730 * static dirsub(x,y) Routine to ask for direction, then modify x,y for it
733 * Function to ask for a direction and modify an x,y for that direction
734 * Enter with the origination coordinates in (x,y).
735 * Returns index into diroffx[] (0-8).
742 lprcat("\nIn What Direction? ");
753 case 'j': i++; goto out;
756 *x = playerx+diroffx[i]; *y = playery+diroffy[i];
761 * vxy(x,y) Routine to verify/fix coordinates for being within bounds
764 * Function to verify x & y are within the bounds for a level
765 * If *x or *y is not within the absolute bounds for a level, fix them so that
766 * they are on the level.
767 * Returns TRUE if it was out of bounds, and the *x & *y in the calling
768 * routine are affected.
774 if (*x<0) { *x=0; flag++; }
775 if (*y<0) { *y=0; flag++; }
776 if (*x>=MAXX) { *x=MAXX-1; flag++; }
777 if (*y>=MAXY) { *y=MAXY-1; flag++; }
782 * dirpoly(spnum) Routine to ask for a direction and polymorph a monst
785 * Subroutine to polymorph a monster and ask for the direction its in
786 * Enter with the spell number in spmun.
793 if (spnum<0 || spnum>=SPNUM) return; /* bad args */
794 if (isconfuse()) return; /* if he is confused, he can't aim his magic */
797 { lprcat(" There wasn't anything there!"); return; }
799 if (nospell(spnum,mitem[x][y])) { lasthx=x; lasthy=y; return; }
800 while ( monster[m = mitem[x][y] = rnd(MAXMONST+7)].genocided );
801 hitp[x][y] = monster[m].hitpoints;
802 show1cell(x,y); /* show the new monster */
806 * hitmonster(x,y) Function to hit a monster at the designated coordinates
809 * This routine is used for a bash & slash type attack on a monster
810 * Enter with the coordinates of the monster in (x,y).
816 int tmp,monst,damag,flag;
817 if (c[TIMESTOP]) return; /* not if time stopped */
818 vxy(&x,&y); /* verify coordinates are within range */
819 if ((monst = mitem[x][y]) == 0) return;
820 hit3flag=1; ifblind(x,y);
821 tmp = monster[monst].armorclass + c[LEVEL] + c[DEXTERITY] + c[WCLASS]/4 - 12;
823 if ((rnd(20) < tmp-c[HARDGAME]) || (rnd(71) < 5)) /* need at least random chance to hit */
825 lprcat("\nYou hit"); flag=1;
827 if (damag<9999) damag=rnd(damag)+1;
831 lprcat("\nYou missed"); flag=0;
833 lprcat(" the "); lprcat(lastmonst);
834 if (flag) /* if the monster was hit */
835 if ((monst==RUSTMONSTER) || (monst==DISENCHANTRESS) || (monst==CUBE))
837 if (ivenarg[c[WIELD]] > -10)
839 lprintf("\nYour weapon is dulled by the %s",lastmonst); beep();
842 if (flag) hitm(x,y,damag);
843 if (monst == VAMPIRE) if (hitp[x][y]<25) { mitem[x][y]=BAT; know[x][y]=0; }
847 * hitm(x,y,amt) Function to just hit a monster at a given coordinates
850 * Returns the number of hitpoints the monster absorbed
851 * This routine is used to specifically damage a monster at a location (x,y)
852 * Called by hitmonster(x,y)
860 vxy(&x,&y); /* verify coordinates are within range */
861 amt2 = amt; /* save initial damage so we can return it */
863 if (c[HALFDAM]) amt >>= 1; /* if half damage curse adjust damage points */
864 if (amt<=0) amt2 = amt = 1;
866 stealth[x][y]=1; /* make sure hitting monst breaks stealth condition */
867 c[HOLDMONST]=0; /* hit a monster breaks hold monster spell */
868 switch(monst) /* if a dragon and orb(s) of dragon slaying */
870 case WHITEDRAGON: case REDDRAGON: case GREENDRAGON:
871 case BRONZEDRAGON: case PLATINUMDRAGON: case SILVERDRAGON:
872 amt *= 1+(c[SLAYING]<<1); break;
874 /* invincible monster fix is here */
875 if (hitp[x][y] > monster[monst].hitpoints)
876 hitp[x][y] = monster[monst].hitpoints;
877 if ((hpoints = hitp[x][y]) <= amt)
882 lprintf("\nThe %s died!",lastmonst);
883 raiseexperience((long)monster[monst].experience);
884 amt = monster[monst].gold; if (amt>0) dropgold(rnd(amt)+amt);
885 dropsomething(monst); disappear(x,y); bottomline();
888 hitp[x][y] = hpoints-amt; return(amt2);
892 * hitplayer(x,y) Function for the monster to hit the player from (x,y)
895 * Function for the monster to hit the player with monster at location x,y
896 * Returns nothing of value.
901 int dam,tmp,mster,bias;
902 vxy(&x,&y); /* verify coordinates are within range */
903 lastnum = mster = mitem[x][y];
904 /* spirit naga's and poltergeist's do nothing if scarab of negate spirit */
905 if (c[NEGATESPIRIT] || c[SPIRITPRO]) if ((mster ==POLTERGEIST) || (mster ==SPIRITNAGA)) return;
906 /* if undead and cube of undead control */
907 if (c[CUBEofUNDEAD] || c[UNDEADPRO]) if ((mster ==VAMPIRE) || (mster ==WRAITH) || (mster ==ZOMBIE)) return;
908 if ((know[x][y]&1) == 0)
910 know[x][y]=1; show1cell(x,y);
912 bias = (c[HARDGAME]) + 1;
913 hitflag = hit2flag = hit3flag = 1;
915 cursors(); ifblind(x,y);
916 if (c[INVISIBILITY]) if (rnd(33)<20)
918 lprintf("\nThe %s misses wildly",lastmonst); return;
920 if (c[CHARMCOUNT]) if (rnd(30)+5*monster[mster].level-c[CHARISMA]<30)
922 lprintf("\nThe %s is awestruck at your magnificence!",lastmonst);
925 if (mster==BAT) dam=1;
928 dam = monster[mster].damage;
929 dam += rnd((int)((dam<1)?1:dam)) + monster[mster].level;
932 if (monster[mster].attack>0)
933 if (((dam + bias + 8) > c[AC]) || (rnd((int)((c[AC]>0)?c[AC]:1))==1))
934 { if (spattack(monster[mster].attack,x,y)) { flushall(); return; }
935 tmp = 1; bias -= 2; cursors(); }
936 if (((dam + bias) > c[AC]) || (rnd((int)((c[AC]>0)?c[AC]:1))==1))
938 lprintf("\n The %s hit you ",lastmonst); tmp = 1;
939 if ((dam -= c[AC]) < 0) dam=0;
940 if (dam > 0) { losehp(dam); bottomhp(); flushall(); }
942 if (tmp == 0) lprintf("\n The %s missed ",lastmonst);
946 * dropsomething(monst) Function to create an object when a monster dies
949 * Function to create an object near the player when certain monsters are killed
950 * Enter with the monster number
951 * Returns nothing of value.
958 case ORC: case NYMPH: case ELF: case TROGLODYTE:
959 case TROLL: case ROTHE: case VIOLETFUNGI:
960 case PLATINUMDRAGON: case GNOMEKING: case REDDRAGON:
961 something(level); return;
963 case LEPRECHAUN: if (rnd(101)>=75) creategem();
964 if (rnd(5)==1) dropsomething(LEPRECHAUN); return;
969 * dropgold(amount) Function to drop some gold around player
972 * Enter with the number of gold pieces to drop
973 * Returns nothing of value.
978 if (amount > 250) createitem(OMAXGOLD,amount/100); else createitem(OGOLDPILE,amount);
982 * something(level) Function to create a random item around player
985 * Function to create an item from a designed probability around player
986 * Enter with the cave level on which something is to be dropped
987 * Returns nothing of value.
994 if (level<0 || level>MAXLEVEL+MAXVLEVEL) return; /* correct level? */
995 if (rnd(101)<8) something(level); /* possibly more than one item */
996 j = newobject(level,&i); createitem(j,i);
1000 * newobject(lev,i) Routine to return a randomly selected new object
1003 * Routine to return a randomly selected object to be created
1004 * Returns the object number created, and sets *i for its argument
1005 * Enter with the cave level and a pointer to the items arg
1007 static char nobjtab[] = { 0, OSCROLL, OSCROLL, OSCROLL, OSCROLL, OPOTION,
1008 OPOTION, OPOTION, OPOTION, OGOLDPILE, OGOLDPILE, OGOLDPILE, OGOLDPILE,
1009 OBOOK, OBOOK, OBOOK, OBOOK, ODAGGER, ODAGGER, ODAGGER, OLEATHER, OLEATHER,
1010 OLEATHER, OREGENRING, OPROTRING, OENERGYRING, ODEXRING, OSTRRING, OSPEAR,
1011 OBELT, ORING, OSTUDLEATHER, OSHIELD, OFLAIL, OCHAIN, O2SWORD, OPLATE,
1018 if (level<0 || level>MAXLEVEL+MAXVLEVEL) return(0); /* correct level? */
1019 if (lev>6) tmp=37; else if (lev>4) tmp=35;
1020 j = nobjtab[tmp=rnd(tmp)]; /* the object type */
1023 case 1: case 2: case 3: case 4: *i=newscroll(); break;
1024 case 5: case 6: case 7: case 8: *i=newpotion(); break;
1025 case 9: case 10: case 11: case 12: *i=rnd((lev+1)*10)+lev*10+10; break;
1026 case 13: case 14: case 15: case 16: *i=lev; break;
1027 case 17: case 18: case 19: if (!(*i=newdagger())) return(0); break;
1028 case 20: case 21: case 22: if (!(*i=newleather())) return(0); break;
1029 case 23: case 32: case 35: *i=rund(lev/3+1); break;
1030 case 24: case 26: *i=rnd(lev/4+1); break;
1031 case 25: *i=rund(lev/4+1); break;
1032 case 27: *i=rnd(lev/2+1); break;
1033 case 30: case 33: *i=rund(lev/2+1); break;
1034 case 28: *i=rund(lev/3+1); if (*i==0) return(0); break;
1035 case 29: case 31: *i=rund(lev/2+1); if (*i==0) return(0); break;
1036 case 34: *i=newchain(); break;
1037 case 36: *i=newplate(); break;
1038 case 37: *i=newsword(); break;
1044 * spattack(atckno,xx,yy) Function to process special attacks from monsters
1047 * Enter with the special attack number, and the coordinates (xx,yy)
1048 * of the monster that is special attacking
1049 * Returns 1 if must do a show1cell(xx,yy) upon return, 0 otherwise
1051 * atckno monster effect
1052 * ---------------------------------------------------
1054 * 1 rust monster eat armor
1055 * 2 hell hound breathe light fire
1056 * 3 dragon breathe fire
1057 * 4 giant centipede weakening sing
1058 * 5 white dragon cold breath
1059 * 6 wraith drain level
1060 * 7 waterlord water gusher
1061 * 8 leprechaun steal gold
1062 * 9 disenchantress disenchant weapon or armor
1063 * 10 ice lizard hits with barbed tail
1064 * 11 umber hulk confusion
1065 * 12 spirit naga cast spells taken from special attacks
1066 * 13 platinum dragon psionics
1067 * 14 nymph steal objects
1071 * char rustarm[ARMORTYPES][2];
1072 * special array for maximum rust damage to armor from rustmonster
1073 * format is: { armor type , minimum attribute
1075 #define ARMORTYPES 6
1076 static char rustarm[ARMORTYPES][2] = { OSTUDLEATHER,-2, ORING,-4, OCHAIN,-5,
1077 OSPLINT,-6, OPLATE,-8, OPLATEARMOR,-9 };
1078 static char spsel[] = { 1, 2, 3, 5, 6, 8, 9, 11, 13, 14 };
1084 if (c[CANCELLATION]) return(0);
1085 vxy(&xx,&yy); /* verify x & y coordinates */
1088 case 1: /* rust your armor, j=1 when rusting has occurred */
1090 if ((i=c[SHIELD]) != -1)
1092 if (--ivenarg[i] < -1) ivenarg[i]= -1; else j=1;
1094 if ((j==0) && (k != -1))
1097 for (i=0; i<ARMORTYPES; i++)
1098 if (m == rustarm[i][0]) /* find his armor in table */
1100 if (--ivenarg[k]< rustarm[i][1])
1101 ivenarg[k]= rustarm[i][1]; else j=1;
1105 if (j==0) /* if rusting did not occur */
1108 case OLEATHER: p = "\nThe %s hit you -- Your lucky you have leather on";
1110 case OSSPLATE: p = "\nThe %s hit you -- Your fortunate to have stainless steel armor!";
1113 else { beep(); p = "\nThe %s hit you -- your armor feels weaker"; }
1116 case 2: i = rnd(15)+8-c[AC];
1117 spout: p="\nThe %s breathes fire at you!";
1118 if (c[FIRERESISTANCE])
1119 p="\nThe %s's flame doesn't phase you!";
1121 spout2: if (p) { lprintf(p,lastmonst); beep(); }
1125 case 3: i = rnd(20)+25-c[AC]; goto spout;
1127 case 4: if (c[STRENGTH]>3)
1129 p="\nThe %s stung you! You feel weaker"; beep();
1132 else p="\nThe %s stung you!";
1135 case 5: p="\nThe %s blasts you with his cold breath";
1136 i = rnd(15)+18-c[AC]; goto spout2;
1138 case 6: lprintf("\nThe %s drains you of your life energy!",lastmonst);
1139 loselevel(); beep(); return(0);
1141 case 7: p="\nThe %s got you with a gusher!";
1142 i = rnd(15)+25-c[AC]; goto spout2;
1144 case 8: if (c[NOTHEFT]) return(0); /* he has a device of no theft */
1147 p="\nThe %s hit you -- Your purse feels lighter";
1148 if (c[GOLD]>32767) c[GOLD]>>=1;
1149 else c[GOLD] -= rnd((int)(1+(c[GOLD]>>1)));
1150 if (c[GOLD] < 0) c[GOLD]=0;
1152 else p="\nThe %s couldn't find any gold to steal";
1153 lprintf(p,lastmonst); disappear(xx,yy); beep();
1154 bottomgold(); return(1);
1156 case 9: for(j=50; ; ) /* disenchant */
1158 i=rund(26); m=iven[i]; /* randomly select item */
1159 if (m>0 && ivenarg[i]>0 && m!=OSCROLL && m!=OPOTION)
1161 if ((ivenarg[i] -= 3)<0) ivenarg[i]=0;
1162 lprintf("\nThe %s hits you -- you feel a sense of loss",lastmonst);
1163 srcount=0; beep(); show3(i); bottomline(); return(0);
1167 p="\nThe %s nearly misses"; break;
1173 case 10: p="\nThe %s hit you with his barbed tail";
1174 i = rnd(25)-c[AC]; goto spout2;
1176 case 11: p="\nThe %s has confused you"; beep();
1177 c[CONFUSE]+= 10+rnd(10); break;
1179 case 12: /* performs any number of other special attacks */
1180 return(spattack(spsel[rund(10)],xx,yy));
1182 case 13: p="\nThe %s flattens you with his psionics!";
1183 i = rnd(15)+30-c[AC]; goto spout2;
1185 case 14: if (c[NOTHEFT]) return(0); /* he has device of no theft */
1186 if (emptyhanded()==1)
1188 p="\nThe %s couldn't find anything to steal";
1191 lprintf("\nThe %s picks your pocket and takes:",lastmonst);
1193 if (stealsomething()==0) lprcat(" nothing"); disappear(xx,yy);
1194 bottomline(); return(1);
1196 case 15: i= rnd(10)+ 5-c[AC];
1197 spout3: p="\nThe %s bit you!";
1200 case 16: i= rnd(15)+10-c[AC]; goto spout3;
1202 if (p) { lprintf(p,lastmonst); bottomline(); }
1207 * checkloss(x) Routine to subtract hp from user and flag bottomline display
1210 * Routine to subtract hitpoints from the user and flag the bottomline display
1211 * Enter with the number of hit points to lose
1212 * Note: if x > c[HP] this routine could kill the player!
1217 if (x>0) { losehp(x); bottomhp(); }
1221 * annihilate() Routine to annihilate all monsters around player (playerx,playery)
1223 * Gives player experience, but no dropped objects
1224 * Returns the experience gained from all monsters killed
1231 for (k=0, i=playerx-1; i<=playerx+1; i++)
1232 for (j=playery-1; j<=playery+1; j++)
1233 if (!vxy(&i,&j)) /* if not out of bounds */
1235 if (*(p= &mitem[i][j])) /* if a monster there */
1238 k += monster[*p].experience; *p=know[i][j]=0;
1242 lprintf("\nThe %s barely escapes being annihilated!",monster[*p].name);
1243 hitp[i][j] = (hitp[i][j]>>1) + 1; /* lose half hit points*/
1248 lprcat("\nYou hear loud screams of agony!"); raiseexperience((long)k);
1254 * newsphere(x,y,dir,lifetime) Function to create a new sphere of annihilation
1255 * int x,y,dir,lifetime;
1257 * Enter with the coordinates of the sphere in x,y
1258 * the direction (0-8 diroffx format) in dir, and the lifespan of the
1259 * sphere in lifetime (in turns)
1260 * Returns the number of spheres currently in existence
1262 newsphere(x,y,dir,life)
1267 if (((sp=(struct sphere *)malloc(sizeof(struct sphere)))) == 0)
1268 return(c[SPHCAST]); /* can't malloc, therefore failure */
1269 if (dir>=9) dir=0; /* no movement if direction not found */
1270 if (level==0) vxy(&x,&y); /* don't go out of bounds */
1273 if (x<1) x=1; if (x>=MAXX-1) x=MAXX-2;
1274 if (y<1) y=1; if (y>=MAXY-1) y=MAXY-2;
1276 if ((m=mitem[x][y]) >= DEMONLORD+4) /* demons dispel spheres */
1278 know[x][y]=1; show1cell(x,y); /* show the demon (ha ha) */
1279 cursors(); lprintf("\nThe %s dispels the sphere!",monster[m].name);
1280 beep(); rmsphere(x,y); /* remove any spheres that are here */
1283 if (m==DISENCHANTRESS) /* disenchantress cancels spheres */
1285 cursors(); lprintf("\nThe %s causes cancellation of the sphere!",monster[m].name); beep();
1286 boom: sphboom(x,y); /* blow up stuff around sphere */
1287 rmsphere(x,y); /* remove any spheres that are here */
1290 if (c[CANCELLATION]) /* cancellation cancels spheres */
1292 cursors(); lprcat("\nAs the cancellation takes effect, you hear a great earth shaking blast!"); beep();
1295 if (item[x][y]==OANNIHILATION) /* collision of spheres detonates spheres */
1297 cursors(); lprcat("\nTwo spheres of annihilation collide! You hear a great earth shaking blast!"); beep();
1301 if (playerx==x && playery==y) /* collision of sphere and player! */
1304 lprcat("\nYou have been enveloped by the zone of nothingness!\n");
1305 beep(); rmsphere(x,y); /* remove any spheres that are here */
1306 nap(4000); died(258);
1308 item[x][y]=OANNIHILATION; mitem[x][y]=0; know[x][y]=1;
1309 show1cell(x,y); /* show the new sphere */
1310 sp->x=x; sp->y=y; sp->lev=level; sp->dir=dir; sp->lifetime=life; sp->p=0;
1311 if (spheres==0) spheres=sp; /* if first node in the sphere list */
1312 else /* add sphere to beginning of linked list */
1314 sp->p = spheres; spheres = sp;
1316 return(++c[SPHCAST]); /* one more sphere in the world */
1320 * rmsphere(x,y) Function to delete a sphere of annihilation from list
1323 * Enter with the coordinates of the sphere (on current level)
1324 * Returns the number of spheres currently in existence
1329 struct sphere *sp,*sp2=0;
1330 for (sp=spheres; sp; sp2=sp,sp=sp->p)
1331 if (level==sp->lev) /* is sphere on this level? */
1332 if ((x==sp->x) && (y==sp->y)) /* locate sphere at this location */
1334 item[x][y]=mitem[x][y]=0; know[x][y]=1;
1335 show1cell(x,y); /* show the now missing sphere */
1337 if (sp==spheres) { sp2=sp; spheres=sp->p; free((char*)sp2); }
1339 { sp2->p = sp->p; free((char*)sp); }
1342 return(c[SPHCAST]); /* return number of spheres in the world */
1346 * sphboom(x,y) Function to perform the effects of a sphere detonation
1349 * Enter with the coordinates of the blast, Returns no value
1355 if (c[HOLDMONST]) c[HOLDMONST]=1;
1356 if (c[CANCELLATION]) c[CANCELLATION]=1;
1357 for (j=max(1,x-2); j<min(x+3,MAXX-1); j++)
1358 for (i=max(1,y-2); i<min(y+3,MAXY-1); i++)
1360 item[j][i]=mitem[j][i]=0;
1362 if (playerx==j && playery==i)
1365 lprcat("\nYou were too close to the sphere!");
1367 died(283); /* player killed in explosion */
1373 * genmonst() Function to ask for monster and genocide from game
1375 * This is done by setting a flag in the monster[] structure
1380 cursors(); lprcat("\nGenocide what monster? ");
1381 for (i=0; (!isalpha(i)) && (i!=' '); i=getchar());
1383 for (j=0; j<MAXMONST; j++) /* search for the monster type */
1384 if (monstnamelist[j]==i) /* have we found it? */
1386 monster[j].genocided=1; /* genocided from game */
1387 lprintf(" There will be no more %s's",monster[j].name);
1388 /* now wipe out monsters on this level */
1389 newcavelevel(level); draws(0,MAXX,0,MAXY); bot_linex();
1392 lprcat(" You sense failure!");