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 $
6 * This file contains the following functions:
7 * ----------------------------------------------------------------------------
9 * createmonster(monstno) Function to create a monster next to the player
12 * int cgood(x,y,itm,monst) Function to check location for emptiness
15 * createitem(it,arg) Routine to place an item next to the player
18 * cast() Subroutine called by parse to cast a spell for the user
20 * speldamage(x) Function to perform spell functions cast by the player
23 * loseint() Routine to decrement your int (intelligence) if > 3
25 * isconfuse() Routine to check to see if player is confused
27 * nospell(x,monst) Routine to return 1 if a spell doesn't affect a monster
30 * fullhit(xx) Function to return full damage against a monst (aka web)
33 * direct(spnum,dam,str,arg) Routine to direct spell damage 1 square in 1 dir
37 * godirect(spnum,dam,str,delay,cshow) Function to perform missile attacks
38 * int spnum,dam,delay;
41 * ifblind(x,y) Routine to put "monster" or the monster name into lastmosnt
44 * tdirect(spnum) Routine to teleport away a monster
47 * omnidirect(sp,dam,str) Routine to damage all monsters 1 square from player
51 * dirsub(x,y) Routine to ask for direction, then modify x,y for it
54 * vxy(x,y) Routine to verify/fix (*x,*y) for being within bounds
57 * dirpoly(spnum) Routine to ask for a direction and polymorph a monst
60 * hitmonster(x,y) Function to hit a monster at the designated coordinates
63 * hitm(x,y,amt) Function to just hit a monster at a given coordinates
66 * hitplayer(x,y) Function for the monster to hit the player from (x,y)
69 * dropsomething(monst) Function to create an object when a monster dies
72 * dropgold(amount) Function to drop some gold around player
75 * something(level) Function to create a random item around player
78 * newobject(lev,i) Routine to return a randomly selected new object
81 * spattack(atckno,xx,yy) Function to process special attacks from monsters
84 * checkloss(x) Routine to subtract hp from user and flag bottomline display
87 * annihilate() Routine to annihilate monsters around player, playerx,playery
89 * newsphere(x,y,dir,lifetime) Function to create a new sphere of annihilation
90 * int x,y,dir,lifetime;
92 * rmsphere(x,y) Function to delete a sphere of annihilation from list
95 * sphboom(x,y) Function to perform the effects of a sphere detonation
98 * genmonst() Function to ask for monster and genocide from game
103 /* used for altar reality */
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 */
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);
128 * createmonster(monstno) Function to create a monster next to the player
131 * Enter with the monster number (1 to MAXMONST+8)
135 createmonster(int mon)
138 if (mon < 1 || mon > MAXMONST + 8) { /* check for monster number out of bounds */
140 lprintf("\ncan't createmonst(%d)\n", (long)mon);
144 while (monster[mon].genocided && mon < MAXMONST) /* genocided? */
146 for (k = rnd(8), i = -8; i < 0; i++, k++) { /* choose direction, then try all */
147 if (k > 8) /* wraparound the diroff arrays */
149 x = playerx + diroffx[k];
150 y = playery + diroffy[k];
151 if (cgood(x, y, 0, 1)) { /* if we can create here */
153 hitp[x][y] = monster[mon].hitpoints;
154 stealth[x][y] = know[x][y] = 0;
167 * int cgood(x,y,itm,monst) Function to check location for emptiness
170 * Routine to return TRUE if a location does not have itm or monst there
171 * returns FALSE (0) otherwise
172 * Enter with itm or monst TRUE or FALSE if checking it
173 * Example: if itm==TRUE check for no item at this location
174 * if monst==TRUE check for no monster at this location
175 * This routine will return FALSE if at a wall or the dungeon exit on level 1
178 cgood(int x, int y, int itm, int monst)
180 if ((y >= 0) && (y <= MAXY - 1) && (x >= 0) && (x <= MAXX - 1))
182 if (item[x][y] != OWALL) /* can't make anything on walls */
183 /* is it free of items? */
184 if (itm == 0 || (item[x][y] == 0))
185 /* is it free of monsters? */
186 if (monst == 0 || (mitem[x][y] == 0))
187 if ((level != 1) || (x != 33) ||
189 /* not exit to level 1 */
195 * createitem(it,arg) Routine to place an item next to the player
198 * Enter with the item number and its argument (iven[], ivenarg[])
199 * Returns no value, thus we don't know about createitem() failures.
202 createitem(int it, int arg)
205 if (it >= MAXOBJ) /* no such object */
207 for (k = rnd(8), i = -8; i < 0; i++, k++) { /* choose direction, then try all */
208 if (k > 8) /* wraparound the diroff arrays */
210 x = playerx + diroffx[k];
211 y = playery + diroffy[k];
212 if (cgood(x, y, 1, 0)) { /* if we can create here */
222 * cast() Subroutine called by parse to cast a spell for the user
224 * No arguments and no return value.
226 static const char eys[] = "\nEnter your spell: ";
233 if (c[SPELLS] <= 0) {
234 lprcat("\nYou don't have any spells!");
239 while ((a = getchr()) == 'D') {
244 if (a == '\33') /* to escape casting a spell */
246 if ((b = getchr()) == '\33') /* to escape casting a spell */
248 if ((d = getchr()) == '\33') {
249 over: lprcat(aborted);
252 } /* to escape casting a spell */
256 for (lprc('\n'), j = -1, i = 0; i < SPNUM; i++)
257 /* seq search for his spell, hash? */
258 if ((spelcode[i][0] == a) && (spelcode[i][1] == b) && (spelcode[i][2] == d))
265 lprcat(" Nothing Happened ");
270 * speldamage(x) Function to perform spell functions cast by the player
273 * Enter with the spell number, returns no value.
274 * Please insure that there are 2 spaces before all messages here
284 if (x >= SPNUM) /* no such spell */
287 lprcat(" It didn't seem to work");
289 } /* not if time stopped */
291 if ((rnd(23) == 7) || (rnd(18) > c[INTELLIGENCE])) {
292 lprcat(" It didn't work!");
295 if (clev * 3 + 2 < x) {
296 lprcat(" Nothing happens. You seem inexperienced at this");
301 /* ----- LEVEL 1 SPELLS ----- */
303 case 0: /* protection field +2 */
304 if (c[PROTECTIONTIME] == 0)
305 c[MOREDEFENSES] += 2;
306 c[PROTECTIONTIME] += 250;
309 case 1: /* magic missile */
310 i = rnd(((clev + 1) << 1)) + clev + 3;
311 godirect(x, i, (clev >= 2) ? " Your missiles hit the %s" : " Your missile hit the %s", 100, '+');
315 case 2: /* dexterity */
316 if (c[DEXCOUNT] == 0)
323 cp = " While the %s slept, you smashed it %d times";
324 ws: direct(x, fullhit(i), cp, i);
327 case 4: /* charm monster */
328 c[CHARMCOUNT] += c[CHARISMA] << 1;
331 case 5: /* sonic spear */
332 godirect(x, rnd(10) + 15 + clev, " The sound damages the %s", 70, '@');
335 /* ----- LEVEL 2 SPELLS ----- */
339 cp = " While the %s is entangled, you hit %d times";
342 case 7: /* strength */
343 if (c[STRCOUNT] == 0)
345 c[STRCOUNT] += 150 + rnd(100);
348 case 8: /* enlightenment */
354 vxy(&xh, &yh); /* check bounds */
355 for (i = yl; i <= yh; i++) /* enlightenment */
356 for (j = xl; j <= xh; j++)
358 draws(xl, xh + 1, yl, yh + 1);
361 case 9: /* healing */
362 raisehp(20 + (clev << 1));
365 case 10: /* cure blindness */
370 createmonster(makemonst(level + 1) + 8);
374 if (rnd(11) + 7 <= c[WISDOM])
375 direct(x, rnd(20) + 20 + clev, " The %s believed!", 0);
377 lprcat(" It didn't believe the illusions!");
380 case 13: /* if he has the amulet of invisibility then add more time */
381 for (j = i = 0; i < 26; i++)
382 if (iven[i] == OAMULET)
384 c[INVISIBILITY] += (j << 7) + 12;
387 /* ----- LEVEL 3 SPELLS ----- */
389 case 14: /* fireball */
390 godirect(x, rnd(25 + clev) + 25 + clev, " The fireball hits the %s", 40, '*');
394 godirect(x, rnd(25) + 20 + clev, " Your cone of cold strikes the %s", 60, 'O');
397 case 16: /* polymorph */
401 case 17: /* cancellation */
402 c[CANCELLATION] += 5 + clev;
405 case 18: /* haste self */
406 c[HASTESELF] += 7 + clev;
409 case 19: /* cloud kill */
410 omnidirect(x, 30 + rnd(10), " The %s gasps for air");
413 case 20: /* vaporize rock */
414 xh = min(playerx + 1, MAXX - 2);
415 yh = min(playery + 1, MAXY - 2);
416 for (i = max(playerx - 1, 1); i <= xh; i++)
417 for (j = max(playery - 1, 1); j <= yh; j++) {
420 switch (*(p = &item[i][j])) {
422 if (level < MAXLEVEL + MAXVLEVEL - 1)
427 if (c[HARDGAME] < 3) {
438 hitp[i][j] = monster[GNOMEKING].hitpoints;
444 hitp[i][j] = monster[DEMONPRINCE].hitpoints;
451 break; /* Xorn takes damage from vpr */
456 /* ----- LEVEL 4 SPELLS ----- */
458 case 21: /* dehydration */
459 direct(x, 100 + clev, " The %s shrivels up", 0);
462 case 22: /* lightning */
463 godirect(x, rnd(25) + 20 + (clev << 1), " A lightning bolt hits the %s", 1, '~');
466 case 23: /* drain life */
467 i = min(c[HP] - 1, c[HPMAX] / 2);
468 direct(x, i + i, "", 0);
472 case 24: /* globe of invulnerability */
474 c[MOREDEFENSES] += 10;
480 omnidirect(x, 32 + clev, " The %s struggles for air in your flood!");
483 case 26: /* finger of death */
484 if (rnd(151) == 63) {
486 lprcat("\nYour heart stopped!\n");
491 if (c[WISDOM] > rnd(10) + 10)
492 direct(x, 2000, " The %s's heart stopped", 0);
494 lprcat(" It didn't work");
497 /* ----- LEVEL 5 SPELLS ----- */
499 case 27: /* scare monster */
500 c[SCAREMONST] += rnd(10) + clev;
503 case 28: /* hold monster */
504 c[HOLDMONST] += rnd(10) + clev;
507 case 29: /* time stop */
508 c[TIMESTOP] += rnd(20) + (clev << 1);
511 case 30: /* teleport away */
515 case 31: /* magic fire */
516 omnidirect(x, 35 + rnd(10) + clev, " The %s cringes from the flame");
519 /* ----- LEVEL 6 SPELLS ----- */
521 case 32: /* sphere of annihilation */
522 if ((rnd(23) == 5) && (wizard == 0)) {
524 lprcat("\nYou have been enveloped by the zone of nothingness!\n");
532 i = dirsub(&xl, &yl); /* get direction of sphere */
533 newsphere(xl, yl, i, rnd(20) + 11); /* make a sphere */
536 case 33: /* genocide */
542 case 34: /* summon demon */
544 direct(x, 150, " The demon strikes at the %s", 0);
548 lprcat(" Nothing seems to have happened");
551 lprcat(" The demon turned on you and vanished!");
555 losehp(i); /* must say killed by a demon */
558 case 35: /* walk through walls */
559 c[WTW] += rnd(10) + 5;
562 case 36: /* alter reality */
564 struct isave *save; /* pointer to item save structure */
566 sc = 0; /* # items saved */
567 save = malloc(sizeof(struct isave) * MAXX * MAXY * 2);
568 for (j = 0; j < MAXY; j++)
569 for (i = 0; i < MAXX; i++) { /* save all items and monsters */
571 if (xl && xl != OWALL && xl != OANNIHILATION) {
573 save[sc].id = item[i][j];
574 save[sc++].arg = iarg[i][j];
578 save[sc].id = mitem[i][j];
579 save[sc++].arg = hitp[i][j];
590 item[33][MAXY - 1] = 0;
591 for (j = rnd(MAXY - 2), i = 1; i < MAXX - 1; i++)
593 while (sc > 0) { /* put objects back in level */
595 if (save[sc].type == 0) {
597 for (trys = 100, i = j = 1; --trys > 0 && item[i][j]; i = rnd(MAXX - 1), j = rnd(MAXY - 1))
600 item[i][j] = save[sc].id;
601 iarg[i][j] = save[sc].arg;
603 } else { /* put monsters back in */
605 for (trys = 100, i = j = 1; --trys > 0 && (item[i][j] == OWALL || mitem[i][j]); i = rnd(MAXX - 1), j = rnd(MAXY - 1))
608 mitem[i][j] = save[sc].id;
609 hitp[i][j] = save[sc].arg;
614 draws(0, MAXX, 0, MAXY);
622 case 37: /* permanence */
623 larn_adjtime(-99999L);
624 spelknow[37] = 0; /* forget */
629 lprintf(" spell %d not available!", (long)x);
636 * loseint() Routine to subtract 1 from your int (intelligence) if > 3
638 * No arguments and no return value
643 if (--c[INTELLIGENCE] < 3)
648 * isconfuse() Routine to check to see if player is confused
650 * This routine prints out a message saying "You can't aim your magic!"
651 * returns 0 if not confused, non-zero (time remaining confused) if confused
657 lprcat(" You can't aim your magic!");
664 * nospell(x,monst) Routine to return 1 if a spell doesn't affect a monster
667 * Subroutine to return 1 if the spell can't affect the monster
668 * otherwise returns 0
669 * Enter with the spell number in x, and the monster number in monst.
672 nospell(int x, int monst)
675 if (x >= SPNUM || monst >= MAXMONST + 8 || monst < 0 || x < 0)
676 return (0); /* bad spell or monst */
677 if ((tmp = spelweird[monst - 1][x]) == 0)
681 lprintf(spelmes[tmp], monster[monst].name);
686 * fullhit(xx) Function to return full damage against a monster (aka web)
689 * Function to return hp damage to monster due to a number of full hits
690 * Enter with the number of full hits being done
696 if (xx < 0 || xx > 20) /* fullhits are out of range */
698 if (c[LANCEDEATH]) /* lance of death */
700 i = xx * ((c[WCLASS] >> 1) + c[STRENGTH] + c[STREXTRA] - c[HARDGAME] - 12 + c[MOREDAM]);
701 return ((i >= 1) ? i : xx);
705 * direct(spnum,dam,str,arg) Routine to direct spell damage 1 square in 1 dir
709 * Routine to ask for a direction to a spell and then hit the monster
710 * Enter with the spell number in spnum, the damage to be done in dam,
711 * lprintf format string in str, and lprintf's argument in arg.
715 direct(int spnum, int dam, const char *str, int arg)
719 if (spnum < 0 || spnum >= SPNUM || str == 0) /* bad arguments */
725 if (item[x][y] == OMIRROR) {
726 if (spnum == 3) { /* sleep */
727 lprcat("You fall asleep! ");
736 } else if (spnum == 6) { /* web */
737 lprcat("You get stuck in your own web! ");
742 lprintf(str, "spell caster (thats you)", (long)arg);
749 lprcat(" There wasn't anything there!");
753 if (nospell(spnum, m)) {
758 lprintf(str, lastmonst, (long)arg);
763 * godirect(spnum,dam,str,delay,cshow) Function to perform missile attacks
764 * int spnum,dam,delay;
767 * Function to hit in a direction from a missile weapon and have it keep
768 * on going in that direction until its power is exhausted
769 * Enter with the spell number in spnum, the power of the weapon in hp,
770 * lprintf format string in str, the # of milliseconds to delay between
771 * locations in delay, and the character to represent the weapon in cshow.
775 godirect(int spnum, int dam, const char *str, int delay, char cshow)
780 if (spnum < 0 || spnum >= SPNUM || str == 0 || delay < 0) /* bad args */
794 if ((x > MAXX - 1) || (y > MAXY - 1) || (x < 0) || (y < 0)) {
796 break; /* out of bounds */
798 if ((x == playerx) && (y == playery)) { /* if energy hits player */
800 lprcat("\nYou are hit my your own magic!");
806 if (c[BLINDCOUNT] == 0) { /* if not blind show effect */
807 cursor(x + 1, y + 1);
812 if ((m = mitem[x][y])) { /* is there a monster there? */
814 if (nospell(spnum, m)) {
821 lprintf(str, lastmonst);
822 dam -= hitm(x, y, dam);
828 switch (*(p = &item[x][y])) {
832 lprintf(str, "wall");
833 if (dam >= 50 + c[HARDGAME]) /* enough damage? */
834 if (level < MAXLEVEL + MAXVLEVEL - 1) /* not on V3 */
835 if ((x < MAXX - 1) && (y < MAXY - 1) && (x) && (y)) {
836 lprcat(" The wall crumbles");
847 lprintf(str, "door");
849 lprcat(" The door is blasted apart");
857 lprintf(str, "statue");
860 lprcat(" The statue crumbles");
870 lprintf(str, "throne");
872 mitem[x][y] = GNOMEKING;
873 hitp[x][y] = monster[GNOMEKING].hitpoints;
884 dam -= 3 + (c[HARDGAME] >> 1);
889 * ifblind(x,y) Routine to put "monster" or the monster name into lastmosnt
892 * Subroutine to copy the word "monster" into lastmonst if the player is blind
893 * Enter with the coordinates (x,y) of the monster
897 ifblind(int x, int y)
901 vxy(&x, &y); /* verify correct x,y coordinates */
906 lastnum = mitem[x][y];
907 p = monster[lastnum].name;
909 strcpy(lastmonst, p);
913 * tdirect(spnum) Routine to teleport away a monster
916 * Routine to ask for a direction to a spell and then teleport away monster
917 * Enter with the spell number that wants to teleport away
925 if (spnum < 0 || spnum >= SPNUM) /* bad args */
930 if ((m = mitem[x][y]) == 0) {
931 lprcat(" There wasn't anything there!");
935 if (nospell(spnum, m)) {
941 mitem[x][y] = know[x][y] = 0;
945 * omnidirect(sp,dam,str) Routine to damage all monsters 1 square from player
949 * Routine to cast a spell and then hit the monster in all directions
950 * Enter with the spell number in sp, the damage done to wach square in dam,
951 * and the lprintf string to identify the spell in str.
955 omnidirect(int spnum, int dam, const char *str)
958 if (spnum < 0 || spnum >= SPNUM || str == 0) /* bad args */
960 for (x = playerx - 1; x < playerx + 2; x++)
961 for (y = playery - 1; y < playery + 2; y++) {
962 if ((m = mitem[x][y]) != 0) {
963 if (nospell(spnum, m) == 0) {
967 lprintf(str, lastmonst);
979 * static dirsub(x,y) Routine to ask for direction, then modify x,y for it
982 * Function to ask for a direction and modify an x,y for that direction
983 * Enter with the origination coordinates in (x,y).
984 * Returns index into diroffx[] (0-8).
987 dirsub(int *x, int *y)
990 lprcat("\nIn What Direction? ");
1012 *x = playerx + diroffx[i];
1013 *y = playery + diroffy[i];
1019 * vxy(x,y) Routine to verify/fix coordinates for being within bounds
1022 * Function to verify x & y are within the bounds for a level
1023 * If *x or *y is not within the absolute bounds for a level, fix them so that
1024 * they are on the level.
1025 * Returns TRUE if it was out of bounds, and the *x & *y in the calling
1026 * routine are affected.
1052 * dirpoly(spnum) Routine to ask for a direction and polymorph a monst
1055 * Subroutine to polymorph a monster and ask for the direction its in
1056 * Enter with the spell number in spmun.
1063 if (spnum < 0 || spnum >= SPNUM) /* bad args */
1065 if (isconfuse()) /* if he is confused, he can't aim his magic */
1068 if (mitem[x][y] == 0) {
1069 lprcat(" There wasn't anything there!");
1073 if (nospell(spnum, mitem[x][y])) {
1078 while (monster[m = mitem[x][y] = rnd(MAXMONST + 7)].genocided)
1080 hitp[x][y] = monster[m].hitpoints;
1081 show1cell(x, y); /* show the new monster */
1085 * hitmonster(x,y) Function to hit a monster at the designated coordinates
1088 * This routine is used for a bash & slash type attack on a monster
1089 * Enter with the coordinates of the monster in (x,y).
1093 hitmonster(int x, int y)
1095 int tmp, monst, damag = 0, flag;
1096 if (c[TIMESTOP]) /* not if time stopped */
1098 vxy(&x, &y); /* verify coordinates are within range */
1099 if ((monst = mitem[x][y]) == 0)
1103 tmp = monster[monst].armorclass + c[LEVEL] + c[DEXTERITY] +
1106 /* need at least random chance to hit */
1107 if ((rnd(20) < tmp - c[HARDGAME]) || (rnd(71) < 5)) {
1108 lprcat("\nYou hit");
1112 damag = rnd(damag) + 1;
1114 lprcat("\nYou missed");
1119 if (flag) /* if the monster was hit */
1120 if ((monst == RUSTMONSTER) || (monst == DISENCHANTRESS) || (monst == CUBE))
1122 if (ivenarg[c[WIELD]] > -10) {
1123 lprintf("\nYour weapon is dulled by the %s", lastmonst);
1125 --ivenarg[c[WIELD]];
1129 if (monst == VAMPIRE)
1130 if (hitp[x][y] < 25) {
1137 * hitm(x,y,amt) Function to just hit a monster at a given coordinates
1140 * Returns the number of hitpoints the monster absorbed
1141 * This routine is used to specifically damage a monster at a location (x,y)
1142 * Called by hitmonster(x,y)
1145 hitm(int x, int y, int amt)
1149 vxy(&x, &y); /* verify coordinates are within range */
1150 amt2 = amt; /* save initial damage so we can return it */
1151 monst = mitem[x][y];
1152 if (c[HALFDAM]) /* if half damage curse adjust damage points */
1158 stealth[x][y] = 1; /* make sure hitting monst breaks stealth condition */
1159 c[HOLDMONST] = 0; /* hit a monster breaks hold monster spell */
1160 switch (monst) { /* if a dragon and orb(s) of dragon slaying */
1165 case PLATINUMDRAGON:
1167 amt *= 1 + (c[SLAYING] << 1);
1170 /* invincible monster fix is here */
1171 if (hitp[x][y] > monster[monst].hitpoints)
1172 hitp[x][y] = monster[monst].hitpoints;
1173 if ((hpoints = hitp[x][y]) <= amt) {
1177 lprintf("\nThe %s died!", lastmonst);
1178 raiseexperience((long)monster[monst].experience);
1179 amt = monster[monst].gold;
1181 dropgold(rnd(amt) + amt);
1182 dropsomething(monst);
1187 hitp[x][y] = hpoints - amt;
1192 * hitplayer(x,y) Function for the monster to hit the player from (x,y)
1195 * Function for the monster to hit the player with monster at location x,y
1196 * Returns nothing of value.
1199 hitplayer(int x, int y)
1201 int dam, tmp, mster, bias;
1202 vxy(&x, &y); /* verify coordinates are within range */
1203 lastnum = mster = mitem[x][y];
1204 /* spirit naga's and poltergeist's do nothing if scarab of negate spirit */
1205 if (c[NEGATESPIRIT] || c[SPIRITPRO])
1206 if ((mster == POLTERGEIST) || (mster == SPIRITNAGA))
1208 /* if undead and cube of undead control */
1209 if (c[CUBEofUNDEAD] || c[UNDEADPRO])
1210 if ((mster == VAMPIRE) || (mster == WRAITH) || (mster == ZOMBIE))
1212 if ((know[x][y] & 1) == 0) {
1216 bias = (c[HARDGAME]) + 1;
1217 hitflag = hit2flag = hit3flag = 1;
1221 if (c[INVISIBILITY])
1223 lprintf("\nThe %s misses wildly", lastmonst);
1227 if (rnd(30) + 5 * monster[mster].level - c[CHARISMA] < 30) {
1228 lprintf("\nThe %s is awestruck at your magnificence!", lastmonst);
1234 dam = monster[mster].damage;
1235 dam += rnd((int)((dam < 1) ? 1 : dam)) + monster[mster].level;
1238 if (monster[mster].attack > 0)
1239 if (((dam + bias + 8) > c[AC]) || (rnd((int)((c[AC] > 0) ? c[AC] : 1)) == 1)) {
1240 if (spattack(monster[mster].attack, x, y)) {
1248 if (((dam + bias) > c[AC]) || (rnd((int)((c[AC] > 0) ? c[AC] : 1)) == 1)) {
1249 lprintf("\n The %s hit you ", lastmonst);
1251 if ((dam -= c[AC]) < 0)
1260 lprintf("\n The %s missed ", lastmonst);
1264 * dropsomething(monst) Function to create an object when a monster dies
1267 * Function to create an object near the player when certain monsters are killed
1268 * Enter with the monster number
1269 * Returns nothing of value.
1272 dropsomething(int monst)
1282 case PLATINUMDRAGON:
1292 dropsomething(LEPRECHAUN);
1298 * dropgold(amount) Function to drop some gold around player
1301 * Enter with the number of gold pieces to drop
1302 * Returns nothing of value.
1305 dropgold(int amount)
1308 createitem(OMAXGOLD, amount / 100);
1310 createitem(OGOLDPILE, amount);
1314 * something(lvl) Function to create a random item around player
1317 * Function to create an item from a designed probability around player
1318 * Enter with the cave level on which something is to be dropped
1319 * Returns nothing of value.
1326 if (lvl < 0 || lvl > MAXLEVEL + MAXVLEVEL) /* correct level? */
1328 if (rnd(101) < 8) /* possibly more than one item */
1330 j = newobject(lvl, &i);
1335 * newobject(lev,i) Routine to return a randomly selected new object
1338 * Routine to return a randomly selected object to be created
1339 * Returns the object number created, and sets *i for its argument
1340 * Enter with the cave level and a pointer to the items arg
1342 static char nobjtab[] = { 0, OSCROLL, OSCROLL, OSCROLL, OSCROLL, OPOTION,
1343 OPOTION, OPOTION, OPOTION, OGOLDPILE, OGOLDPILE, OGOLDPILE, OGOLDPILE,
1344 OBOOK, OBOOK, OBOOK, OBOOK, ODAGGER, ODAGGER, ODAGGER, OLEATHER, OLEATHER,
1345 OLEATHER, OREGENRING, OPROTRING, OENERGYRING, ODEXRING, OSTRRING, OSPEAR,
1346 OBELT, ORING, OSTUDLEATHER, OSHIELD, OFLAIL, OCHAIN, O2SWORD, OPLATE,
1350 newobject(int lev, int *i)
1353 if (level < 0 || level > MAXLEVEL + MAXVLEVEL) /* correct level? */
1359 j = nobjtab[tmp = rnd(tmp)]; /* the object type */
1377 *i = rnd((lev + 1) * 10) + lev * 10 + 10;
1388 if (!(*i = newdagger()))
1394 if (!(*i = newleather()))
1400 *i = rund(lev / 3 + 1);
1404 *i = rnd(lev / 4 + 1);
1407 *i = rund(lev / 4 + 1);
1410 *i = rnd(lev / 2 + 1);
1414 *i = rund(lev / 2 + 1);
1417 *i = rund(lev / 3 + 1);
1423 *i = rund(lev / 2 + 1);
1441 * spattack(atckno,xx,yy) Function to process special attacks from monsters
1444 * Enter with the special attack number, and the coordinates (xx,yy)
1445 * of the monster that is special attacking
1446 * Returns 1 if must do a show1cell(xx,yy) upon return, 0 otherwise
1448 * atckno monster effect
1449 * ---------------------------------------------------
1451 * 1 rust monster eat armor
1452 * 2 hell hound breathe light fire
1453 * 3 dragon breathe fire
1454 * 4 giant centipede weakening sing
1455 * 5 white dragon cold breath
1456 * 6 wraith drain level
1457 * 7 waterlord water gusher
1458 * 8 leprechaun steal gold
1459 * 9 disenchantress disenchant weapon or armor
1460 * 10 ice lizard hits with barbed tail
1461 * 11 umber hulk confusion
1462 * 12 spirit naga cast spells taken from special attacks
1463 * 13 platinum dragon psionics
1464 * 14 nymph steal objects
1468 * char rustarm[ARMORTYPES][2];
1469 * special array for maximum rust damage to armor from rustmonster
1470 * format is: { armor type , minimum attribute
1472 #define ARMORTYPES 6
1473 static char rustarm[ARMORTYPES][2] = {
1474 { OSTUDLEATHER, -2 },
1481 static char spsel[] = { 1, 2, 3, 5, 6, 8, 9, 11, 13, 14 };
1484 spattack(int x, int xx, int yy)
1487 const char *p = NULL;
1489 if (c[CANCELLATION])
1491 vxy(&xx, &yy); /* verify x & y coordinates */
1493 case 1: /* rust your armor, j=1 when rusting has occurred */
1495 if ((i = c[SHIELD]) != -1) {
1496 if (--ivenarg[i] < -1)
1501 if ((j == 0) && (k != -1)) {
1503 for (i = 0; i < ARMORTYPES; i++)
1504 /* find his armor in table */
1505 if (m == rustarm[i][0]) {
1506 if (--ivenarg[k] < rustarm[i][1])
1507 ivenarg[k] = rustarm[i][1];
1513 if (j == 0) /* if rusting did not occur */
1516 p = "\nThe %s hit you -- Your lucky you have leather on";
1519 p = "\nThe %s hit you -- Your fortunate to have stainless steel armor!";
1524 p = "\nThe %s hit you -- your armor feels weaker";
1529 i = rnd(15) + 8 - c[AC];
1530 spout: p = "\nThe %s breathes fire at you!";
1531 if (c[FIRERESISTANCE])
1532 p = "\nThe %s's flame doesn't phase you!";
1535 lprintf(p, lastmonst);
1542 i = rnd(20) + 25 - c[AC];
1546 if (c[STRENGTH] > 3) {
1547 p = "\nThe %s stung you! You feel weaker";
1551 p = "\nThe %s stung you!";
1555 p = "\nThe %s blasts you with his cold breath";
1556 i = rnd(15) + 18 - c[AC];
1560 lprintf("\nThe %s drains you of your life energy!", lastmonst);
1566 p = "\nThe %s got you with a gusher!";
1567 i = rnd(15) + 25 - c[AC];
1571 if (c[NOTHEFT]) /* he has a device of no theft */
1574 p = "\nThe %s hit you -- Your purse feels lighter";
1575 if (c[GOLD] > 32767)
1578 c[GOLD] -= rnd((int)(1 + (c[GOLD] >> 1)));
1582 p = "\nThe %s couldn't find any gold to steal";
1583 lprintf(p, lastmonst);
1590 for (j = 50;;) { /* disenchant */
1592 m = iven[i]; /* randomly select item */
1593 if (m > 0 && ivenarg[i] > 0 && m != OSCROLL && m != OPOTION) {
1594 if ((ivenarg[i] -= 3) < 0)
1596 lprintf("\nThe %s hits you -- you feel a sense of loss", lastmonst);
1604 p = "\nThe %s nearly misses";
1612 p = "\nThe %s hit you with his barbed tail";
1613 i = rnd(25) - c[AC];
1617 p = "\nThe %s has confused you";
1619 c[CONFUSE] += 10 + rnd(10);
1622 case 12: /* performs any number of other special attacks */
1623 return (spattack(spsel[rund(10)], xx, yy));
1626 p = "\nThe %s flattens you with his psionics!";
1627 i = rnd(15) + 30 - c[AC];
1631 if (c[NOTHEFT]) /* he has device of no theft */
1633 if (emptyhanded() == 1) {
1634 p = "\nThe %s couldn't find anything to steal";
1637 lprintf("\nThe %s picks your pocket and takes:", lastmonst);
1639 if (stealsomething() == 0)
1646 i = rnd(10) + 5 - c[AC];
1647 spout3: p = "\nThe %s bit you!";
1651 i = rnd(15) + 10 - c[AC];
1655 lprintf(p, lastmonst);
1662 * checkloss(x) Routine to subtract hp from user and flag bottomline display
1665 * Routine to subtract hitpoints from the user and flag the bottomline display
1666 * Enter with the number of hit points to lose
1667 * Note: if x > c[HP] this routine could kill the player!
1679 * annihilate() Routine to annihilate all monsters around player (playerx,playery)
1681 * Gives player experience, but no dropped objects
1682 * Returns the experience gained from all monsters killed
1690 for (k = 0, i = playerx - 1; i <= playerx + 1; i++)
1691 for (j = playery - 1; j <= playery + 1; j++)
1692 if (!vxy(&i, &j)) { /* if not out of bounds */
1693 if (*(p = &mitem[i][j])) { /* if a monster there */
1694 if (*p < DEMONLORD + 2) {
1695 k += monster[(int)*p].experience;
1696 *p = know[i][j] = 0;
1698 lprintf("\nThe %s barely escapes being annihilated!", monster[(int)*p].name);
1699 hitp[i][j] = (hitp[i][j] >> 1) + 1; /* lose half hit points*/
1704 lprcat("\nYou hear loud screams of agony!");
1705 raiseexperience((long)k);
1711 * newsphere(x,y,dir,lifetime) Function to create a new sphere of annihilation
1712 * int x,y,dir,lifetime;
1714 * Enter with the coordinates of the sphere in x,y
1715 * the direction (0-8 diroffx format) in dir, and the lifespan of the
1716 * sphere in lifetime (in turns)
1717 * Returns the number of spheres currently in existence
1720 newsphere(int x, int y, int dir, int life)
1724 if (((sp = malloc(sizeof(struct sphere)))) == 0)
1725 return (c[SPHCAST]); /* can't malloc, therefore failure */
1726 if (dir >= 9) /* no movement if direction not found */
1728 if (level == 0) /* don't go out of bounds */
1740 if ((m = mitem[x][y]) >= DEMONLORD + 4) { /* demons dispel spheres */
1742 show1cell(x, y); /* show the demon (ha ha) */
1744 lprintf("\nThe %s dispels the sphere!", monster[m].name);
1746 rmsphere(x, y); /* remove any spheres that are here */
1747 return (c[SPHCAST]);
1749 if (m == DISENCHANTRESS) { /* disenchantress cancels spheres */
1751 lprintf("\nThe %s causes cancellation of the sphere!", monster[m].name);
1753 boom: sphboom(x, y); /* blow up stuff around sphere */
1754 rmsphere(x, y); /* remove any spheres that are here */
1755 return (c[SPHCAST]);
1757 if (c[CANCELLATION]) { /* cancellation cancels spheres */
1759 lprcat("\nAs the cancellation takes effect, you hear a great earth shaking blast!");
1763 if (item[x][y] == OANNIHILATION) { /* collision of spheres detonates spheres */
1765 lprcat("\nTwo spheres of annihilation collide! You hear a great earth shaking blast!");
1770 if (playerx == x && playery == y) { /* collision of sphere and player! */
1772 lprcat("\nYou have been enveloped by the zone of nothingness!\n");
1774 rmsphere(x, y); /* remove any spheres that are here */
1778 item[x][y] = OANNIHILATION;
1781 show1cell(x, y); /* show the new sphere */
1786 sp->lifetime = life;
1788 if (spheres == 0) /* if first node in the sphere list */
1790 else { /* add sphere to beginning of linked list */
1794 return (++c[SPHCAST]); /* one more sphere in the world */
1798 * rmsphere(x,y) Function to delete a sphere of annihilation from list
1801 * Enter with the coordinates of the sphere (on current level)
1802 * Returns the number of spheres currently in existence
1805 rmsphere(int x, int y)
1807 struct sphere *sp, *sp2 = 0;
1808 for (sp = spheres; sp; sp2 = sp, sp = sp->p)
1809 if (level == sp->lev) /* is sphere on this level? */
1810 if ((x == sp->x) && (y == sp->y)) { /* locate sphere at this location */
1811 item[x][y] = mitem[x][y] = 0;
1813 show1cell(x, y); /* show the now missing sphere */
1815 if (sp == spheres) {
1825 return (c[SPHCAST]); /* return number of spheres in the world */
1829 * sphboom(x,y) Function to perform the effects of a sphere detonation
1832 * Enter with the coordinates of the blast, Returns no value
1835 sphboom(int x, int y)
1840 if (c[CANCELLATION])
1841 c[CANCELLATION] = 1;
1842 for (j = max(1, x - 2); j < min(x + 3, MAXX - 1); j++)
1843 for (i = max(1, y - 2); i < min(y + 3, MAXY - 1); i++) {
1844 item[j][i] = mitem[j][i] = 0;
1846 if (playerx == j && playery == i) {
1849 lprcat("\nYou were too close to the sphere!");
1851 died(283); /* player killed in explosion */
1857 * genmonst() Function to ask for monster and genocide from game
1859 * This is done by setting a flag in the monster[] structure
1866 lprcat("\nGenocide what monster? ");
1867 for (i = 0; (!isalpha(i)) && (i != ' '); i = getchr())
1870 for (j = 0; j < MAXMONST; j++) /* search for the monster type */
1871 if (monstnamelist[j] == i) { /* have we found it? */
1872 monster[j].genocided = 1; /* genocided from game */
1873 lprintf(" There will be no more %s's", monster[j].name);
1874 /* now wipe out monsters on this level */
1875 newcavelevel(level);
1876 draws(0, MAXX, 0, MAXY);
1880 lprcat(" You sense failure!");