1 /*
2 * monster.c Larn is copyrighted 1986 by Noah Morgan.
3 * $FreeBSD: src/games/larn/monster.c,v 1.6 1999/11/16 11:47:40 marcel Exp $
4 *
5 * This file contains the following functions:
6 * ----------------------------------------------------------------------------
7 *
8 * createmonster(monstno) Function to create a monster next to the player
9 * int monstno;
10 *
11 * int cgood(x,y,itm,monst) Function to check location for emptiness
12 * int x,y,itm,monst;
13 *
14 * createitem(it,arg) Routine to place an item next to the player
15 * int it,arg;
16 *
17 * cast() Subroutine called by parse to cast a spell for the user
18 *
19 * speldamage(x) Function to perform spell functions cast by the player
20 * int x;
21 *
22 * loseint() Routine to decrement your int (intelligence) if > 3
23 *
24 * isconfuse() Routine to check to see if player is confused
25 *
26 * nospell(x,monst) Routine to return 1 if a spell doesn't affect a monster
27 * int x,monst;
28 *
29 * fullhit(xx) Function to return full damage against a monst (aka web)
30 * int xx;
31 *
32 * direct(spnum,dam,str,arg) Routine to direct spell damage 1 square in 1 dir
33 * int spnum,dam,arg;
34 * char *str;
35 *
36 * godirect(spnum,dam,str,delay,cshow) Function to perform missile attacks
37 * int spnum,dam,delay;
38 * char *str,cshow;
39 *
40 * ifblind(x,y) Routine to put "monster" or the monster name into lastmosnt
41 * int x,y;
42 *
43 * tdirect(spnum) Routine to teleport away a monster
44 * int spnum;
45 *
46 * omnidirect(sp,dam,str) Routine to damage all monsters 1 square from player
47 * int sp,dam;
48 * char *str;
49 *
50 * dirsub(x,y) Routine to ask for direction, then modify x,y for it
51 * int *x,*y;
52 *
53 * vxy(x,y) Routine to verify/fix (*x,*y) for being within bounds
54 * int *x,*y;
55 *
56 * dirpoly(spnum) Routine to ask for a direction and polymorph a monst
57 * int spnum;
58 *
59 * hitmonster(x,y) Function to hit a monster at the designated coordinates
60 * int x,y;
61 *
62 * hitm(x,y,amt) Function to just hit a monster at a given coordinates
63 * int x,y,amt;
64 *
65 * hitplayer(x,y) Function for the monster to hit the player from (x,y)
66 * int x,y;
67 *
68 * dropsomething(monst) Function to create an object when a monster dies
69 * int monst;
70 *
71 * dropgold(amount) Function to drop some gold around player
72 * int amount;
73 *
74 * something(level) Function to create a random item around player
75 * int level;
76 *
77 * newobject(lev,i) Routine to return a randomly selected new object
78 * int lev,*i;
79 *
80 * spattack(atckno,xx,yy) Function to process special attacks from monsters
81 * int atckno,xx,yy;
82 *
83 * checkloss(x) Routine to subtract hp from user and flag bottomline display
84 * int x;
85 *
86 * annihilate() Routine to annihilate monsters around player, playerx,playery
87 *
88 * newsphere(x,y,dir,lifetime) Function to create a new sphere of annihilation
89 * int x,y,dir,lifetime;
90 *
91 * rmsphere(x,y) Function to delete a sphere of annihilation from list
92 * int x,y;
93 *
94 * sphboom(x,y) Function to perform the effects of a sphere detonation
95 * int x,y;
96 *
97 * genmonst() Function to ask for monster and genocide from game
98 *
99 */
100 #include "header.h"
101
102 /* used for altar reality */
103 struct isave {
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 */
107 };
108
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);
125
126 /*
127 * createmonster(monstno) Function to create a monster next to the player
128 * int monstno;
129 *
130 * Enter with the monster number (1 to MAXMONST+8)
131 * Returns no value.
132 */
133 void
createmonster(int mon)134 createmonster(int mon)
135 {
136 int x, y, k, i;
137 if (mon < 1 || mon > MAXMONST + 8) { /* check for monster number out of bounds */
138 beep();
139 lprintf("\ncan't createmonst(%d)\n", (long)mon);
140 nap(3000);
141 return;
142 }
143 while (mon < MAXMONST && monster[mon].genocided) /* genocided? */
144 mon++;
145 for (k = rnd(8), i = -8; i < 0; i++, k++) { /* choose direction, then try all */
146 if (k > 8) /* wraparound the diroff arrays */
147 k = 1;
148 x = playerx + diroffx[k];
149 y = playery + diroffy[k];
150 if (cgood(x, y, 0, 1)) { /* if we can create here */
151 mitem[x][y] = mon;
152 hitp[x][y] = monster[mon].hitpoints;
153 stealth[x][y] = know[x][y] = 0;
154 switch (mon) {
155 case ROTHE:
156 case POLTERGEIST:
157 case VAMPIRE:
158 stealth[x][y] = 1;
159 }
160 return;
161 }
162 }
163 }
164
165 /*
166 * int cgood(x,y,itm,monst) Function to check location for emptiness
167 * int x,y,itm,monst;
168 *
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
175 */
176 static int
cgood(int x,int y,int itm,int monst)177 cgood(int x, int y, int itm, int monst)
178 {
179 if ((y >= 0) && (y <= MAXY - 1) && (x >= 0) && (x <= MAXX - 1))
180 /* within bounds? */
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) ||
187 (y != MAXY - 1))
188 /* not exit to level 1 */
189 return (1);
190 return (0);
191 }
192
193 /*
194 * createitem(it,arg) Routine to place an item next to the player
195 * int it,arg;
196 *
197 * Enter with the item number and its argument (iven[], ivenarg[])
198 * Returns no value, thus we don't know about createitem() failures.
199 */
200 void
createitem(int it,int arg)201 createitem(int it, int arg)
202 {
203 int x, y, k, i;
204 if (it >= MAXOBJ) /* no such object */
205 return;
206 for (k = rnd(8), i = -8; i < 0; i++, k++) { /* choose direction, then try all */
207 if (k > 8) /* wraparound the diroff arrays */
208 k = 1;
209 x = playerx + diroffx[k];
210 y = playery + diroffy[k];
211 if (cgood(x, y, 1, 0)) { /* if we can create here */
212 item[x][y] = it;
213 know[x][y] = 0;
214 iarg[x][y] = arg;
215 return;
216 }
217 }
218 }
219
220 /*
221 * cast() Subroutine called by parse to cast a spell for the user
222 *
223 * No arguments and no return value.
224 */
225 static const char eys[] = "\nEnter your spell: ";
226
227 void
cast(void)228 cast(void)
229 {
230 int i, j, a, b, d;
231 cursors();
232 if (c[SPELLS] <= 0) {
233 lprcat("\nYou don't have any spells!");
234 return;
235 }
236 lprcat(eys);
237 --c[SPELLS];
238 while ((a = getchr()) == 'D') {
239 seemagic(-1);
240 cursors();
241 lprcat(eys);
242 }
243 if (a == '\33') /* to escape casting a spell */
244 goto over;
245 if ((b = getchr()) == '\33') /* to escape casting a spell */
246 goto over;
247 if ((d = getchr()) == '\33') {
248 over: lprcat(aborted);
249 c[SPELLS]++;
250 return;
251 } /* to escape casting a spell */
252 #ifdef EXTRA
253 c[SPELLSCAST]++;
254 #endif
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))
258 if (spelknow[i]) {
259 speldamage(i);
260 j = 1;
261 i = SPNUM;
262 }
263 if (j == -1)
264 lprcat(" Nothing Happened ");
265 bottomline();
266 }
267
268 /*
269 * speldamage(x) Function to perform spell functions cast by the player
270 * int x;
271 *
272 * Enter with the spell number, returns no value.
273 * Please insure that there are 2 spaces before all messages here
274 */
275 static void
speldamage(int x)276 speldamage(int x)
277 {
278 int i, j, clev;
279 int xl, xh, yl, yh;
280 char *p, *kn, *pm;
281 const char *cp;
282
283 if (x >= SPNUM) /* no such spell */
284 return;
285 if (c[TIMESTOP]) {
286 lprcat(" It didn't seem to work");
287 return;
288 } /* not if time stopped */
289 clev = c[LEVEL];
290 if ((rnd(23) == 7) || (rnd(18) > c[INTELLIGENCE])) {
291 lprcat(" It didn't work!");
292 return;
293 }
294 if (clev * 3 + 2 < x) {
295 lprcat(" Nothing happens. You seem inexperienced at this");
296 return;
297 }
298
299 switch (x) {
300 /* ----- LEVEL 1 SPELLS ----- */
301
302 case 0: /* protection field +2 */
303 if (c[PROTECTIONTIME] == 0)
304 c[MOREDEFENSES] += 2;
305 c[PROTECTIONTIME] += 250;
306 return;
307
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, '+');
311
312 return;
313
314 case 2: /* dexterity */
315 if (c[DEXCOUNT] == 0)
316 c[DEXTERITY] += 3;
317 c[DEXCOUNT] += 400;
318 return;
319
320 case 3: /* sleep */
321 i = rnd(3) + 1;
322 cp = " While the %s slept, you smashed it %d times";
323 ws: direct(x, fullhit(i), cp, i);
324 return;
325
326 case 4: /* charm monster */
327 c[CHARMCOUNT] += c[CHARISMA] << 1;
328 return;
329
330 case 5: /* sonic spear */
331 godirect(x, rnd(10) + 15 + clev, " The sound damages the %s", 70, '@');
332 return;
333
334 /* ----- LEVEL 2 SPELLS ----- */
335
336 case 6: /* web */
337 i = rnd(3) + 2;
338 cp = " While the %s is entangled, you hit %d times";
339 goto ws;
340
341 case 7: /* strength */
342 if (c[STRCOUNT] == 0)
343 c[STREXTRA] += 3;
344 c[STRCOUNT] += 150 + rnd(100);
345 return;
346
347 case 8: /* enlightenment */
348 yl = playery - 5;
349 yh = playery + 6;
350 xl = playerx - 15;
351 xh = playerx + 16;
352 vxy(&xl, &yl);
353 vxy(&xh, &yh); /* check bounds */
354 for (i = yl; i <= yh; i++) /* enlightenment */
355 for (j = xl; j <= xh; j++)
356 know[j][i] = 1;
357 draws(xl, xh + 1, yl, yh + 1);
358 return;
359
360 case 9: /* healing */
361 raisehp(20 + (clev << 1));
362 return;
363
364 case 10: /* cure blindness */
365 c[BLINDCOUNT] = 0;
366 return;
367
368 case 11:
369 createmonster(makemonst(level + 1) + 8);
370 return;
371
372 case 12:
373 if (rnd(11) + 7 <= c[WISDOM])
374 direct(x, rnd(20) + 20 + clev, " The %s believed!", 0);
375 else
376 lprcat(" It didn't believe the illusions!");
377 return;
378
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)
382 j += 1 + ivenarg[i];
383 c[INVISIBILITY] += (j << 7) + 12;
384 return;
385
386 /* ----- LEVEL 3 SPELLS ----- */
387
388 case 14: /* fireball */
389 godirect(x, rnd(25 + clev) + 25 + clev, " The fireball hits the %s", 40, '*');
390 return;
391
392 case 15: /* cold */
393 godirect(x, rnd(25) + 20 + clev, " Your cone of cold strikes the %s", 60, 'O');
394 return;
395
396 case 16: /* polymorph */
397 dirpoly(x);
398 return;
399
400 case 17: /* cancellation */
401 c[CANCELLATION] += 5 + clev;
402 return;
403
404 case 18: /* haste self */
405 c[HASTESELF] += 7 + clev;
406 return;
407
408 case 19: /* cloud kill */
409 omnidirect(x, 30 + rnd(10), " The %s gasps for air");
410 return;
411
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++) {
417 kn = &know[i][j];
418 pm = &mitem[i][j];
419 switch (*(p = &item[i][j])) {
420 case OWALL:
421 if (level < MAXLEVEL + MAXVLEVEL - 1)
422 *p = *kn = 0;
423 break;
424
425 case OSTATUE:
426 if (c[HARDGAME] < 3) {
427 *p = OBOOK;
428 iarg[i][j] = level;
429 *kn = 0;
430 }
431 break;
432
433 case OTHRONE:
434 *pm = GNOMEKING;
435 *kn = 0;
436 *p = OTHRONE2;
437 hitp[i][j] = monster[GNOMEKING].hitpoints;
438 break;
439
440 case OALTAR:
441 *pm = DEMONPRINCE;
442 *kn = 0;
443 hitp[i][j] = monster[DEMONPRINCE].hitpoints;
444 break;
445 }
446 switch (*pm) {
447 case XORN:
448 ifblind(i, j);
449 hitm(i, j, 200);
450 break; /* Xorn takes damage from vpr */
451 }
452 }
453 return;
454
455 /* ----- LEVEL 4 SPELLS ----- */
456
457 case 21: /* dehydration */
458 direct(x, 100 + clev, " The %s shrivels up", 0);
459 return;
460
461 case 22: /* lightning */
462 godirect(x, rnd(25) + 20 + (clev << 1), " A lightning bolt hits the %s", 1, '~');
463 return;
464
465 case 23: /* drain life */
466 i = min(c[HP] - 1, c[HPMAX] / 2);
467 direct(x, i + i, "", 0);
468 c[HP] -= i;
469 return;
470
471 case 24: /* globe of invulnerability */
472 if (c[GLOBE] == 0)
473 c[MOREDEFENSES] += 10;
474 c[GLOBE] += 200;
475 loseint();
476 return;
477
478 case 25: /* flood */
479 omnidirect(x, 32 + clev, " The %s struggles for air in your flood!");
480 return;
481
482 case 26: /* finger of death */
483 if (rnd(151) == 63) {
484 beep();
485 lprcat("\nYour heart stopped!\n");
486 nap(4000);
487 died(270);
488 return;
489 }
490 if (c[WISDOM] > rnd(10) + 10)
491 direct(x, 2000, " The %s's heart stopped", 0);
492 else
493 lprcat(" It didn't work");
494 return;
495
496 /* ----- LEVEL 5 SPELLS ----- */
497
498 case 27: /* scare monster */
499 c[SCAREMONST] += rnd(10) + clev;
500 return;
501
502 case 28: /* hold monster */
503 c[HOLDMONST] += rnd(10) + clev;
504 return;
505
506 case 29: /* time stop */
507 c[TIMESTOP] += rnd(20) + (clev << 1);
508 return;
509
510 case 30: /* teleport away */
511 tdirect(x);
512 return;
513
514 case 31: /* magic fire */
515 omnidirect(x, 35 + rnd(10) + clev, " The %s cringes from the flame");
516 return;
517
518 /* ----- LEVEL 6 SPELLS ----- */
519
520 case 32: /* sphere of annihilation */
521 if ((rnd(23) == 5) && (wizard == 0)) {
522 beep();
523 lprcat("\nYou have been enveloped by the zone of nothingness!\n");
524 nap(4000);
525 died(258);
526 return;
527 }
528 xl = playerx;
529 yl = playery;
530 loseint();
531 i = dirsub(&xl, &yl); /* get direction of sphere */
532 newsphere(xl, yl, i, rnd(20) + 11); /* make a sphere */
533 return;
534
535 case 33: /* genocide */
536 genmonst();
537 spelknow[33] = 0;
538 loseint();
539 return;
540
541 case 34: /* summon demon */
542 if (rnd(100) > 30) {
543 direct(x, 150, " The demon strikes at the %s", 0);
544 return;
545 }
546 if (rnd(100) > 15) {
547 lprcat(" Nothing seems to have happened");
548 return;
549 }
550 lprcat(" The demon turned on you and vanished!");
551 beep();
552 i = rnd(40) + 30;
553 lastnum = 277;
554 losehp(i); /* must say killed by a demon */
555 return;
556
557 case 35: /* walk through walls */
558 c[WTW] += rnd(10) + 5;
559 return;
560
561 case 36: /* alter reality */
562 {
563 struct isave *save; /* pointer to item save structure */
564 int sc;
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 */
569 xl = item[i][j];
570 if (xl && xl != OWALL && xl != OANNIHILATION) {
571 save[sc].type = 0;
572 save[sc].id = item[i][j];
573 save[sc++].arg = iarg[i][j];
574 }
575 if (mitem[i][j]) {
576 save[sc].type = 1;
577 save[sc].id = mitem[i][j];
578 save[sc++].arg = hitp[i][j];
579 }
580 item[i][j] = OWALL;
581 mitem[i][j] = 0;
582 if (wizard)
583 know[i][j] = 1;
584 else
585 know[i][j] = 0;
586 }
587 eat(1, 1);
588 if (level == 1)
589 item[33][MAXY - 1] = 0;
590 for (j = rnd(MAXY - 2), i = 1; i < MAXX - 1; i++)
591 item[i][j] = 0;
592 while (sc > 0) { /* put objects back in level */
593 --sc;
594 if (save[sc].type == 0) {
595 int trys;
596 for (trys = 100, i = j = 1; --trys > 0 && item[i][j]; i = rnd(MAXX - 1), j = rnd(MAXY - 1))
597 ;
598 if (trys) {
599 item[i][j] = save[sc].id;
600 iarg[i][j] = save[sc].arg;
601 }
602 } else { /* put monsters back in */
603 int trys;
604 for (trys = 100, i = j = 1; --trys > 0 && (item[i][j] == OWALL || mitem[i][j]); i = rnd(MAXX - 1), j = rnd(MAXY - 1))
605 ;
606 if (trys) {
607 mitem[i][j] = save[sc].id;
608 hitp[i][j] = save[sc].arg;
609 }
610 }
611 }
612 loseint();
613 draws(0, MAXX, 0, MAXY);
614 if (wizard == 0)
615 spelknow[36] = 0;
616 free(save);
617 positionplayer();
618 return;
619 }
620
621 case 37: /* permanence */
622 larn_adjtime(-99999L);
623 spelknow[37] = 0; /* forget */
624 loseint();
625 return;
626
627 default:
628 lprintf(" spell %d not available!", (long)x);
629 beep();
630 return;
631 }
632 }
633
634 /*
635 * loseint() Routine to subtract 1 from your int (intelligence) if > 3
636 *
637 * No arguments and no return value
638 */
639 static void
loseint(void)640 loseint(void)
641 {
642 if (--c[INTELLIGENCE] < 3)
643 c[INTELLIGENCE] = 3;
644 }
645
646 /*
647 * isconfuse() Routine to check to see if player is confused
648 *
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
651 */
652 static long
isconfuse(void)653 isconfuse(void)
654 {
655 if (c[CONFUSE]) {
656 lprcat(" You can't aim your magic!");
657 beep();
658 }
659 return (c[CONFUSE]);
660 }
661
662 /*
663 * nospell(x,monst) Routine to return 1 if a spell doesn't affect a monster
664 * int x,monst;
665 *
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.
669 */
670 static int
nospell(int x,int monst)671 nospell(int x, int monst)
672 {
673 int tmp;
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)
677 return (0);
678 cursors();
679 lprc('\n');
680 lprintf(spelmes[tmp], monster[monst].name);
681 return (1);
682 }
683
684 /*
685 * fullhit(xx) Function to return full damage against a monster (aka web)
686 * int xx;
687 *
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
690 */
691 static int
fullhit(int xx)692 fullhit(int xx)
693 {
694 int i;
695 if (xx < 0 || xx > 20) /* fullhits are out of range */
696 return (0);
697 if (c[LANCEDEATH]) /* lance of death */
698 return (10000);
699 i = xx * ((c[WCLASS] >> 1) + c[STRENGTH] + c[STREXTRA] - c[HARDGAME] - 12 + c[MOREDAM]);
700 return ((i >= 1) ? i : xx);
701 }
702
703 /*
704 * direct(spnum,dam,str,arg) Routine to direct spell damage 1 square in 1 dir
705 * int spnum,dam,arg;
706 * char *str;
707 *
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.
711 * Returns no value.
712 */
713 static void
direct(int spnum,int dam,const char * str,int arg)714 direct(int spnum, int dam, const char *str, int arg)
715 {
716 int x, y;
717 int m;
718 if (spnum < 0 || spnum >= SPNUM || str == NULL) /* bad arguments */
719 return;
720 if (isconfuse())
721 return;
722 dirsub(&x, &y);
723 m = mitem[x][y];
724 if (item[x][y] == OMIRROR) {
725 if (spnum == 3) { /* sleep */
726 lprcat("You fall asleep! ");
727 beep();
728 fool:
729 arg += 2;
730 while (arg-- > 0) {
731 parse2();
732 nap(1000);
733 }
734 return;
735 } else if (spnum == 6) { /* web */
736 lprcat("You get stuck in your own web! ");
737 beep();
738 goto fool;
739 } else {
740 lastnum = 278;
741 lprintf(str, "spell caster (thats you)", (long)arg);
742 beep();
743 losehp(dam);
744 return;
745 }
746 }
747 if (m == 0) {
748 lprcat(" There wasn't anything there!");
749 return;
750 }
751 ifblind(x, y);
752 if (nospell(spnum, m)) {
753 lasthx = x;
754 lasthy = y;
755 return;
756 }
757 lprintf(str, lastmonst, (long)arg);
758 hitm(x, y, dam);
759 }
760
761 /*
762 * godirect(spnum,dam,str,delay,cshow) Function to perform missile attacks
763 * int spnum,dam,delay;
764 * char *str,cshow;
765 *
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.
771 * Returns no value.
772 */
773 void
godirect(int spnum,int dam,const char * str,int delay,char cshow)774 godirect(int spnum, int dam, const char *str, int delay, char cshow)
775 {
776 char *p;
777 int x, y, m;
778 int dx, dy;
779 if (spnum < 0 || spnum >= SPNUM || str == NULL || delay < 0) /* bad args */
780 return;
781 if (isconfuse())
782 return;
783 dirsub(&dx, &dy);
784 x = dx;
785 y = dy;
786 dx = x - playerx;
787 dy = y - playery;
788 x = playerx;
789 y = playery;
790 while (dam > 0) {
791 x += dx;
792 y += dy;
793 if ((x > MAXX - 1) || (y > MAXY - 1) || (x < 0) || (y < 0)) {
794 dam = 0;
795 break; /* out of bounds */
796 }
797 if ((x == playerx) && (y == playery)) { /* if energy hits player */
798 cursors();
799 lprcat("\nYou are hit my your own magic!");
800 beep();
801 lastnum = 278;
802 losehp(dam);
803 return;
804 }
805 if (c[BLINDCOUNT] == 0) { /* if not blind show effect */
806 cursor(x + 1, y + 1);
807 lprc(cshow);
808 nap(delay);
809 show1cell(x, y);
810 }
811 if ((m = mitem[x][y])) { /* is there a monster there? */
812 ifblind(x, y);
813 if (nospell(spnum, m)) {
814 lasthx = x;
815 lasthy = y;
816 return;
817 }
818 cursors();
819 lprc('\n');
820 lprintf(str, lastmonst);
821 dam -= hitm(x, y, dam);
822 show1cell(x, y);
823 nap(1000);
824 x -= dx;
825 y -= dy;
826 } else
827 switch (*(p = &item[x][y])) {
828 case OWALL:
829 cursors();
830 lprc('\n');
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");
836 god3: *p = 0;
837 god: know[x][y] = 0;
838 show1cell(x, y);
839 }
840 god2: dam = 0;
841 break;
842
843 case OCLOSEDDOOR:
844 cursors();
845 lprc('\n');
846 lprintf(str, "door");
847 if (dam >= 40) {
848 lprcat(" The door is blasted apart");
849 goto god3;
850 }
851 goto god2;
852
853 case OSTATUE:
854 cursors();
855 lprc('\n');
856 lprintf(str, "statue");
857 if (c[HARDGAME] < 3)
858 if (dam > 44) {
859 lprcat(" The statue crumbles");
860 *p = OBOOK;
861 iarg[x][y] = level;
862 goto god;
863 }
864 goto god2;
865
866 case OTHRONE:
867 cursors();
868 lprc('\n');
869 lprintf(str, "throne");
870 if (dam > 39) {
871 mitem[x][y] = GNOMEKING;
872 hitp[x][y] = monster[GNOMEKING].hitpoints;
873 *p = OTHRONE2;
874 goto god;
875 }
876 goto god2;
877
878 case OMIRROR:
879 dx *= -1;
880 dy *= -1;
881 break;
882 }
883 dam -= 3 + (c[HARDGAME] >> 1);
884 }
885 }
886
887 /*
888 * ifblind(x,y) Routine to put "monster" or the monster name into lastmosnt
889 * int x,y;
890 *
891 * Subroutine to copy the word "monster" into lastmonst if the player is blind
892 * Enter with the coordinates (x,y) of the monster
893 * Returns no value.
894 */
895 static void
ifblind(int x,int y)896 ifblind(int x, int y)
897 {
898 const char *p;
899
900 vxy(&x, &y); /* verify correct x,y coordinates */
901 if (c[BLINDCOUNT]) {
902 lastnum = 279;
903 p = "monster";
904 } else {
905 lastnum = mitem[x][y];
906 p = monster[lastnum].name;
907 }
908 strcpy(lastmonst, p);
909 }
910
911 /*
912 * tdirect(spnum) Routine to teleport away a monster
913 * int spnum;
914 *
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
917 * Returns no value.
918 */
919 static void
tdirect(int spnum)920 tdirect(int spnum)
921 {
922 int x, y;
923 int m;
924 if (spnum < 0 || spnum >= SPNUM) /* bad args */
925 return;
926 if (isconfuse())
927 return;
928 dirsub(&x, &y);
929 if ((m = mitem[x][y]) == 0) {
930 lprcat(" There wasn't anything there!");
931 return;
932 }
933 ifblind(x, y);
934 if (nospell(spnum, m)) {
935 lasthx = x;
936 lasthy = y;
937 return;
938 }
939 fillmonst(m);
940 mitem[x][y] = know[x][y] = 0;
941 }
942
943 /*
944 * omnidirect(sp,dam,str) Routine to damage all monsters 1 square from player
945 * int sp,dam;
946 * char *str;
947 *
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.
951 * Returns no value.
952 */
953 static void
omnidirect(int spnum,int dam,const char * str)954 omnidirect(int spnum, int dam, const char *str)
955 {
956 int x, y, m;
957 if (spnum < 0 || spnum >= SPNUM || str == NULL) /* bad args */
958 return;
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) {
963 ifblind(x, y);
964 cursors();
965 lprc('\n');
966 lprintf(str, lastmonst);
967 hitm(x, y, dam);
968 nap(800);
969 } else {
970 lasthx = x;
971 lasthy = y;
972 }
973 }
974 }
975 }
976
977 /*
978 * static dirsub(x,y) Routine to ask for direction, then modify x,y for it
979 * int *x,*y;
980 *
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).
984 */
985 static int
dirsub(int * x,int * y)986 dirsub(int *x, int *y)
987 {
988 int i;
989 lprcat("\nIn What Direction? ");
990 for (i = 0;;)
991 switch (getchr()) {
992 case 'b':
993 i++;
994 /* FALLTHROUGH */
995 case 'n':
996 i++;
997 /* FALLTHROUGH */
998 case 'y':
999 i++;
1000 /* FALLTHROUGH */
1001 case 'u':
1002 i++;
1003 /* FALLTHROUGH */
1004 case 'h':
1005 i++;
1006 /* FALLTHROUGH */
1007 case 'k':
1008 i++;
1009 /* FALLTHROUGH */
1010 case 'l':
1011 i++;
1012 /* FALLTHROUGH */
1013 case 'j':
1014 i++;
1015 goto out;
1016 }
1017 out:
1018 *x = playerx + diroffx[i];
1019 *y = playery + diroffy[i];
1020 vxy(x, y);
1021 return (i);
1022 }
1023
1024 /*
1025 * vxy(x,y) Routine to verify/fix coordinates for being within bounds
1026 * int *x,*y;
1027 *
1028 * Function to verify x & y are within the bounds for a level
1029 * If *x or *y is not within the absolute bounds for a level, fix them so that
1030 * they are on the level.
1031 * Returns TRUE if it was out of bounds, and the *x & *y in the calling
1032 * routine are affected.
1033 */
1034 int
vxy(int * x,int * y)1035 vxy(int *x, int *y)
1036 {
1037 int flag = 0;
1038 if (*x < 0) {
1039 *x = 0;
1040 flag++;
1041 }
1042 if (*y < 0) {
1043 *y = 0;
1044 flag++;
1045 }
1046 if (*x >= MAXX) {
1047 *x = MAXX - 1;
1048 flag++;
1049 }
1050 if (*y >= MAXY) {
1051 *y = MAXY - 1;
1052 flag++;
1053 }
1054 return (flag);
1055 }
1056
1057 /*
1058 * dirpoly(spnum) Routine to ask for a direction and polymorph a monst
1059 * int spnum;
1060 *
1061 * Subroutine to polymorph a monster and ask for the direction its in
1062 * Enter with the spell number in spmun.
1063 * Returns no value.
1064 */
1065 static void
dirpoly(int spnum)1066 dirpoly(int spnum)
1067 {
1068 int x, y, m;
1069 if (spnum < 0 || spnum >= SPNUM) /* bad args */
1070 return;
1071 if (isconfuse()) /* if he is confused, he can't aim his magic */
1072 return;
1073 dirsub(&x, &y);
1074 if (mitem[x][y] == 0) {
1075 lprcat(" There wasn't anything there!");
1076 return;
1077 }
1078 ifblind(x, y);
1079 if (nospell(spnum, mitem[x][y])) {
1080 lasthx = x;
1081 lasthy = y;
1082 return;
1083 }
1084 while (monster[m = mitem[x][y] = rnd(MAXMONST + 7)].genocided)
1085 ;
1086 hitp[x][y] = monster[m].hitpoints;
1087 show1cell(x, y); /* show the new monster */
1088 }
1089
1090 /*
1091 * hitmonster(x,y) Function to hit a monster at the designated coordinates
1092 * int x,y;
1093 *
1094 * This routine is used for a bash & slash type attack on a monster
1095 * Enter with the coordinates of the monster in (x,y).
1096 * Returns no value.
1097 */
1098 void
hitmonster(int x,int y)1099 hitmonster(int x, int y)
1100 {
1101 int tmp, monst, damag = 0, flag;
1102 if (c[TIMESTOP]) /* not if time stopped */
1103 return;
1104 vxy(&x, &y); /* verify coordinates are within range */
1105 if ((monst = mitem[x][y]) == 0)
1106 return;
1107 hit3flag = 1;
1108 ifblind(x, y);
1109 tmp = monster[monst].armorclass + c[LEVEL] + c[DEXTERITY] +
1110 c[WCLASS] / 4 - 12;
1111 cursors();
1112 /* need at least random chance to hit */
1113 if ((rnd(20) < tmp - c[HARDGAME]) || (rnd(71) < 5)) {
1114 lprcat("\nYou hit");
1115 flag = 1;
1116 damag = fullhit(1);
1117 if (damag < 9999)
1118 damag = rnd(damag) + 1;
1119 } else {
1120 lprcat("\nYou missed");
1121 flag = 0;
1122 }
1123 lprcat(" the ");
1124 lprcat(lastmonst);
1125 if (flag) /* if the monster was hit */
1126 if ((monst == RUSTMONSTER) || (monst == DISENCHANTRESS) || (monst == CUBE))
1127 if (c[WIELD] > 0)
1128 if (ivenarg[c[WIELD]] > -10) {
1129 lprintf("\nYour weapon is dulled by the %s", lastmonst);
1130 beep();
1131 --ivenarg[c[WIELD]];
1132 }
1133 if (flag)
1134 hitm(x, y, damag);
1135 if (monst == VAMPIRE)
1136 if (hitp[x][y] < 25) {
1137 mitem[x][y] = BAT;
1138 know[x][y] = 0;
1139 }
1140 }
1141
1142 /*
1143 * hitm(x,y,amt) Function to just hit a monster at a given coordinates
1144 * int x,y,amt;
1145 *
1146 * Returns the number of hitpoints the monster absorbed
1147 * This routine is used to specifically damage a monster at a location (x,y)
1148 * Called by hitmonster(x,y)
1149 */
1150 int
hitm(int x,int y,int amt)1151 hitm(int x, int y, int amt)
1152 {
1153 int monst;
1154 int hpoints, amt2;
1155 vxy(&x, &y); /* verify coordinates are within range */
1156 amt2 = amt; /* save initial damage so we can return it */
1157 monst = mitem[x][y];
1158 if (c[HALFDAM]) /* if half damage curse adjust damage points */
1159 amt >>= 1;
1160 if (amt <= 0)
1161 amt2 = amt = 1;
1162 lasthx = x;
1163 lasthy = y;
1164 stealth[x][y] = 1; /* make sure hitting monst breaks stealth condition */
1165 c[HOLDMONST] = 0; /* hit a monster breaks hold monster spell */
1166 switch (monst) { /* if a dragon and orb(s) of dragon slaying */
1167 case WHITEDRAGON:
1168 case REDDRAGON:
1169 case GREENDRAGON:
1170 case BRONZEDRAGON:
1171 case PLATINUMDRAGON:
1172 case SILVERDRAGON:
1173 amt *= 1 + (c[SLAYING] << 1);
1174 break;
1175 }
1176 /* invincible monster fix is here */
1177 if (hitp[x][y] > monster[monst].hitpoints)
1178 hitp[x][y] = monster[monst].hitpoints;
1179 if ((hpoints = hitp[x][y]) <= amt) {
1180 #ifdef EXTRA
1181 c[MONSTKILLED]++;
1182 #endif
1183 lprintf("\nThe %s died!", lastmonst);
1184 raiseexperience((long)monster[monst].experience);
1185 amt = monster[monst].gold;
1186 if (amt > 0)
1187 dropgold(rnd(amt) + amt);
1188 dropsomething(monst);
1189 disappear(x, y);
1190 bottomline();
1191 return (hpoints);
1192 }
1193 hitp[x][y] = hpoints - amt;
1194 return (amt2);
1195 }
1196
1197 /*
1198 * hitplayer(x,y) Function for the monster to hit the player from (x,y)
1199 * int x,y;
1200 *
1201 * Function for the monster to hit the player with monster at location x,y
1202 * Returns nothing of value.
1203 */
1204 void
hitplayer(int x,int y)1205 hitplayer(int x, int y)
1206 {
1207 int dam, tmp, mster, bias;
1208 vxy(&x, &y); /* verify coordinates are within range */
1209 lastnum = mster = mitem[x][y];
1210 /* spirit naga's and poltergeist's do nothing if scarab of negate spirit */
1211 if (c[NEGATESPIRIT] || c[SPIRITPRO])
1212 if ((mster == POLTERGEIST) || (mster == SPIRITNAGA))
1213 return;
1214 /* if undead and cube of undead control */
1215 if (c[CUBEofUNDEAD] || c[UNDEADPRO])
1216 if ((mster == VAMPIRE) || (mster == WRAITH) || (mster == ZOMBIE))
1217 return;
1218 if ((know[x][y] & 1) == 0) {
1219 know[x][y] = 1;
1220 show1cell(x, y);
1221 }
1222 bias = (c[HARDGAME]) + 1;
1223 hitflag = hit2flag = hit3flag = 1;
1224 yrepcount = 0;
1225 cursors();
1226 ifblind(x, y);
1227 if (c[INVISIBILITY])
1228 if (rnd(33) < 20) {
1229 lprintf("\nThe %s misses wildly", lastmonst);
1230 return;
1231 }
1232 if (c[CHARMCOUNT])
1233 if (rnd(30) + 5 * monster[mster].level - c[CHARISMA] < 30) {
1234 lprintf("\nThe %s is awestruck at your magnificence!", lastmonst);
1235 return;
1236 }
1237 if (mster == BAT)
1238 dam = 1;
1239 else {
1240 dam = monster[mster].damage;
1241 dam += rnd((int)((dam < 1) ? 1 : dam)) + monster[mster].level;
1242 }
1243 tmp = 0;
1244 if (monster[mster].attack > 0)
1245 if (((dam + bias + 8) > c[AC]) || (rnd((int)((c[AC] > 0) ? c[AC] : 1)) == 1)) {
1246 if (spattack(monster[mster].attack, x, y)) {
1247 flushall();
1248 return;
1249 }
1250 tmp = 1;
1251 bias -= 2;
1252 cursors();
1253 }
1254 if (((dam + bias) > c[AC]) || (rnd((int)((c[AC] > 0) ? c[AC] : 1)) == 1)) {
1255 lprintf("\n The %s hit you ", lastmonst);
1256 tmp = 1;
1257 if ((dam -= c[AC]) < 0)
1258 dam = 0;
1259 if (dam > 0) {
1260 losehp(dam);
1261 bottomhp();
1262 flushall();
1263 }
1264 }
1265 if (tmp == 0)
1266 lprintf("\n The %s missed ", lastmonst);
1267 }
1268
1269 /*
1270 * dropsomething(monst) Function to create an object when a monster dies
1271 * int monst;
1272 *
1273 * Function to create an object near the player when certain monsters are killed
1274 * Enter with the monster number
1275 * Returns nothing of value.
1276 */
1277 static void
dropsomething(int monst)1278 dropsomething(int monst)
1279 {
1280 switch (monst) {
1281 case ORC:
1282 case NYMPH:
1283 case ELF:
1284 case TROGLODYTE:
1285 case TROLL:
1286 case ROTHE:
1287 case VIOLETFUNGI:
1288 case PLATINUMDRAGON:
1289 case GNOMEKING:
1290 case REDDRAGON:
1291 something(level);
1292 return;
1293
1294 case LEPRECHAUN:
1295 if (rnd(101) >= 75)
1296 creategem();
1297 if (rnd(5) == 1)
1298 dropsomething(LEPRECHAUN);
1299 return;
1300 }
1301 }
1302
1303 /*
1304 * dropgold(amount) Function to drop some gold around player
1305 * int amount;
1306 *
1307 * Enter with the number of gold pieces to drop
1308 * Returns nothing of value.
1309 */
1310 void
dropgold(int amount)1311 dropgold(int amount)
1312 {
1313 if (amount > 250)
1314 createitem(OMAXGOLD, amount / 100);
1315 else
1316 createitem(OGOLDPILE, amount);
1317 }
1318
1319 /*
1320 * something(lvl) Function to create a random item around player
1321 * int lvl;
1322 *
1323 * Function to create an item from a designed probability around player
1324 * Enter with the cave level on which something is to be dropped
1325 * Returns nothing of value.
1326 */
1327 void
something(int lvl)1328 something(int lvl)
1329 {
1330 int j;
1331 int i;
1332 if (lvl < 0 || lvl > MAXLEVEL + MAXVLEVEL) /* correct level? */
1333 return;
1334 if (rnd(101) < 8) /* possibly more than one item */
1335 something(lvl);
1336 j = newobject(lvl, &i);
1337 createitem(j, i);
1338 }
1339
1340 /*
1341 * newobject(lev,i) Routine to return a randomly selected new object
1342 * int lev,*i;
1343 *
1344 * Routine to return a randomly selected object to be created
1345 * Returns the object number created, and sets *i for its argument
1346 * Enter with the cave level and a pointer to the items arg
1347 */
1348 static char nobjtab[] = { 0, OSCROLL, OSCROLL, OSCROLL, OSCROLL, OPOTION,
1349 OPOTION, OPOTION, OPOTION, OGOLDPILE, OGOLDPILE, OGOLDPILE, OGOLDPILE,
1350 OBOOK, OBOOK, OBOOK, OBOOK, ODAGGER, ODAGGER, ODAGGER, OLEATHER, OLEATHER,
1351 OLEATHER, OREGENRING, OPROTRING, OENERGYRING, ODEXRING, OSTRRING, OSPEAR,
1352 OBELT, ORING, OSTUDLEATHER, OSHIELD, OFLAIL, OCHAIN, O2SWORD, OPLATE,
1353 OLONGSWORD };
1354
1355 int
newobject(int lev,int * i)1356 newobject(int lev, int *i)
1357 {
1358 int tmp = 32, j;
1359 if (level < 0 || level > MAXLEVEL + MAXVLEVEL) /* correct level? */
1360 return (0);
1361 if (lev > 6)
1362 tmp = 37;
1363 else if (lev > 4)
1364 tmp = 35;
1365 j = nobjtab[tmp = rnd(tmp)]; /* the object type */
1366 switch (tmp) {
1367 case 1:
1368 case 2:
1369 case 3:
1370 case 4:
1371 *i = newscroll();
1372 break;
1373 case 5:
1374 case 6:
1375 case 7:
1376 case 8:
1377 *i = newpotion();
1378 break;
1379 case 9:
1380 case 10:
1381 case 11:
1382 case 12:
1383 *i = rnd((lev + 1) * 10) + lev * 10 + 10;
1384 break;
1385 case 13:
1386 case 14:
1387 case 15:
1388 case 16:
1389 *i = lev;
1390 break;
1391 case 17:
1392 case 18:
1393 case 19:
1394 if (!(*i = newdagger()))
1395 return (0);
1396 break;
1397 case 20:
1398 case 21:
1399 case 22:
1400 if (!(*i = newleather()))
1401 return (0);
1402 break;
1403 case 23:
1404 case 32:
1405 case 35:
1406 *i = rund(lev / 3 + 1);
1407 break;
1408 case 24:
1409 case 26:
1410 *i = rnd(lev / 4 + 1);
1411 break;
1412 case 25:
1413 *i = rund(lev / 4 + 1);
1414 break;
1415 case 27:
1416 *i = rnd(lev / 2 + 1);
1417 break;
1418 case 30:
1419 case 33:
1420 *i = rund(lev / 2 + 1);
1421 break;
1422 case 28:
1423 *i = rund(lev / 3 + 1);
1424 if (*i == 0)
1425 return (0);
1426 break;
1427 case 29:
1428 case 31:
1429 *i = rund(lev / 2 + 1);
1430 if (*i == 0)
1431 return (0);
1432 break;
1433 case 34:
1434 *i = newchain();
1435 break;
1436 case 36:
1437 *i = newplate();
1438 break;
1439 case 37:
1440 *i = newsword();
1441 break;
1442 }
1443 return (j);
1444 }
1445
1446 /*
1447 * spattack(atckno,xx,yy) Function to process special attacks from monsters
1448 * int atckno,xx,yy;
1449 *
1450 * Enter with the special attack number, and the coordinates (xx,yy)
1451 * of the monster that is special attacking
1452 * Returns 1 if must do a show1cell(xx,yy) upon return, 0 otherwise
1453 *
1454 * atckno monster effect
1455 * ---------------------------------------------------
1456 * 0 none
1457 * 1 rust monster eat armor
1458 * 2 hell hound breathe light fire
1459 * 3 dragon breathe fire
1460 * 4 giant centipede weakening sing
1461 * 5 white dragon cold breath
1462 * 6 wraith drain level
1463 * 7 waterlord water gusher
1464 * 8 leprechaun steal gold
1465 * 9 disenchantress disenchant weapon or armor
1466 * 10 ice lizard hits with barbed tail
1467 * 11 umber hulk confusion
1468 * 12 spirit naga cast spells taken from special attacks
1469 * 13 platinum dragon psionics
1470 * 14 nymph steal objects
1471 * 15 bugbear bite
1472 * 16 osequip bite
1473 *
1474 * char rustarm[ARMORTYPES][2];
1475 * special array for maximum rust damage to armor from rustmonster
1476 * format is: { armor type , minimum attribute
1477 */
1478 #define ARMORTYPES 6
1479 static char rustarm[ARMORTYPES][2] = {
1480 { OSTUDLEATHER, -2 },
1481 { ORING, -4 },
1482 { OCHAIN, -5 },
1483 { OSPLINT, -6 },
1484 { OPLATE, -8 },
1485 { OPLATEARMOR, -9 }
1486 };
1487 static char spsel[] = { 1, 2, 3, 5, 6, 8, 9, 11, 13, 14 };
1488
1489 static int
spattack(int x,int xx,int yy)1490 spattack(int x, int xx, int yy)
1491 {
1492 int i, j = 0, k, m;
1493 const char *p = NULL;
1494
1495 if (c[CANCELLATION])
1496 return (0);
1497 vxy(&xx, &yy); /* verify x & y coordinates */
1498 switch (x) {
1499 case 1: /* rust your armor, j=1 when rusting has occurred */
1500 m = k = c[WEAR];
1501 if ((i = c[SHIELD]) != -1) {
1502 if (--ivenarg[i] < -1)
1503 ivenarg[i] = -1;
1504 else
1505 j = 1;
1506 }
1507 if ((j == 0) && (k != -1)) {
1508 m = iven[k];
1509 for (i = 0; i < ARMORTYPES; i++)
1510 /* find his armor in table */
1511 if (m == rustarm[i][0]) {
1512 if (--ivenarg[k] < rustarm[i][1])
1513 ivenarg[k] = rustarm[i][1];
1514 else
1515 j = 1;
1516 break;
1517 }
1518 }
1519 if (j == 0) /* if rusting did not occur */
1520 switch (m) {
1521 case OLEATHER:
1522 p = "\nThe %s hit you -- Your lucky you have leather on";
1523 break;
1524 case OSSPLATE:
1525 p = "\nThe %s hit you -- Your fortunate to have stainless steel armor!";
1526 break;
1527 }
1528 else {
1529 beep();
1530 p = "\nThe %s hit you -- your armor feels weaker";
1531 }
1532 break;
1533
1534 case 2:
1535 i = rnd(15) + 8 - c[AC];
1536 spout: p = "\nThe %s breathes fire at you!";
1537 if (c[FIRERESISTANCE])
1538 p = "\nThe %s's flame doesn't phase you!";
1539 else
1540 spout2: if (p) {
1541 lprintf(p, lastmonst);
1542 beep();
1543 }
1544 checkloss(i);
1545 return (0);
1546
1547 case 3:
1548 i = rnd(20) + 25 - c[AC];
1549 goto spout;
1550
1551 case 4:
1552 if (c[STRENGTH] > 3) {
1553 p = "\nThe %s stung you! You feel weaker";
1554 beep();
1555 --c[STRENGTH];
1556 } else
1557 p = "\nThe %s stung you!";
1558 break;
1559
1560 case 5:
1561 p = "\nThe %s blasts you with his cold breath";
1562 i = rnd(15) + 18 - c[AC];
1563 goto spout2;
1564
1565 case 6:
1566 lprintf("\nThe %s drains you of your life energy!", lastmonst);
1567 loselevel();
1568 beep();
1569 return (0);
1570
1571 case 7:
1572 p = "\nThe %s got you with a gusher!";
1573 i = rnd(15) + 25 - c[AC];
1574 goto spout2;
1575
1576 case 8:
1577 if (c[NOTHEFT]) /* he has a device of no theft */
1578 return (0);
1579 if (c[GOLD]) {
1580 p = "\nThe %s hit you -- Your purse feels lighter";
1581 if (c[GOLD] > 32767)
1582 c[GOLD] >>= 1;
1583 else
1584 c[GOLD] -= rnd((int)(1 + (c[GOLD] >> 1)));
1585 if (c[GOLD] < 0)
1586 c[GOLD] = 0;
1587 } else
1588 p = "\nThe %s couldn't find any gold to steal";
1589 lprintf(p, lastmonst);
1590 disappear(xx, yy);
1591 beep();
1592 bottomgold();
1593 return (1);
1594
1595 case 9:
1596 for (j = 50;;) { /* disenchant */
1597 i = rund(26);
1598 m = iven[i]; /* randomly select item */
1599 if (m > 0 && ivenarg[i] > 0 && m != OSCROLL && m != OPOTION) {
1600 if ((ivenarg[i] -= 3) < 0)
1601 ivenarg[i] = 0;
1602 lprintf("\nThe %s hits you -- you feel a sense of loss", lastmonst);
1603 srcount = 0;
1604 beep();
1605 show3(i);
1606 bottomline();
1607 return (0);
1608 }
1609 if (--j <= 0) {
1610 p = "\nThe %s nearly misses";
1611 break;
1612 }
1613 break;
1614 }
1615 break;
1616
1617 case 10:
1618 p = "\nThe %s hit you with his barbed tail";
1619 i = rnd(25) - c[AC];
1620 goto spout2;
1621
1622 case 11:
1623 p = "\nThe %s has confused you";
1624 beep();
1625 c[CONFUSE] += 10 + rnd(10);
1626 break;
1627
1628 case 12: /* performs any number of other special attacks */
1629 return (spattack(spsel[rund(10)], xx, yy));
1630
1631 case 13:
1632 p = "\nThe %s flattens you with his psionics!";
1633 i = rnd(15) + 30 - c[AC];
1634 goto spout2;
1635
1636 case 14:
1637 if (c[NOTHEFT]) /* he has device of no theft */
1638 return (0);
1639 if (emptyhanded() == 1) {
1640 p = "\nThe %s couldn't find anything to steal";
1641 break;
1642 }
1643 lprintf("\nThe %s picks your pocket and takes:", lastmonst);
1644 beep();
1645 if (stealsomething() == 0)
1646 lprcat(" nothing");
1647 disappear(xx, yy);
1648 bottomline();
1649 return (1);
1650
1651 case 15:
1652 i = rnd(10) + 5 - c[AC];
1653 spout3: p = "\nThe %s bit you!";
1654 goto spout2;
1655
1656 case 16:
1657 i = rnd(15) + 10 - c[AC];
1658 goto spout3;
1659 }
1660 if (p) {
1661 lprintf(p, lastmonst);
1662 bottomline();
1663 }
1664 return (0);
1665 }
1666
1667 /*
1668 * checkloss(x) Routine to subtract hp from user and flag bottomline display
1669 * int x;
1670 *
1671 * Routine to subtract hitpoints from the user and flag the bottomline display
1672 * Enter with the number of hit points to lose
1673 * Note: if x > c[HP] this routine could kill the player!
1674 */
1675 void
checkloss(int x)1676 checkloss(int x)
1677 {
1678 if (x > 0) {
1679 losehp(x);
1680 bottomhp();
1681 }
1682 }
1683
1684 /*
1685 * annihilate() Routine to annihilate all monsters around player (playerx,playery)
1686 *
1687 * Gives player experience, but no dropped objects
1688 * Returns the experience gained from all monsters killed
1689 */
1690 long
annihilate(void)1691 annihilate(void)
1692 {
1693 int i, j;
1694 long k;
1695 char *p;
1696 for (k = 0, i = playerx - 1; i <= playerx + 1; i++)
1697 for (j = playery - 1; j <= playery + 1; j++)
1698 if (!vxy(&i, &j)) { /* if not out of bounds */
1699 if (*(p = &mitem[i][j])) { /* if a monster there */
1700 if (*p < DEMONLORD + 2) {
1701 k += monster[(int)*p].experience;
1702 *p = know[i][j] = 0;
1703 } else {
1704 lprintf("\nThe %s barely escapes being annihilated!", monster[(int)*p].name);
1705 hitp[i][j] = (hitp[i][j] >> 1) + 1; /* lose half hit points*/
1706 }
1707 }
1708 }
1709 if (k > 0) {
1710 lprcat("\nYou hear loud screams of agony!");
1711 raiseexperience((long)k);
1712 }
1713 return (k);
1714 }
1715
1716 /*
1717 * newsphere(x,y,dir,lifetime) Function to create a new sphere of annihilation
1718 * int x,y,dir,lifetime;
1719 *
1720 * Enter with the coordinates of the sphere in x,y
1721 * the direction (0-8 diroffx format) in dir, and the lifespan of the
1722 * sphere in lifetime (in turns)
1723 * Returns the number of spheres currently in existence
1724 */
1725 long
newsphere(int x,int y,int dir,int life)1726 newsphere(int x, int y, int dir, int life)
1727 {
1728 int m;
1729 struct sphere *sp;
1730 if (((sp = malloc(sizeof(struct sphere)))) == NULL)
1731 return (c[SPHCAST]); /* can't malloc, therefore failure */
1732 if (dir >= 9) /* no movement if direction not found */
1733 dir = 0;
1734 if (level == 0) /* don't go out of bounds */
1735 vxy(&x, &y);
1736 else {
1737 if (x < 1)
1738 x = 1;
1739 if (x >= MAXX - 1)
1740 x = MAXX - 2;
1741 if (y < 1)
1742 y = 1;
1743 if (y >= MAXY - 1)
1744 y = MAXY - 2;
1745 }
1746 if ((m = mitem[x][y]) >= DEMONLORD + 4) { /* demons dispel spheres */
1747 know[x][y] = 1;
1748 show1cell(x, y); /* show the demon (ha ha) */
1749 cursors();
1750 lprintf("\nThe %s dispels the sphere!", monster[m].name);
1751 beep();
1752 rmsphere(x, y); /* remove any spheres that are here */
1753 return (c[SPHCAST]);
1754 }
1755 if (m == DISENCHANTRESS) { /* disenchantress cancels spheres */
1756 cursors();
1757 lprintf("\nThe %s causes cancellation of the sphere!", monster[m].name);
1758 beep();
1759 boom: sphboom(x, y); /* blow up stuff around sphere */
1760 rmsphere(x, y); /* remove any spheres that are here */
1761 return (c[SPHCAST]);
1762 }
1763 if (c[CANCELLATION]) { /* cancellation cancels spheres */
1764 cursors();
1765 lprcat("\nAs the cancellation takes effect, you hear a great earth shaking blast!");
1766 beep();
1767 goto boom;
1768 }
1769 if (item[x][y] == OANNIHILATION) { /* collision of spheres detonates spheres */
1770 cursors();
1771 lprcat("\nTwo spheres of annihilation collide! You hear a great earth shaking blast!");
1772 beep();
1773 rmsphere(x, y);
1774 goto boom;
1775 }
1776 if (playerx == x && playery == y) { /* collision of sphere and player! */
1777 cursors();
1778 lprcat("\nYou have been enveloped by the zone of nothingness!\n");
1779 beep();
1780 rmsphere(x, y); /* remove any spheres that are here */
1781 nap(4000);
1782 died(258);
1783 }
1784 item[x][y] = OANNIHILATION;
1785 mitem[x][y] = 0;
1786 know[x][y] = 1;
1787 show1cell(x, y); /* show the new sphere */
1788 sp->x = x;
1789 sp->y = y;
1790 sp->lev = level;
1791 sp->dir = dir;
1792 sp->lifetime = life;
1793 sp->p = 0;
1794 if (spheres == 0) /* if first node in the sphere list */
1795 spheres = sp;
1796 else { /* add sphere to beginning of linked list */
1797 sp->p = spheres;
1798 spheres = sp;
1799 }
1800 return (++c[SPHCAST]); /* one more sphere in the world */
1801 }
1802
1803 /*
1804 * rmsphere(x,y) Function to delete a sphere of annihilation from list
1805 * int x,y;
1806 *
1807 * Enter with the coordinates of the sphere (on current level)
1808 * Returns the number of spheres currently in existence
1809 */
1810 long
rmsphere(int x,int y)1811 rmsphere(int x, int y)
1812 {
1813 struct sphere *sp, *sp2 = NULL;
1814 for (sp = spheres; sp; sp2 = sp, sp = sp->p)
1815 if (level == sp->lev) /* is sphere on this level? */
1816 if ((x == sp->x) && (y == sp->y)) { /* locate sphere at this location */
1817 item[x][y] = mitem[x][y] = 0;
1818 know[x][y] = 1;
1819 show1cell(x, y); /* show the now missing sphere */
1820 --c[SPHCAST];
1821 if (sp == spheres) {
1822 sp2 = sp;
1823 spheres = sp->p;
1824 free(sp2);
1825 } else {
1826 sp2->p = sp->p;
1827 free(sp);
1828 }
1829 break;
1830 }
1831 return (c[SPHCAST]); /* return number of spheres in the world */
1832 }
1833
1834 /*
1835 * sphboom(x,y) Function to perform the effects of a sphere detonation
1836 * int x,y;
1837 *
1838 * Enter with the coordinates of the blast, Returns no value
1839 */
1840 static void
sphboom(int x,int y)1841 sphboom(int x, int y)
1842 {
1843 int i, j;
1844 if (c[HOLDMONST])
1845 c[HOLDMONST] = 1;
1846 if (c[CANCELLATION])
1847 c[CANCELLATION] = 1;
1848 for (j = max(1, x - 2); j < min(x + 3, MAXX - 1); j++)
1849 for (i = max(1, y - 2); i < min(y + 3, MAXY - 1); i++) {
1850 item[j][i] = mitem[j][i] = 0;
1851 show1cell(j, i);
1852 if (playerx == j && playery == i) {
1853 cursors();
1854 beep();
1855 lprcat("\nYou were too close to the sphere!");
1856 nap(3000);
1857 died(283); /* player killed in explosion */
1858 }
1859 }
1860 }
1861
1862 /*
1863 * genmonst() Function to ask for monster and genocide from game
1864 *
1865 * This is done by setting a flag in the monster[] structure
1866 */
1867 static void
genmonst(void)1868 genmonst(void)
1869 {
1870 int i, j;
1871 cursors();
1872 lprcat("\nGenocide what monster? ");
1873 for (i = 0; (!isalpha(i)) && (i != ' '); i = getchr())
1874 ;
1875 lprc(i);
1876 for (j = 0; j < MAXMONST; j++) /* search for the monster type */
1877 if (monstnamelist[j] == i) { /* have we found it? */
1878 monster[j].genocided = 1; /* genocided from game */
1879 lprintf(" There will be no more %s's", monster[j].name);
1880 /* now wipe out monsters on this level */
1881 newcavelevel(level);
1882 draws(0, MAXX, 0, MAXY);
1883 bot_linex();
1884 return;
1885 }
1886 lprcat(" You sense failure!");
1887 }
1888