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 /* 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 */
109 static int cgood(int, int, int, int);
110 static void speldamage(int);
111 static void loseint(void);
112 static long isconfuse(void);
113 static int nospell(int, int);
114 static int fullhit(int);
115 static void direct(int, int, const char *, int);
116 static void ifblind(int, int);
117 static void tdirect(int);
118 static void omnidirect(int, int, const char *);
119 static int dirsub(int *, int *);
120 static void dirpoly(int);
121 static void dropsomething(int);
122 static int spattack(int, int, int);
123 static void sphboom(int, int);
124 static void genmonst(void);
127 * createmonster(monstno) Function to create a monster next to the player
130 * Enter with the monster number (1 to MAXMONST+8)
134 createmonster(int mon)
137 if (mon < 1 || mon > MAXMONST + 8) { /* check for monster number out of bounds */
139 lprintf("\ncan't createmonst(%d)\n", (long)mon);
143 while (mon < MAXMONST && monster[mon].genocided) /* genocided? */
145 for (k = rnd(8), i = -8; i < 0; i++, k++) { /* choose direction, then try all */
146 if (k > 8) /* wraparound the diroff arrays */
148 x = playerx + diroffx[k];
149 y = playery + diroffy[k];
150 if (cgood(x, y, 0, 1)) { /* if we can create here */
152 hitp[x][y] = monster[mon].hitpoints;
153 stealth[x][y] = know[x][y] = 0;
166 * int cgood(x,y,itm,monst) Function to check location for emptiness
169 * Routine to return TRUE if a location does not have itm or monst there
170 * returns FALSE (0) otherwise
171 * Enter with itm or monst TRUE or FALSE if checking it
172 * Example: if itm==TRUE check for no item at this location
173 * if monst==TRUE check for no monster at this location
174 * This routine will return FALSE if at a wall or the dungeon exit on level 1
177 cgood(int x, int y, int itm, int monst)
179 if ((y >= 0) && (y <= MAXY - 1) && (x >= 0) && (x <= MAXX - 1))
181 if (item[x][y] != OWALL) /* can't make anything on walls */
182 /* is it free of items? */
183 if (itm == 0 || (item[x][y] == 0))
184 /* is it free of monsters? */
185 if (monst == 0 || (mitem[x][y] == 0))
186 if ((level != 1) || (x != 33) ||
188 /* not exit to level 1 */
194 * createitem(it,arg) Routine to place an item next to the player
197 * Enter with the item number and its argument (iven[], ivenarg[])
198 * Returns no value, thus we don't know about createitem() failures.
201 createitem(int it, int arg)
204 if (it >= MAXOBJ) /* no such object */
206 for (k = rnd(8), i = -8; i < 0; i++, k++) { /* choose direction, then try all */
207 if (k > 8) /* wraparound the diroff arrays */
209 x = playerx + diroffx[k];
210 y = playery + diroffy[k];
211 if (cgood(x, y, 1, 0)) { /* if we can create here */
221 * cast() Subroutine called by parse to cast a spell for the user
223 * No arguments and no return value.
225 static const char eys[] = "\nEnter your spell: ";
232 if (c[SPELLS] <= 0) {
233 lprcat("\nYou don't have any spells!");
238 while ((a = getchr()) == 'D') {
243 if (a == '\33') /* to escape casting a spell */
245 if ((b = getchr()) == '\33') /* to escape casting a spell */
247 if ((d = getchr()) == '\33') {
248 over: lprcat(aborted);
251 } /* to escape casting a spell */
255 for (lprc('\n'), j = -1, i = 0; i < SPNUM; i++)
256 /* seq search for his spell, hash? */
257 if ((spelcode[i][0] == a) && (spelcode[i][1] == b) && (spelcode[i][2] == d))
264 lprcat(" Nothing Happened ");
269 * speldamage(x) Function to perform spell functions cast by the player
272 * Enter with the spell number, returns no value.
273 * Please insure that there are 2 spaces before all messages here
283 if (x >= SPNUM) /* no such spell */
286 lprcat(" It didn't seem to work");
288 } /* not if time stopped */
290 if ((rnd(23) == 7) || (rnd(18) > c[INTELLIGENCE])) {
291 lprcat(" It didn't work!");
294 if (clev * 3 + 2 < x) {
295 lprcat(" Nothing happens. You seem inexperienced at this");
300 /* ----- LEVEL 1 SPELLS ----- */
302 case 0: /* protection field +2 */
303 if (c[PROTECTIONTIME] == 0)
304 c[MOREDEFENSES] += 2;
305 c[PROTECTIONTIME] += 250;
308 case 1: /* magic missile */
309 i = rnd(((clev + 1) << 1)) + clev + 3;
310 godirect(x, i, (clev >= 2) ? " Your missiles hit the %s" : " Your missile hit the %s", 100, '+');
314 case 2: /* dexterity */
315 if (c[DEXCOUNT] == 0)
322 cp = " While the %s slept, you smashed it %d times";
323 ws: direct(x, fullhit(i), cp, i);
326 case 4: /* charm monster */
327 c[CHARMCOUNT] += c[CHARISMA] << 1;
330 case 5: /* sonic spear */
331 godirect(x, rnd(10) + 15 + clev, " The sound damages the %s", 70, '@');
334 /* ----- LEVEL 2 SPELLS ----- */
338 cp = " While the %s is entangled, you hit %d times";
341 case 7: /* strength */
342 if (c[STRCOUNT] == 0)
344 c[STRCOUNT] += 150 + rnd(100);
347 case 8: /* enlightenment */
353 vxy(&xh, &yh); /* check bounds */
354 for (i = yl; i <= yh; i++) /* enlightenment */
355 for (j = xl; j <= xh; j++)
357 draws(xl, xh + 1, yl, yh + 1);
360 case 9: /* healing */
361 raisehp(20 + (clev << 1));
364 case 10: /* cure blindness */
369 createmonster(makemonst(level + 1) + 8);
373 if (rnd(11) + 7 <= c[WISDOM])
374 direct(x, rnd(20) + 20 + clev, " The %s believed!", 0);
376 lprcat(" It didn't believe the illusions!");
379 case 13: /* if he has the amulet of invisibility then add more time */
380 for (j = i = 0; i < 26; i++)
381 if (iven[i] == OAMULET)
383 c[INVISIBILITY] += (j << 7) + 12;
386 /* ----- LEVEL 3 SPELLS ----- */
388 case 14: /* fireball */
389 godirect(x, rnd(25 + clev) + 25 + clev, " The fireball hits the %s", 40, '*');
393 godirect(x, rnd(25) + 20 + clev, " Your cone of cold strikes the %s", 60, 'O');
396 case 16: /* polymorph */
400 case 17: /* cancellation */
401 c[CANCELLATION] += 5 + clev;
404 case 18: /* haste self */
405 c[HASTESELF] += 7 + clev;
408 case 19: /* cloud kill */
409 omnidirect(x, 30 + rnd(10), " The %s gasps for air");
412 case 20: /* vaporize rock */
413 xh = min(playerx + 1, MAXX - 2);
414 yh = min(playery + 1, MAXY - 2);
415 for (i = max(playerx - 1, 1); i <= xh; i++)
416 for (j = max(playery - 1, 1); j <= yh; j++) {
419 switch (*(p = &item[i][j])) {
421 if (level < MAXLEVEL + MAXVLEVEL - 1)
426 if (c[HARDGAME] < 3) {
437 hitp[i][j] = monster[GNOMEKING].hitpoints;
443 hitp[i][j] = monster[DEMONPRINCE].hitpoints;
450 break; /* Xorn takes damage from vpr */
455 /* ----- LEVEL 4 SPELLS ----- */
457 case 21: /* dehydration */
458 direct(x, 100 + clev, " The %s shrivels up", 0);
461 case 22: /* lightning */
462 godirect(x, rnd(25) + 20 + (clev << 1), " A lightning bolt hits the %s", 1, '~');
465 case 23: /* drain life */
466 i = min(c[HP] - 1, c[HPMAX] / 2);
467 direct(x, i + i, "", 0);
471 case 24: /* globe of invulnerability */
473 c[MOREDEFENSES] += 10;
479 omnidirect(x, 32 + clev, " The %s struggles for air in your flood!");
482 case 26: /* finger of death */
483 if (rnd(151) == 63) {
485 lprcat("\nYour heart stopped!\n");
490 if (c[WISDOM] > rnd(10) + 10)
491 direct(x, 2000, " The %s's heart stopped", 0);
493 lprcat(" It didn't work");
496 /* ----- LEVEL 5 SPELLS ----- */
498 case 27: /* scare monster */
499 c[SCAREMONST] += rnd(10) + clev;
502 case 28: /* hold monster */
503 c[HOLDMONST] += rnd(10) + clev;
506 case 29: /* time stop */
507 c[TIMESTOP] += rnd(20) + (clev << 1);
510 case 30: /* teleport away */
514 case 31: /* magic fire */
515 omnidirect(x, 35 + rnd(10) + clev, " The %s cringes from the flame");
518 /* ----- LEVEL 6 SPELLS ----- */
520 case 32: /* sphere of annihilation */
521 if ((rnd(23) == 5) && (wizard == 0)) {
523 lprcat("\nYou have been enveloped by the zone of nothingness!\n");
531 i = dirsub(&xl, &yl); /* get direction of sphere */
532 newsphere(xl, yl, i, rnd(20) + 11); /* make a sphere */
535 case 33: /* genocide */
541 case 34: /* summon demon */
543 direct(x, 150, " The demon strikes at the %s", 0);
547 lprcat(" Nothing seems to have happened");
550 lprcat(" The demon turned on you and vanished!");
554 losehp(i); /* must say killed by a demon */
557 case 35: /* walk through walls */
558 c[WTW] += rnd(10) + 5;
561 case 36: /* alter reality */
563 struct isave *save; /* pointer to item save structure */
565 sc = 0; /* # items saved */
566 save = malloc(sizeof(struct isave) * MAXX * MAXY * 2);
567 for (j = 0; j < MAXY; j++)
568 for (i = 0; i < MAXX; i++) { /* save all items and monsters */
570 if (xl && xl != OWALL && xl != OANNIHILATION) {
572 save[sc].id = item[i][j];
573 save[sc++].arg = iarg[i][j];
577 save[sc].id = mitem[i][j];
578 save[sc++].arg = hitp[i][j];
589 item[33][MAXY - 1] = 0;
590 for (j = rnd(MAXY - 2), i = 1; i < MAXX - 1; i++)
592 while (sc > 0) { /* put objects back in level */
594 if (save[sc].type == 0) {
596 for (trys = 100, i = j = 1; --trys > 0 && item[i][j]; i = rnd(MAXX - 1), j = rnd(MAXY - 1))
599 item[i][j] = save[sc].id;
600 iarg[i][j] = save[sc].arg;
602 } else { /* put monsters back in */
604 for (trys = 100, i = j = 1; --trys > 0 && (item[i][j] == OWALL || mitem[i][j]); i = rnd(MAXX - 1), j = rnd(MAXY - 1))
607 mitem[i][j] = save[sc].id;
608 hitp[i][j] = save[sc].arg;
613 draws(0, MAXX, 0, MAXY);
621 case 37: /* permanence */
622 larn_adjtime(-99999L);
623 spelknow[37] = 0; /* forget */
628 lprintf(" spell %d not available!", (long)x);
635 * loseint() Routine to subtract 1 from your int (intelligence) if > 3
637 * No arguments and no return value
642 if (--c[INTELLIGENCE] < 3)
647 * isconfuse() Routine to check to see if player is confused
649 * This routine prints out a message saying "You can't aim your magic!"
650 * returns 0 if not confused, non-zero (time remaining confused) if confused
656 lprcat(" You can't aim your magic!");
663 * nospell(x,monst) Routine to return 1 if a spell doesn't affect a monster
666 * Subroutine to return 1 if the spell can't affect the monster
667 * otherwise returns 0
668 * Enter with the spell number in x, and the monster number in monst.
671 nospell(int x, int monst)
674 if (x >= SPNUM || monst >= MAXMONST + 8 || monst < 0 || x < 0)
675 return (0); /* bad spell or monst */
676 if ((tmp = spelweird[monst - 1][x]) == 0)
680 lprintf(spelmes[tmp], monster[monst].name);
685 * fullhit(xx) Function to return full damage against a monster (aka web)
688 * Function to return hp damage to monster due to a number of full hits
689 * Enter with the number of full hits being done
695 if (xx < 0 || xx > 20) /* fullhits are out of range */
697 if (c[LANCEDEATH]) /* lance of death */
699 i = xx * ((c[WCLASS] >> 1) + c[STRENGTH] + c[STREXTRA] - c[HARDGAME] - 12 + c[MOREDAM]);
700 return ((i >= 1) ? i : xx);
704 * direct(spnum,dam,str,arg) Routine to direct spell damage 1 square in 1 dir
708 * Routine to ask for a direction to a spell and then hit the monster
709 * Enter with the spell number in spnum, the damage to be done in dam,
710 * lprintf format string in str, and lprintf's argument in arg.
714 direct(int spnum, int dam, const char *str, int arg)
718 if (spnum < 0 || spnum >= SPNUM || str == NULL) /* bad arguments */
724 if (item[x][y] == OMIRROR) {
725 if (spnum == 3) { /* sleep */
726 lprcat("You fall asleep! ");
735 } else if (spnum == 6) { /* web */
736 lprcat("You get stuck in your own web! ");
741 lprintf(str, "spell caster (thats you)", (long)arg);
748 lprcat(" There wasn't anything there!");
752 if (nospell(spnum, m)) {
757 lprintf(str, lastmonst, (long)arg);
762 * godirect(spnum,dam,str,delay,cshow) Function to perform missile attacks
763 * int spnum,dam,delay;
766 * Function to hit in a direction from a missile weapon and have it keep
767 * on going in that direction until its power is exhausted
768 * Enter with the spell number in spnum, the power of the weapon in hp,
769 * lprintf format string in str, the # of milliseconds to delay between
770 * locations in delay, and the character to represent the weapon in cshow.
774 godirect(int spnum, int dam, const char *str, int delay, char cshow)
779 if (spnum < 0 || spnum >= SPNUM || str == NULL || delay < 0) /* bad args */
793 if ((x > MAXX - 1) || (y > MAXY - 1) || (x < 0) || (y < 0)) {
795 break; /* out of bounds */
797 if ((x == playerx) && (y == playery)) { /* if energy hits player */
799 lprcat("\nYou are hit my your own magic!");
805 if (c[BLINDCOUNT] == 0) { /* if not blind show effect */
806 cursor(x + 1, y + 1);
811 if ((m = mitem[x][y])) { /* is there a monster there? */
813 if (nospell(spnum, m)) {
820 lprintf(str, lastmonst);
821 dam -= hitm(x, y, dam);
827 switch (*(p = &item[x][y])) {
831 lprintf(str, "wall");
832 if (dam >= 50 + c[HARDGAME]) /* enough damage? */
833 if (level < MAXLEVEL + MAXVLEVEL - 1) /* not on V3 */
834 if ((x < MAXX - 1) && (y < MAXY - 1) && (x) && (y)) {
835 lprcat(" The wall crumbles");
846 lprintf(str, "door");
848 lprcat(" The door is blasted apart");
856 lprintf(str, "statue");
859 lprcat(" The statue crumbles");
869 lprintf(str, "throne");
871 mitem[x][y] = GNOMEKING;
872 hitp[x][y] = monster[GNOMEKING].hitpoints;
883 dam -= 3 + (c[HARDGAME] >> 1);
888 * ifblind(x,y) Routine to put "monster" or the monster name into lastmosnt
891 * Subroutine to copy the word "monster" into lastmonst if the player is blind
892 * Enter with the coordinates (x,y) of the monster
896 ifblind(int x, int y)
900 vxy(&x, &y); /* verify correct x,y coordinates */
905 lastnum = mitem[x][y];
906 p = monster[lastnum].name;
908 strcpy(lastmonst, p);
912 * tdirect(spnum) Routine to teleport away a monster
915 * Routine to ask for a direction to a spell and then teleport away monster
916 * Enter with the spell number that wants to teleport away
924 if (spnum < 0 || spnum >= SPNUM) /* bad args */
929 if ((m = mitem[x][y]) == 0) {
930 lprcat(" There wasn't anything there!");
934 if (nospell(spnum, m)) {
940 mitem[x][y] = know[x][y] = 0;
944 * omnidirect(sp,dam,str) Routine to damage all monsters 1 square from player
948 * Routine to cast a spell and then hit the monster in all directions
949 * Enter with the spell number in sp, the damage done to wach square in dam,
950 * and the lprintf string to identify the spell in str.
954 omnidirect(int spnum, int dam, const char *str)
957 if (spnum < 0 || spnum >= SPNUM || str == NULL) /* bad args */
959 for (x = playerx - 1; x < playerx + 2; x++)
960 for (y = playery - 1; y < playery + 2; y++) {
961 if ((m = mitem[x][y]) != 0) {
962 if (nospell(spnum, m) == 0) {
966 lprintf(str, lastmonst);
978 * static dirsub(x,y) Routine to ask for direction, then modify x,y for it
981 * Function to ask for a direction and modify an x,y for that direction
982 * Enter with the origination coordinates in (x,y).
983 * Returns index into diroffx[] (0-8).
986 dirsub(int *x, int *y)
989 lprcat("\nIn What Direction? ");
1011 *x = playerx + diroffx[i];
1012 *y = playery + diroffy[i];
1018 * vxy(x,y) Routine to verify/fix coordinates for being within bounds
1021 * Function to verify x & y are within the bounds for a level
1022 * If *x or *y is not within the absolute bounds for a level, fix them so that
1023 * they are on the level.
1024 * Returns TRUE if it was out of bounds, and the *x & *y in the calling
1025 * routine are affected.
1051 * dirpoly(spnum) Routine to ask for a direction and polymorph a monst
1054 * Subroutine to polymorph a monster and ask for the direction its in
1055 * Enter with the spell number in spmun.
1062 if (spnum < 0 || spnum >= SPNUM) /* bad args */
1064 if (isconfuse()) /* if he is confused, he can't aim his magic */
1067 if (mitem[x][y] == 0) {
1068 lprcat(" There wasn't anything there!");
1072 if (nospell(spnum, mitem[x][y])) {
1077 while (monster[m = mitem[x][y] = rnd(MAXMONST + 7)].genocided)
1079 hitp[x][y] = monster[m].hitpoints;
1080 show1cell(x, y); /* show the new monster */
1084 * hitmonster(x,y) Function to hit a monster at the designated coordinates
1087 * This routine is used for a bash & slash type attack on a monster
1088 * Enter with the coordinates of the monster in (x,y).
1092 hitmonster(int x, int y)
1094 int tmp, monst, damag = 0, flag;
1095 if (c[TIMESTOP]) /* not if time stopped */
1097 vxy(&x, &y); /* verify coordinates are within range */
1098 if ((monst = mitem[x][y]) == 0)
1102 tmp = monster[monst].armorclass + c[LEVEL] + c[DEXTERITY] +
1105 /* need at least random chance to hit */
1106 if ((rnd(20) < tmp - c[HARDGAME]) || (rnd(71) < 5)) {
1107 lprcat("\nYou hit");
1111 damag = rnd(damag) + 1;
1113 lprcat("\nYou missed");
1118 if (flag) /* if the monster was hit */
1119 if ((monst == RUSTMONSTER) || (monst == DISENCHANTRESS) || (monst == CUBE))
1121 if (ivenarg[c[WIELD]] > -10) {
1122 lprintf("\nYour weapon is dulled by the %s", lastmonst);
1124 --ivenarg[c[WIELD]];
1128 if (monst == VAMPIRE)
1129 if (hitp[x][y] < 25) {
1136 * hitm(x,y,amt) Function to just hit a monster at a given coordinates
1139 * Returns the number of hitpoints the monster absorbed
1140 * This routine is used to specifically damage a monster at a location (x,y)
1141 * Called by hitmonster(x,y)
1144 hitm(int x, int y, int amt)
1148 vxy(&x, &y); /* verify coordinates are within range */
1149 amt2 = amt; /* save initial damage so we can return it */
1150 monst = mitem[x][y];
1151 if (c[HALFDAM]) /* if half damage curse adjust damage points */
1157 stealth[x][y] = 1; /* make sure hitting monst breaks stealth condition */
1158 c[HOLDMONST] = 0; /* hit a monster breaks hold monster spell */
1159 switch (monst) { /* if a dragon and orb(s) of dragon slaying */
1164 case PLATINUMDRAGON:
1166 amt *= 1 + (c[SLAYING] << 1);
1169 /* invincible monster fix is here */
1170 if (hitp[x][y] > monster[monst].hitpoints)
1171 hitp[x][y] = monster[monst].hitpoints;
1172 if ((hpoints = hitp[x][y]) <= amt) {
1176 lprintf("\nThe %s died!", lastmonst);
1177 raiseexperience((long)monster[monst].experience);
1178 amt = monster[monst].gold;
1180 dropgold(rnd(amt) + amt);
1181 dropsomething(monst);
1186 hitp[x][y] = hpoints - amt;
1191 * hitplayer(x,y) Function for the monster to hit the player from (x,y)
1194 * Function for the monster to hit the player with monster at location x,y
1195 * Returns nothing of value.
1198 hitplayer(int x, int y)
1200 int dam, tmp, mster, bias;
1201 vxy(&x, &y); /* verify coordinates are within range */
1202 lastnum = mster = mitem[x][y];
1203 /* spirit naga's and poltergeist's do nothing if scarab of negate spirit */
1204 if (c[NEGATESPIRIT] || c[SPIRITPRO])
1205 if ((mster == POLTERGEIST) || (mster == SPIRITNAGA))
1207 /* if undead and cube of undead control */
1208 if (c[CUBEofUNDEAD] || c[UNDEADPRO])
1209 if ((mster == VAMPIRE) || (mster == WRAITH) || (mster == ZOMBIE))
1211 if ((know[x][y] & 1) == 0) {
1215 bias = (c[HARDGAME]) + 1;
1216 hitflag = hit2flag = hit3flag = 1;
1220 if (c[INVISIBILITY])
1222 lprintf("\nThe %s misses wildly", lastmonst);
1226 if (rnd(30) + 5 * monster[mster].level - c[CHARISMA] < 30) {
1227 lprintf("\nThe %s is awestruck at your magnificence!", lastmonst);
1233 dam = monster[mster].damage;
1234 dam += rnd((int)((dam < 1) ? 1 : dam)) + monster[mster].level;
1237 if (monster[mster].attack > 0)
1238 if (((dam + bias + 8) > c[AC]) || (rnd((int)((c[AC] > 0) ? c[AC] : 1)) == 1)) {
1239 if (spattack(monster[mster].attack, x, y)) {
1247 if (((dam + bias) > c[AC]) || (rnd((int)((c[AC] > 0) ? c[AC] : 1)) == 1)) {
1248 lprintf("\n The %s hit you ", lastmonst);
1250 if ((dam -= c[AC]) < 0)
1259 lprintf("\n The %s missed ", lastmonst);
1263 * dropsomething(monst) Function to create an object when a monster dies
1266 * Function to create an object near the player when certain monsters are killed
1267 * Enter with the monster number
1268 * Returns nothing of value.
1271 dropsomething(int monst)
1281 case PLATINUMDRAGON:
1291 dropsomething(LEPRECHAUN);
1297 * dropgold(amount) Function to drop some gold around player
1300 * Enter with the number of gold pieces to drop
1301 * Returns nothing of value.
1304 dropgold(int amount)
1307 createitem(OMAXGOLD, amount / 100);
1309 createitem(OGOLDPILE, amount);
1313 * something(lvl) Function to create a random item around player
1316 * Function to create an item from a designed probability around player
1317 * Enter with the cave level on which something is to be dropped
1318 * Returns nothing of value.
1325 if (lvl < 0 || lvl > MAXLEVEL + MAXVLEVEL) /* correct level? */
1327 if (rnd(101) < 8) /* possibly more than one item */
1329 j = newobject(lvl, &i);
1334 * newobject(lev,i) Routine to return a randomly selected new object
1337 * Routine to return a randomly selected object to be created
1338 * Returns the object number created, and sets *i for its argument
1339 * Enter with the cave level and a pointer to the items arg
1341 static char nobjtab[] = { 0, OSCROLL, OSCROLL, OSCROLL, OSCROLL, OPOTION,
1342 OPOTION, OPOTION, OPOTION, OGOLDPILE, OGOLDPILE, OGOLDPILE, OGOLDPILE,
1343 OBOOK, OBOOK, OBOOK, OBOOK, ODAGGER, ODAGGER, ODAGGER, OLEATHER, OLEATHER,
1344 OLEATHER, OREGENRING, OPROTRING, OENERGYRING, ODEXRING, OSTRRING, OSPEAR,
1345 OBELT, ORING, OSTUDLEATHER, OSHIELD, OFLAIL, OCHAIN, O2SWORD, OPLATE,
1349 newobject(int lev, int *i)
1352 if (level < 0 || level > MAXLEVEL + MAXVLEVEL) /* correct level? */
1358 j = nobjtab[tmp = rnd(tmp)]; /* the object type */
1376 *i = rnd((lev + 1) * 10) + lev * 10 + 10;
1387 if (!(*i = newdagger()))
1393 if (!(*i = newleather()))
1399 *i = rund(lev / 3 + 1);
1403 *i = rnd(lev / 4 + 1);
1406 *i = rund(lev / 4 + 1);
1409 *i = rnd(lev / 2 + 1);
1413 *i = rund(lev / 2 + 1);
1416 *i = rund(lev / 3 + 1);
1422 *i = rund(lev / 2 + 1);
1440 * spattack(atckno,xx,yy) Function to process special attacks from monsters
1443 * Enter with the special attack number, and the coordinates (xx,yy)
1444 * of the monster that is special attacking
1445 * Returns 1 if must do a show1cell(xx,yy) upon return, 0 otherwise
1447 * atckno monster effect
1448 * ---------------------------------------------------
1450 * 1 rust monster eat armor
1451 * 2 hell hound breathe light fire
1452 * 3 dragon breathe fire
1453 * 4 giant centipede weakening sing
1454 * 5 white dragon cold breath
1455 * 6 wraith drain level
1456 * 7 waterlord water gusher
1457 * 8 leprechaun steal gold
1458 * 9 disenchantress disenchant weapon or armor
1459 * 10 ice lizard hits with barbed tail
1460 * 11 umber hulk confusion
1461 * 12 spirit naga cast spells taken from special attacks
1462 * 13 platinum dragon psionics
1463 * 14 nymph steal objects
1467 * char rustarm[ARMORTYPES][2];
1468 * special array for maximum rust damage to armor from rustmonster
1469 * format is: { armor type , minimum attribute
1471 #define ARMORTYPES 6
1472 static char rustarm[ARMORTYPES][2] = {
1473 { OSTUDLEATHER, -2 },
1480 static char spsel[] = { 1, 2, 3, 5, 6, 8, 9, 11, 13, 14 };
1483 spattack(int x, int xx, int yy)
1486 const char *p = NULL;
1488 if (c[CANCELLATION])
1490 vxy(&xx, &yy); /* verify x & y coordinates */
1492 case 1: /* rust your armor, j=1 when rusting has occurred */
1494 if ((i = c[SHIELD]) != -1) {
1495 if (--ivenarg[i] < -1)
1500 if ((j == 0) && (k != -1)) {
1502 for (i = 0; i < ARMORTYPES; i++)
1503 /* find his armor in table */
1504 if (m == rustarm[i][0]) {
1505 if (--ivenarg[k] < rustarm[i][1])
1506 ivenarg[k] = rustarm[i][1];
1512 if (j == 0) /* if rusting did not occur */
1515 p = "\nThe %s hit you -- Your lucky you have leather on";
1518 p = "\nThe %s hit you -- Your fortunate to have stainless steel armor!";
1523 p = "\nThe %s hit you -- your armor feels weaker";
1528 i = rnd(15) + 8 - c[AC];
1529 spout: p = "\nThe %s breathes fire at you!";
1530 if (c[FIRERESISTANCE])
1531 p = "\nThe %s's flame doesn't phase you!";
1534 lprintf(p, lastmonst);
1541 i = rnd(20) + 25 - c[AC];
1545 if (c[STRENGTH] > 3) {
1546 p = "\nThe %s stung you! You feel weaker";
1550 p = "\nThe %s stung you!";
1554 p = "\nThe %s blasts you with his cold breath";
1555 i = rnd(15) + 18 - c[AC];
1559 lprintf("\nThe %s drains you of your life energy!", lastmonst);
1565 p = "\nThe %s got you with a gusher!";
1566 i = rnd(15) + 25 - c[AC];
1570 if (c[NOTHEFT]) /* he has a device of no theft */
1573 p = "\nThe %s hit you -- Your purse feels lighter";
1574 if (c[GOLD] > 32767)
1577 c[GOLD] -= rnd((int)(1 + (c[GOLD] >> 1)));
1581 p = "\nThe %s couldn't find any gold to steal";
1582 lprintf(p, lastmonst);
1589 for (j = 50;;) { /* disenchant */
1591 m = iven[i]; /* randomly select item */
1592 if (m > 0 && ivenarg[i] > 0 && m != OSCROLL && m != OPOTION) {
1593 if ((ivenarg[i] -= 3) < 0)
1595 lprintf("\nThe %s hits you -- you feel a sense of loss", lastmonst);
1603 p = "\nThe %s nearly misses";
1611 p = "\nThe %s hit you with his barbed tail";
1612 i = rnd(25) - c[AC];
1616 p = "\nThe %s has confused you";
1618 c[CONFUSE] += 10 + rnd(10);
1621 case 12: /* performs any number of other special attacks */
1622 return (spattack(spsel[rund(10)], xx, yy));
1625 p = "\nThe %s flattens you with his psionics!";
1626 i = rnd(15) + 30 - c[AC];
1630 if (c[NOTHEFT]) /* he has device of no theft */
1632 if (emptyhanded() == 1) {
1633 p = "\nThe %s couldn't find anything to steal";
1636 lprintf("\nThe %s picks your pocket and takes:", lastmonst);
1638 if (stealsomething() == 0)
1645 i = rnd(10) + 5 - c[AC];
1646 spout3: p = "\nThe %s bit you!";
1650 i = rnd(15) + 10 - c[AC];
1654 lprintf(p, lastmonst);
1661 * checkloss(x) Routine to subtract hp from user and flag bottomline display
1664 * Routine to subtract hitpoints from the user and flag the bottomline display
1665 * Enter with the number of hit points to lose
1666 * Note: if x > c[HP] this routine could kill the player!
1678 * annihilate() Routine to annihilate all monsters around player (playerx,playery)
1680 * Gives player experience, but no dropped objects
1681 * Returns the experience gained from all monsters killed
1689 for (k = 0, i = playerx - 1; i <= playerx + 1; i++)
1690 for (j = playery - 1; j <= playery + 1; j++)
1691 if (!vxy(&i, &j)) { /* if not out of bounds */
1692 if (*(p = &mitem[i][j])) { /* if a monster there */
1693 if (*p < DEMONLORD + 2) {
1694 k += monster[(int)*p].experience;
1695 *p = know[i][j] = 0;
1697 lprintf("\nThe %s barely escapes being annihilated!", monster[(int)*p].name);
1698 hitp[i][j] = (hitp[i][j] >> 1) + 1; /* lose half hit points*/
1703 lprcat("\nYou hear loud screams of agony!");
1704 raiseexperience((long)k);
1710 * newsphere(x,y,dir,lifetime) Function to create a new sphere of annihilation
1711 * int x,y,dir,lifetime;
1713 * Enter with the coordinates of the sphere in x,y
1714 * the direction (0-8 diroffx format) in dir, and the lifespan of the
1715 * sphere in lifetime (in turns)
1716 * Returns the number of spheres currently in existence
1719 newsphere(int x, int y, int dir, int life)
1723 if (((sp = malloc(sizeof(struct sphere)))) == NULL)
1724 return (c[SPHCAST]); /* can't malloc, therefore failure */
1725 if (dir >= 9) /* no movement if direction not found */
1727 if (level == 0) /* don't go out of bounds */
1739 if ((m = mitem[x][y]) >= DEMONLORD + 4) { /* demons dispel spheres */
1741 show1cell(x, y); /* show the demon (ha ha) */
1743 lprintf("\nThe %s dispels the sphere!", monster[m].name);
1745 rmsphere(x, y); /* remove any spheres that are here */
1746 return (c[SPHCAST]);
1748 if (m == DISENCHANTRESS) { /* disenchantress cancels spheres */
1750 lprintf("\nThe %s causes cancellation of the sphere!", monster[m].name);
1752 boom: sphboom(x, y); /* blow up stuff around sphere */
1753 rmsphere(x, y); /* remove any spheres that are here */
1754 return (c[SPHCAST]);
1756 if (c[CANCELLATION]) { /* cancellation cancels spheres */
1758 lprcat("\nAs the cancellation takes effect, you hear a great earth shaking blast!");
1762 if (item[x][y] == OANNIHILATION) { /* collision of spheres detonates spheres */
1764 lprcat("\nTwo spheres of annihilation collide! You hear a great earth shaking blast!");
1769 if (playerx == x && playery == y) { /* collision of sphere and player! */
1771 lprcat("\nYou have been enveloped by the zone of nothingness!\n");
1773 rmsphere(x, y); /* remove any spheres that are here */
1777 item[x][y] = OANNIHILATION;
1780 show1cell(x, y); /* show the new sphere */
1785 sp->lifetime = life;
1787 if (spheres == 0) /* if first node in the sphere list */
1789 else { /* add sphere to beginning of linked list */
1793 return (++c[SPHCAST]); /* one more sphere in the world */
1797 * rmsphere(x,y) Function to delete a sphere of annihilation from list
1800 * Enter with the coordinates of the sphere (on current level)
1801 * Returns the number of spheres currently in existence
1804 rmsphere(int x, int y)
1806 struct sphere *sp, *sp2 = NULL;
1807 for (sp = spheres; sp; sp2 = sp, sp = sp->p)
1808 if (level == sp->lev) /* is sphere on this level? */
1809 if ((x == sp->x) && (y == sp->y)) { /* locate sphere at this location */
1810 item[x][y] = mitem[x][y] = 0;
1812 show1cell(x, y); /* show the now missing sphere */
1814 if (sp == spheres) {
1824 return (c[SPHCAST]); /* return number of spheres in the world */
1828 * sphboom(x,y) Function to perform the effects of a sphere detonation
1831 * Enter with the coordinates of the blast, Returns no value
1834 sphboom(int x, int y)
1839 if (c[CANCELLATION])
1840 c[CANCELLATION] = 1;
1841 for (j = max(1, x - 2); j < min(x + 3, MAXX - 1); j++)
1842 for (i = max(1, y - 2); i < min(y + 3, MAXY - 1); i++) {
1843 item[j][i] = mitem[j][i] = 0;
1845 if (playerx == j && playery == i) {
1848 lprcat("\nYou were too close to the sphere!");
1850 died(283); /* player killed in explosion */
1856 * genmonst() Function to ask for monster and genocide from game
1858 * This is done by setting a flag in the monster[] structure
1865 lprcat("\nGenocide what monster? ");
1866 for (i = 0; (!isalpha(i)) && (i != ' '); i = getchr())
1869 for (j = 0; j < MAXMONST; j++) /* search for the monster type */
1870 if (monstnamelist[j] == i) { /* have we found it? */
1871 monster[j].genocided = 1; /* genocided from game */
1872 lprintf(" There will be no more %s's", monster[j].name);
1873 /* now wipe out monsters on this level */
1874 newcavelevel(level);
1875 draws(0, MAXX, 0, MAXY);
1879 lprcat(" You sense failure!");