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