Commit | Line | Data |
---|---|---|
9546bac0 WJ |
1 | /* |
2 | * monster.c Larn is copyrighted 1986 by Noah Morgan. | |
3 | * | |
4 | * This file contains the following functions: | |
5 | * ---------------------------------------------------------------------------- | |
6 | * | |
7 | * createmonster(monstno) Function to create a monster next to the player | |
8 | * int monstno; | |
9 | * | |
10 | * int cgood(x,y,itm,monst) Function to check location for emptiness | |
11 | * int x,y,itm,monst; | |
12 | * | |
13 | * createitem(it,arg) Routine to place an item next to the player | |
14 | * int it,arg; | |
15 | * | |
16 | * cast() Subroutine called by parse to cast a spell for the user | |
17 | * | |
18 | * speldamage(x) Function to perform spell functions cast by the player | |
19 | * int x; | |
20 | * | |
21 | * loseint() Routine to decrement your int (intelligence) if > 3 | |
22 | * | |
23 | * isconfuse() Routine to check to see if player is confused | |
24 | * | |
25 | * nospell(x,monst) Routine to return 1 if a spell doesn't affect a monster | |
26 | * int x,monst; | |
27 | * | |
28 | * fullhit(xx) Function to return full damage against a monst (aka web) | |
29 | * int xx; | |
30 | * | |
31 | * direct(spnum,dam,str,arg) Routine to direct spell damage 1 square in 1 dir | |
32 | * int spnum,dam,arg; | |
33 | * char *str; | |
34 | * | |
35 | * godirect(spnum,dam,str,delay,cshow) Function to perform missile attacks | |
36 | * int spnum,dam,delay; | |
37 | * char *str,cshow; | |
38 | * | |
39 | * ifblind(x,y) Routine to put "monster" or the monster name into lastmosnt | |
40 | * int x,y; | |
41 | * | |
42 | * tdirect(spnum) Routine to teleport away a monster | |
43 | * int spnum; | |
44 | * | |
45 | * omnidirect(sp,dam,str) Routine to damage all monsters 1 square from player | |
46 | * int sp,dam; | |
47 | * char *str; | |
48 | * | |
49 | * dirsub(x,y) Routine to ask for direction, then modify x,y for it | |
50 | * int *x,*y; | |
51 | * | |
52 | * vxy(x,y) Routine to verify/fix (*x,*y) for being within bounds | |
53 | * int *x,*y; | |
54 | * | |
55 | * dirpoly(spnum) Routine to ask for a direction and polymorph a monst | |
56 | * int spnum; | |
57 | * | |
58 | * hitmonster(x,y) Function to hit a monster at the designated coordinates | |
59 | * int x,y; | |
60 | * | |
61 | * hitm(x,y,amt) Function to just hit a monster at a given coordinates | |
62 | * int x,y,amt; | |
63 | * | |
64 | * hitplayer(x,y) Function for the monster to hit the player from (x,y) | |
65 | * int x,y; | |
66 | * | |
67 | * dropsomething(monst) Function to create an object when a monster dies | |
68 | * int monst; | |
69 | * | |
70 | * dropgold(amount) Function to drop some gold around player | |
71 | * int amount; | |
72 | * | |
73 | * something(level) Function to create a random item around player | |
74 | * int level; | |
75 | * | |
76 | * newobject(lev,i) Routine to return a randomly selected new object | |
77 | * int lev,*i; | |
78 | * | |
79 | * spattack(atckno,xx,yy) Function to process special attacks from monsters | |
80 | * int atckno,xx,yy; | |
81 | * | |
82 | * checkloss(x) Routine to subtract hp from user and flag bottomline display | |
83 | * int x; | |
84 | * | |
85 | * annihilate() Routine to annihilate monsters around player, playerx,playery | |
86 | * | |
87 | * newsphere(x,y,dir,lifetime) Function to create a new sphere of annihilation | |
88 | * int x,y,dir,lifetime; | |
89 | * | |
90 | * rmsphere(x,y) Function to delete a sphere of annihilation from list | |
91 | * int x,y; | |
92 | * | |
93 | * sphboom(x,y) Function to perform the effects of a sphere detonation | |
94 | * int x,y; | |
95 | * | |
96 | * genmonst() Function to ask for monster and genocide from game | |
97 | * | |
98 | */ | |
99 | #include "header.h" | |
100 | ||
101 | struct isave /* used for altar reality */ | |
102 | { | |
103 | char type; /* 0=item, 1=monster */ | |
104 | char id; /* item number or monster number */ | |
105 | short arg; /* the type of item or hitpoints of monster */ | |
106 | }; | |
107 | ||
108 | /* | |
109 | * createmonster(monstno) Function to create a monster next to the player | |
110 | * int monstno; | |
111 | * | |
112 | * Enter with the monster number (1 to MAXMONST+8) | |
113 | * Returns no value. | |
114 | */ | |
115 | createmonster(mon) | |
116 | int mon; | |
117 | { | |
118 | register int x,y,k,i; | |
119 | if (mon<1 || mon>MAXMONST+8) /* check for monster number out of bounds */ | |
120 | { | |
121 | beep(); lprintf("\ncan't createmonst(%d)\n",(long)mon); nap(3000); return; | |
122 | } | |
123 | while (monster[mon].genocided && mon<MAXMONST) mon++; /* genocided? */ | |
124 | for (k=rnd(8), i= -8; i<0; i++,k++) /* choose direction, then try all */ | |
125 | { | |
126 | if (k>8) k=1; /* wraparound the diroff arrays */ | |
127 | x = playerx + diroffx[k]; y = playery + diroffy[k]; | |
128 | if (cgood(x,y,0,1)) /* if we can create here */ | |
129 | { | |
130 | mitem[x][y] = mon; | |
131 | hitp[x][y] = monster[mon].hitpoints; | |
132 | stealth[x][y]=know[x][y]=0; | |
133 | switch(mon) | |
134 | { | |
135 | case ROTHE: case POLTERGEIST: case VAMPIRE: stealth[x][y]=1; | |
136 | }; | |
137 | return; | |
138 | } | |
139 | } | |
140 | } | |
141 | ||
142 | /* | |
143 | * int cgood(x,y,itm,monst) Function to check location for emptiness | |
144 | * int x,y,itm,monst; | |
145 | * | |
146 | * Routine to return TRUE if a location does not have itm or monst there | |
147 | * returns FALSE (0) otherwise | |
148 | * Enter with itm or monst TRUE or FALSE if checking it | |
149 | * Example: if itm==TRUE check for no item at this location | |
150 | * if monst==TRUE check for no monster at this location | |
151 | * This routine will return FALSE if at a wall or the dungeon exit on level 1 | |
152 | */ | |
153 | int cgood(x,y,itm,monst) | |
154 | register int x,y; | |
155 | int itm,monst; | |
156 | { | |
157 | if ((y>=0) && (y<=MAXY-1) && (x>=0) && (x<=MAXX-1)) /* within bounds? */ | |
158 | if (item[x][y]!=OWALL) /* can't make anything on walls */ | |
159 | if (itm==0 || (item[x][y]==0)) /* is it free of items? */ | |
160 | if (monst==0 || (mitem[x][y]==0)) /* is it free of monsters? */ | |
161 | if ((level!=1) || (x!=33) || (y!=MAXY-1)) /* not exit to level 1 */ | |
162 | return(1); | |
163 | return(0); | |
164 | } | |
165 | ||
166 | /* | |
167 | * createitem(it,arg) Routine to place an item next to the player | |
168 | * int it,arg; | |
169 | * | |
170 | * Enter with the item number and its argument (iven[], ivenarg[]) | |
171 | * Returns no value, thus we don't know about createitem() failures. | |
172 | */ | |
173 | createitem(it,arg) | |
174 | int it,arg; | |
175 | { | |
176 | register int x,y,k,i; | |
177 | if (it >= MAXOBJ) return; /* no such object */ | |
178 | for (k=rnd(8), i= -8; i<0; i++,k++) /* choose direction, then try all */ | |
179 | { | |
180 | if (k>8) k=1; /* wraparound the diroff arrays */ | |
181 | x = playerx + diroffx[k]; y = playery + diroffy[k]; | |
182 | if (cgood(x,y,1,0)) /* if we can create here */ | |
183 | { | |
184 | item[x][y] = it; know[x][y]=0; iarg[x][y]=arg; return; | |
185 | } | |
186 | } | |
187 | } | |
188 | ||
189 | /* | |
190 | * cast() Subroutine called by parse to cast a spell for the user | |
191 | * | |
192 | * No arguments and no return value. | |
193 | */ | |
194 | static char eys[] = "\nEnter your spell: "; | |
195 | cast() | |
196 | { | |
197 | register int i,j,a,b,d; | |
198 | cursors(); | |
199 | if (c[SPELLS]<=0) { lprcat("\nYou don't have any spells!"); return; } | |
200 | lprcat(eys); --c[SPELLS]; | |
201 | while ((a=getchar())=='D') | |
202 | { seemagic(-1); cursors(); lprcat(eys); } | |
203 | if (a=='\33') goto over; /* to escape casting a spell */ | |
204 | if ((b=getchar())=='\33') goto over; /* to escape casting a spell */ | |
205 | if ((d=getchar())=='\33') | |
206 | { over: lprcat(aborted); c[SPELLS]++; return; } /* to escape casting a spell */ | |
207 | #ifdef EXTRA | |
208 | c[SPELLSCAST]++; | |
209 | #endif | |
210 | for (lprc('\n'),j= -1,i=0; i<SPNUM; i++) /*seq search for his spell, hash?*/ | |
211 | if ((spelcode[i][0]==a) && (spelcode[i][1]==b) && (spelcode[i][2]==d)) | |
212 | if (spelknow[i]) | |
213 | { speldamage(i); j = 1; i=SPNUM; } | |
214 | ||
215 | if (j == -1) lprcat(" Nothing Happened "); | |
216 | bottomline(); | |
217 | } | |
218 | ||
219 | static int dirsub(); | |
220 | ||
221 | /* | |
222 | * speldamage(x) Function to perform spell functions cast by the player | |
223 | * int x; | |
224 | * | |
225 | * Enter with the spell number, returns no value. | |
226 | * Please insure that there are 2 spaces before all messages here | |
227 | */ | |
228 | speldamage(x) | |
229 | int x; | |
230 | { | |
231 | register int i,j,clev; | |
232 | int xl,xh,yl,yh; | |
233 | register char *p,*kn,*pm; | |
234 | if (x>=SPNUM) return; /* no such spell */ | |
235 | if (c[TIMESTOP]) { lprcat(" It didn't seem to work"); return; } /* not if time stopped */ | |
236 | clev = c[LEVEL]; | |
237 | if ((rnd(23)==7) || (rnd(18) > c[INTELLIGENCE])) | |
238 | { lprcat(" It didn't work!"); return; } | |
239 | if (clev*3+2 < x) { lprcat(" Nothing happens. You seem inexperienced at this"); return; } | |
240 | ||
241 | switch(x) | |
242 | { | |
243 | /* ----- LEVEL 1 SPELLS ----- */ | |
244 | ||
245 | case 0: if (c[PROTECTIONTIME]==0) c[MOREDEFENSES]+=2; /* protection field +2 */ | |
246 | c[PROTECTIONTIME] += 250; return; | |
247 | ||
248 | case 1: i = rnd(((clev+1)<<1)) + clev + 3; | |
249 | godirect(x,i,(clev>=2)?" Your missiles hit the %s":" Your missile hit the %s",100,'+'); /* magic missile */ | |
250 | ||
251 | return; | |
252 | ||
253 | case 2: if (c[DEXCOUNT]==0) c[DEXTERITY]+=3; /* dexterity */ | |
254 | c[DEXCOUNT] += 400; return; | |
255 | ||
256 | case 3: i=rnd(3)+1; | |
257 | p=" While the %s slept, you smashed it %d times"; | |
258 | ws: direct(x,fullhit(i),p,i); /* sleep */ return; | |
259 | ||
260 | case 4: /* charm monster */ c[CHARMCOUNT] += c[CHARISMA]<<1; return; | |
261 | ||
262 | case 5: godirect(x,rnd(10)+15+clev," The sound damages the %s",70,'@'); /* sonic spear */ | |
263 | return; | |
264 | ||
265 | /* ----- LEVEL 2 SPELLS ----- */ | |
266 | ||
267 | case 6: i=rnd(3)+2; p=" While the %s is entangled, you hit %d times"; | |
268 | goto ws; /* web */ | |
269 | ||
270 | case 7: if (c[STRCOUNT]==0) c[STREXTRA]+=3; /* strength */ | |
271 | c[STRCOUNT] += 150+rnd(100); return; | |
272 | ||
273 | case 8: yl = playery-5; /* enlightenment */ | |
274 | yh = playery+6; xl = playerx-15; xh = playerx+16; | |
275 | vxy(&xl,&yl); vxy(&xh,&yh); /* check bounds */ | |
276 | for (i=yl; i<=yh; i++) /* enlightenment */ | |
277 | for (j=xl; j<=xh; j++) know[j][i]=1; | |
278 | draws(xl,xh+1,yl,yh+1); return; | |
279 | ||
280 | case 9: raisehp(20+(clev<<1)); return; /* healing */ | |
281 | ||
282 | case 10: c[BLINDCOUNT]=0; return; /* cure blindness */ | |
283 | ||
284 | case 11: createmonster(makemonst(level+1)+8); return; | |
285 | ||
286 | case 12: if (rnd(11)+7 <= c[WISDOM]) direct(x,rnd(20)+20+clev," The %s believed!",0); | |
287 | else lprcat(" It didn't believe the illusions!"); | |
288 | return; | |
289 | ||
290 | case 13: /* if he has the amulet of invisibility then add more time */ | |
291 | for (j=i=0; i<26; i++) | |
292 | if (iven[i]==OAMULET) j+= 1+ivenarg[i]; | |
293 | c[INVISIBILITY] += (j<<7)+12; return; | |
294 | ||
295 | /* ----- LEVEL 3 SPELLS ----- */ | |
296 | ||
297 | case 14: godirect(x,rnd(25+clev)+25+clev," The fireball hits the %s",40,'*'); return; /* fireball */ | |
298 | ||
299 | case 15: godirect(x,rnd(25)+20+clev," Your cone of cold strikes the %s",60,'O'); /* cold */ | |
300 | return; | |
301 | ||
302 | case 16: dirpoly(x); return; /* polymorph */ | |
303 | ||
304 | case 17: c[CANCELLATION]+= 5+clev; return; /* cancellation */ | |
305 | ||
306 | case 18: c[HASTESELF]+= 7+clev; return; /* haste self */ | |
307 | ||
308 | case 19: omnidirect(x,30+rnd(10)," The %s gasps for air"); /* cloud kill */ | |
309 | return; | |
310 | ||
311 | case 20: xh = min(playerx+1,MAXX-2); yh = min(playery+1,MAXY-2); | |
312 | for (i=max(playerx-1,1); i<=xh; i++) /* vaporize rock */ | |
313 | for (j=max(playery-1,1); j<=yh; j++) | |
314 | { | |
315 | kn = &know[i][j]; pm = &mitem[i][j]; | |
316 | switch(*(p= &item[i][j])) | |
317 | { | |
318 | case OWALL: if (level < MAXLEVEL+MAXVLEVEL-1) | |
319 | *p = *kn = 0; | |
320 | break; | |
321 | ||
322 | case OSTATUE: if (c[HARDGAME]<3) | |
323 | { | |
324 | *p=OBOOK; iarg[i][j]=level; *kn=0; | |
325 | } | |
326 | break; | |
327 | ||
328 | case OTHRONE: *pm=GNOMEKING; *kn=0; *p= OTHRONE2; | |
329 | hitp[i][j]=monster[GNOMEKING].hitpoints; break; | |
330 | ||
331 | case OALTAR: *pm=DEMONPRINCE; *kn=0; | |
332 | hitp[i][j]=monster[DEMONPRINCE].hitpoints; break; | |
333 | }; | |
334 | switch(*pm) | |
335 | { | |
336 | case XORN: ifblind(i,j); hitm(i,j,200); break; /* Xorn takes damage from vpr */ | |
337 | } | |
338 | } | |
339 | return; | |
340 | ||
341 | /* ----- LEVEL 4 SPELLS ----- */ | |
342 | ||
343 | case 21: direct(x,100+clev," The %s shrivels up",0); /* dehydration */ | |
344 | return; | |
345 | ||
346 | case 22: godirect(x,rnd(25)+20+(clev<<1)," A lightning bolt hits the %s",1,'~'); /* lightning */ | |
347 | return; | |
348 | ||
349 | case 23: i=min(c[HP]-1,c[HPMAX]/2); /* drain life */ | |
350 | direct(x,i+i,"",0); c[HP] -= i; return; | |
351 | ||
352 | case 24: if (c[GLOBE]==0) c[MOREDEFENSES] += 10; | |
353 | c[GLOBE] += 200; loseint(); /* globe of invulnerability */ | |
354 | return; | |
355 | ||
356 | case 25: omnidirect(x,32+clev," The %s struggles for air in your flood!"); /* flood */ | |
357 | return; | |
358 | ||
359 | case 26: if (rnd(151)==63) { beep(); lprcat("\nYour heart stopped!\n"); nap(4000); died(270); return; } | |
360 | if (c[WISDOM]>rnd(10)+10) direct(x,2000," The %s's heart stopped",0); /* finger of death */ | |
361 | else lprcat(" It didn't work"); return; | |
362 | ||
363 | /* ----- LEVEL 5 SPELLS ----- */ | |
364 | ||
365 | case 27: c[SCAREMONST] += rnd(10)+clev; return; /* scare monster */ | |
366 | ||
367 | case 28: c[HOLDMONST] += rnd(10)+clev; return; /* hold monster */ | |
368 | ||
369 | case 29: c[TIMESTOP] += rnd(20)+(clev<<1); return; /* time stop */ | |
370 | ||
371 | case 30: tdirect(x); return; /* teleport away */ | |
372 | ||
373 | case 31: omnidirect(x,35+rnd(10)+clev," The %s cringes from the flame"); /* magic fire */ | |
374 | return; | |
375 | ||
376 | /* ----- LEVEL 6 SPELLS ----- */ | |
377 | ||
378 | case 32: if ((rnd(23)==5) && (wizard==0)) /* sphere of annihilation */ | |
379 | { | |
380 | beep(); lprcat("\nYou have been enveloped by the zone of nothingness!\n"); | |
381 | nap(4000); died(258); return; | |
382 | } | |
383 | xl=playerx; yl=playery; | |
384 | loseint(); | |
385 | i=dirsub(&xl,&yl); /* get direction of sphere */ | |
386 | newsphere(xl,yl,i,rnd(20)+11); /* make a sphere */ | |
387 | return; | |
388 | ||
389 | case 33: genmonst(); spelknow[33]=0; /* genocide */ | |
390 | loseint(); | |
391 | return; | |
392 | ||
393 | case 34: /* summon demon */ | |
394 | if (rnd(100) > 30) { direct(x,150," The demon strikes at the %s",0); return; } | |
395 | if (rnd(100) > 15) { lprcat(" Nothing seems to have happened"); return; } | |
396 | lprcat(" The demon turned on you and vanished!"); beep(); | |
397 | i=rnd(40)+30; lastnum=277; | |
398 | losehp(i); /* must say killed by a demon */ return; | |
399 | ||
400 | case 35: /* walk through walls */ | |
401 | c[WTW] += rnd(10)+5; return; | |
402 | ||
403 | case 36: /* alter reality */ | |
404 | { | |
405 | struct isave *save; /* pointer to item save structure */ | |
406 | int sc; sc=0; /* # items saved */ | |
407 | save = (struct isave *)malloc(sizeof(struct isave)*MAXX*MAXY*2); | |
408 | for (j=0; j<MAXY; j++) | |
409 | for (i=0; i<MAXX; i++) /* save all items and monsters */ | |
410 | { | |
411 | xl = item[i][j]; | |
412 | if (xl && xl!=OWALL && xl!=OANNIHILATION) | |
413 | { | |
414 | save[sc].type=0; save[sc].id=item[i][j]; | |
415 | save[sc++].arg=iarg[i][j]; | |
416 | } | |
417 | if (mitem[i][j]) | |
418 | { | |
419 | save[sc].type=1; save[sc].id=mitem[i][j]; | |
420 | save[sc++].arg=hitp[i][j]; | |
421 | } | |
422 | item[i][j]=OWALL; mitem[i][j]=0; | |
423 | if (wizard) know[i][j]=1; else know[i][j]=0; | |
424 | } | |
425 | eat(1,1); if (level==1) item[33][MAXY-1]=0; | |
426 | for (j=rnd(MAXY-2), i=1; i<MAXX-1; i++) item[i][j]=0; | |
427 | while (sc>0) /* put objects back in level */ | |
428 | { | |
429 | --sc; | |
430 | if (save[sc].type == 0) | |
431 | { | |
432 | int trys; | |
433 | for (trys=100, i=j=1; --trys>0 && item[i][j]; i=rnd(MAXX-1), j=rnd(MAXY-1)); | |
434 | if (trys) { item[i][j]=save[sc].id; iarg[i][j]=save[sc].arg; } | |
435 | } | |
436 | else | |
437 | { /* put monsters back in */ | |
438 | int trys; | |
439 | for (trys=100, i=j=1; --trys>0 && (item[i][j]==OWALL || mitem[i][j]); i=rnd(MAXX-1), j=rnd(MAXY-1)); | |
440 | if (trys) { mitem[i][j]=save[sc].id; hitp[i][j]=save[sc].arg; } | |
441 | } | |
442 | } | |
443 | loseint(); | |
444 | draws(0,MAXX,0,MAXY); if (wizard==0) spelknow[36]=0; | |
445 | free((char*)save); positionplayer(); return; | |
446 | } | |
447 | ||
448 | case 37: /* permanence */ adjtime(-99999L); spelknow[37]=0; /* forget */ | |
449 | loseint(); | |
450 | return; | |
451 | ||
452 | default: lprintf(" spell %d not available!",(long)x); beep(); return; | |
453 | }; | |
454 | } | |
455 | ||
456 | /* | |
457 | * loseint() Routine to subtract 1 from your int (intelligence) if > 3 | |
458 | * | |
459 | * No arguments and no return value | |
460 | */ | |
461 | loseint() | |
462 | { | |
463 | if (--c[INTELLIGENCE]<3) c[INTELLIGENCE]=3; | |
464 | } | |
465 | ||
466 | /* | |
467 | * isconfuse() Routine to check to see if player is confused | |
468 | * | |
469 | * This routine prints out a message saying "You can't aim your magic!" | |
470 | * returns 0 if not confused, non-zero (time remaining confused) if confused | |
471 | */ | |
472 | isconfuse() | |
473 | { | |
474 | if (c[CONFUSE]) { lprcat(" You can't aim your magic!"); beep(); } | |
475 | return(c[CONFUSE]); | |
476 | } | |
477 | ||
478 | /* | |
479 | * nospell(x,monst) Routine to return 1 if a spell doesn't affect a monster | |
480 | * int x,monst; | |
481 | * | |
482 | * Subroutine to return 1 if the spell can't affect the monster | |
483 | * otherwise returns 0 | |
484 | * Enter with the spell number in x, and the monster number in monst. | |
485 | */ | |
486 | nospell(x,monst) | |
487 | int x,monst; | |
488 | { | |
489 | register int tmp; | |
490 | if (x>=SPNUM || monst>=MAXMONST+8 || monst<0 || x<0) return(0); /* bad spell or monst */ | |
491 | if ((tmp=spelweird[monst-1][x])==0) return(0); | |
492 | cursors(); lprc('\n'); lprintf(spelmes[tmp],monster[monst].name); return(1); | |
493 | } | |
494 | ||
495 | /* | |
496 | * fullhit(xx) Function to return full damage against a monster (aka web) | |
497 | * int xx; | |
498 | * | |
499 | * Function to return hp damage to monster due to a number of full hits | |
500 | * Enter with the number of full hits being done | |
501 | */ | |
502 | fullhit(xx) | |
503 | int xx; | |
504 | { | |
505 | register int i; | |
506 | if (xx<0 || xx>20) return(0); /* fullhits are out of range */ | |
507 | if (c[LANCEDEATH]) return(10000); /* lance of death */ | |
508 | i = xx * ((c[WCLASS]>>1)+c[STRENGTH]+c[STREXTRA]-c[HARDGAME]-12+c[MOREDAM]); | |
509 | return( (i>=1) ? i : xx ); | |
510 | } | |
511 | ||
512 | /* | |
513 | * direct(spnum,dam,str,arg) Routine to direct spell damage 1 square in 1 dir | |
514 | * int spnum,dam,arg; | |
515 | * char *str; | |
516 | * | |
517 | * Routine to ask for a direction to a spell and then hit the monster | |
518 | * Enter with the spell number in spnum, the damage to be done in dam, | |
519 | * lprintf format string in str, and lprintf's argument in arg. | |
520 | * Returns no value. | |
521 | */ | |
522 | direct(spnum,dam,str,arg) | |
523 | int spnum,dam,arg; | |
524 | char *str; | |
525 | { | |
526 | int x,y; | |
527 | register int m; | |
528 | if (spnum<0 || spnum>=SPNUM || str==0) return; /* bad arguments */ | |
529 | if (isconfuse()) return; | |
530 | dirsub(&x,&y); | |
531 | m = mitem[x][y]; | |
532 | if (item[x][y]==OMIRROR) | |
533 | { | |
534 | if (spnum==3) /* sleep */ | |
535 | { | |
536 | lprcat("You fall asleep! "); beep(); | |
537 | fool: | |
538 | arg += 2; | |
539 | while (arg-- > 0) { parse2(); nap(1000); } | |
540 | return; | |
541 | } | |
542 | else if (spnum==6) /* web */ | |
543 | { | |
544 | lprcat("You get stuck in your own web! "); beep(); | |
545 | goto fool; | |
546 | } | |
547 | else | |
548 | { | |
549 | lastnum=278; | |
550 | lprintf(str,"spell caster (thats you)",(long)arg); | |
551 | beep(); losehp(dam); return; | |
552 | } | |
553 | } | |
554 | if (m==0) | |
555 | { lprcat(" There wasn't anything there!"); return; } | |
556 | ifblind(x,y); | |
557 | if (nospell(spnum,m)) { lasthx=x; lasthy=y; return; } | |
558 | lprintf(str,lastmonst,(long)arg); hitm(x,y,dam); | |
559 | } | |
560 | ||
561 | /* | |
562 | * godirect(spnum,dam,str,delay,cshow) Function to perform missile attacks | |
563 | * int spnum,dam,delay; | |
564 | * char *str,cshow; | |
565 | * | |
566 | * Function to hit in a direction from a missile weapon and have it keep | |
567 | * on going in that direction until its power is exhausted | |
568 | * Enter with the spell number in spnum, the power of the weapon in hp, | |
569 | * lprintf format string in str, the # of milliseconds to delay between | |
570 | * locations in delay, and the character to represent the weapon in cshow. | |
571 | * Returns no value. | |
572 | */ | |
573 | godirect(spnum,dam,str,delay,cshow) | |
574 | int spnum,dam,delay; | |
575 | char *str,cshow; | |
576 | { | |
577 | register char *p; | |
578 | register int x,y,m; | |
579 | int dx,dy; | |
580 | if (spnum<0 || spnum>=SPNUM || str==0 || delay<0) return; /* bad args */ | |
581 | if (isconfuse()) return; | |
582 | dirsub(&dx,&dy); x=dx; y=dy; | |
583 | dx = x-playerx; dy = y-playery; x = playerx; y = playery; | |
584 | while (dam>0) | |
585 | { | |
586 | x += dx; y += dy; | |
587 | if ((x > MAXX-1) || (y > MAXY-1) || (x < 0) || (y < 0)) | |
588 | { | |
589 | dam=0; break; /* out of bounds */ | |
590 | } | |
591 | if ((x==playerx) && (y==playery)) /* if energy hits player */ | |
592 | { | |
593 | cursors(); lprcat("\nYou are hit my your own magic!"); beep(); | |
594 | lastnum=278; losehp(dam); return; | |
595 | } | |
596 | if (c[BLINDCOUNT]==0) /* if not blind show effect */ | |
597 | { | |
598 | cursor(x+1,y+1); lprc(cshow); nap(delay); show1cell(x,y); | |
599 | } | |
600 | if ((m=mitem[x][y])) /* is there a monster there? */ | |
601 | { | |
602 | ifblind(x,y); | |
603 | if (nospell(spnum,m)) { lasthx=x; lasthy=y; return; } | |
604 | cursors(); lprc('\n'); | |
605 | lprintf(str,lastmonst); dam -= hitm(x,y,dam); | |
606 | show1cell(x,y); nap(1000); x -= dx; y -= dy; | |
607 | } | |
608 | else switch (*(p= &item[x][y])) | |
609 | { | |
610 | case OWALL: cursors(); lprc('\n'); lprintf(str,"wall"); | |
611 | if (dam>=50+c[HARDGAME]) /* enough damage? */ | |
612 | if (level<MAXLEVEL+MAXVLEVEL-1) /* not on V3 */ | |
613 | if ((x<MAXX-1) && (y<MAXY-1) && (x) && (y)) | |
614 | { | |
615 | lprcat(" The wall crumbles"); | |
616 | god3: *p=0; | |
617 | god: know[x][y]=0; | |
618 | show1cell(x,y); | |
619 | } | |
620 | god2: dam = 0; break; | |
621 | ||
622 | case OCLOSEDDOOR: cursors(); lprc('\n'); lprintf(str,"door"); | |
623 | if (dam>=40) | |
624 | { | |
625 | lprcat(" The door is blasted apart"); | |
626 | goto god3; | |
627 | } | |
628 | goto god2; | |
629 | ||
630 | case OSTATUE: cursors(); lprc('\n'); lprintf(str,"statue"); | |
631 | if (c[HARDGAME]<3) | |
632 | if (dam>44) | |
633 | { | |
634 | lprcat(" The statue crumbles"); | |
635 | *p=OBOOK; iarg[x][y]=level; | |
636 | goto god; | |
637 | } | |
638 | goto god2; | |
639 | ||
640 | case OTHRONE: cursors(); lprc('\n'); lprintf(str,"throne"); | |
641 | if (dam>39) | |
642 | { | |
643 | mitem[x][y]=GNOMEKING; hitp[x][y]=monster[GNOMEKING].hitpoints; | |
644 | *p = OTHRONE2; | |
645 | goto god; | |
646 | } | |
647 | goto god2; | |
648 | ||
649 | case OMIRROR: dx *= -1; dy *= -1; break; | |
650 | }; | |
651 | dam -= 3 + (c[HARDGAME]>>1); | |
652 | } | |
653 | } | |
654 | ||
655 | /* | |
656 | * ifblind(x,y) Routine to put "monster" or the monster name into lastmosnt | |
657 | * int x,y; | |
658 | * | |
659 | * Subroutine to copy the word "monster" into lastmonst if the player is blind | |
660 | * Enter with the coordinates (x,y) of the monster | |
661 | * Returns no value. | |
662 | */ | |
663 | ifblind(x,y) | |
664 | int x,y; | |
665 | { | |
666 | char *p; | |
667 | vxy(&x,&y); /* verify correct x,y coordinates */ | |
668 | if (c[BLINDCOUNT]) { lastnum=279; p="monster"; } | |
669 | else { lastnum=mitem[x][y]; p=monster[lastnum].name; } | |
670 | strcpy(lastmonst,p); | |
671 | } | |
672 | ||
673 | /* | |
674 | * tdirect(spnum) Routine to teleport away a monster | |
675 | * int spnum; | |
676 | * | |
677 | * Routine to ask for a direction to a spell and then teleport away monster | |
678 | * Enter with the spell number that wants to teleport away | |
679 | * Returns no value. | |
680 | */ | |
681 | tdirect(spnum) | |
682 | int spnum; | |
683 | { | |
684 | int x,y; | |
685 | register int m; | |
686 | if (spnum<0 || spnum>=SPNUM) return; /* bad args */ | |
687 | if (isconfuse()) return; | |
688 | dirsub(&x,&y); | |
689 | if ((m=mitem[x][y])==0) | |
690 | { lprcat(" There wasn't anything there!"); return; } | |
691 | ifblind(x,y); | |
692 | if (nospell(spnum,m)) { lasthx=x; lasthy=y; return; } | |
693 | fillmonst(m); mitem[x][y]=know[x][y]=0; | |
694 | } | |
695 | ||
696 | /* | |
697 | * omnidirect(sp,dam,str) Routine to damage all monsters 1 square from player | |
698 | * int sp,dam; | |
699 | * char *str; | |
700 | * | |
701 | * Routine to cast a spell and then hit the monster in all directions | |
702 | * Enter with the spell number in sp, the damage done to wach square in dam, | |
703 | * and the lprintf string to identify the spell in str. | |
704 | * Returns no value. | |
705 | */ | |
706 | omnidirect(spnum,dam,str) | |
707 | int spnum,dam; | |
708 | char *str; | |
709 | { | |
710 | register int x,y,m; | |
711 | if (spnum<0 || spnum>=SPNUM || str==0) return; /* bad args */ | |
712 | for (x=playerx-1; x<playerx+2; x++) | |
713 | for (y=playery-1; y<playery+2; y++) | |
714 | { | |
715 | if (m=mitem[x][y]) | |
716 | if (nospell(spnum,m) == 0) | |
717 | { | |
718 | ifblind(x,y); | |
719 | cursors(); lprc('\n'); lprintf(str,lastmonst); | |
720 | hitm(x,y,dam); nap(800); | |
721 | } | |
722 | else { lasthx=x; lasthy=y; } | |
723 | } | |
724 | } | |
725 | ||
726 | /* | |
727 | * static dirsub(x,y) Routine to ask for direction, then modify x,y for it | |
728 | * int *x,*y; | |
729 | * | |
730 | * Function to ask for a direction and modify an x,y for that direction | |
731 | * Enter with the origination coordinates in (x,y). | |
732 | * Returns index into diroffx[] (0-8). | |
733 | */ | |
734 | static int | |
735 | dirsub(x,y) | |
736 | int *x,*y; | |
737 | { | |
738 | register int i; | |
739 | lprcat("\nIn What Direction? "); | |
740 | for (i=0; ; ) | |
741 | switch(getchar()) | |
742 | { | |
743 | case 'b': i++; | |
744 | case 'n': i++; | |
745 | case 'y': i++; | |
746 | case 'u': i++; | |
747 | case 'h': i++; | |
748 | case 'k': i++; | |
749 | case 'l': i++; | |
750 | case 'j': i++; goto out; | |
751 | }; | |
752 | out: | |
753 | *x = playerx+diroffx[i]; *y = playery+diroffy[i]; | |
754 | vxy(x,y); return(i); | |
755 | } | |
756 | ||
757 | /* | |
758 | * vxy(x,y) Routine to verify/fix coordinates for being within bounds | |
759 | * int *x,*y; | |
760 | * | |
761 | * Function to verify x & y are within the bounds for a level | |
762 | * If *x or *y is not within the absolute bounds for a level, fix them so that | |
763 | * they are on the level. | |
764 | * Returns TRUE if it was out of bounds, and the *x & *y in the calling | |
765 | * routine are affected. | |
766 | */ | |
767 | vxy(x,y) | |
768 | int *x,*y; | |
769 | { | |
770 | int flag=0; | |
771 | if (*x<0) { *x=0; flag++; } | |
772 | if (*y<0) { *y=0; flag++; } | |
773 | if (*x>=MAXX) { *x=MAXX-1; flag++; } | |
774 | if (*y>=MAXY) { *y=MAXY-1; flag++; } | |
775 | return(flag); | |
776 | } | |
777 | ||
778 | /* | |
779 | * dirpoly(spnum) Routine to ask for a direction and polymorph a monst | |
780 | * int spnum; | |
781 | * | |
782 | * Subroutine to polymorph a monster and ask for the direction its in | |
783 | * Enter with the spell number in spmun. | |
784 | * Returns no value. | |
785 | */ | |
786 | dirpoly(spnum) | |
787 | int spnum; | |
788 | { | |
789 | int x,y,m; | |
790 | if (spnum<0 || spnum>=SPNUM) return; /* bad args */ | |
791 | if (isconfuse()) return; /* if he is confused, he can't aim his magic */ | |
792 | dirsub(&x,&y); | |
793 | if (mitem[x][y]==0) | |
794 | { lprcat(" There wasn't anything there!"); return; } | |
795 | ifblind(x,y); | |
796 | if (nospell(spnum,mitem[x][y])) { lasthx=x; lasthy=y; return; } | |
797 | while ( monster[m = mitem[x][y] = rnd(MAXMONST+7)].genocided ); | |
798 | hitp[x][y] = monster[m].hitpoints; | |
799 | show1cell(x,y); /* show the new monster */ | |
800 | } | |
801 | ||
802 | /* | |
803 | * hitmonster(x,y) Function to hit a monster at the designated coordinates | |
804 | * int x,y; | |
805 | * | |
806 | * This routine is used for a bash & slash type attack on a monster | |
807 | * Enter with the coordinates of the monster in (x,y). | |
808 | * Returns no value. | |
809 | */ | |
810 | hitmonster(x,y) | |
811 | int x,y; | |
812 | { | |
813 | register int tmp,monst,damag,flag; | |
814 | if (c[TIMESTOP]) return; /* not if time stopped */ | |
815 | vxy(&x,&y); /* verify coordinates are within range */ | |
816 | if ((monst = mitem[x][y]) == 0) return; | |
817 | hit3flag=1; ifblind(x,y); | |
818 | tmp = monster[monst].armorclass + c[LEVEL] + c[DEXTERITY] + c[WCLASS]/4 - 12; | |
819 | cursors(); | |
820 | if ((rnd(20) < tmp-c[HARDGAME]) || (rnd(71) < 5)) /* need at least random chance to hit */ | |
821 | { | |
822 | lprcat("\nYou hit"); flag=1; | |
823 | damag = fullhit(1); | |
824 | if (damag<9999) damag=rnd(damag)+1; | |
825 | } | |
826 | else | |
827 | { | |
828 | lprcat("\nYou missed"); flag=0; | |
829 | } | |
830 | lprcat(" the "); lprcat(lastmonst); | |
831 | if (flag) /* if the monster was hit */ | |
832 | if ((monst==RUSTMONSTER) || (monst==DISENCHANTRESS) || (monst==CUBE)) | |
833 | if (c[WIELD]>0) | |
834 | if (ivenarg[c[WIELD]] > -10) | |
835 | { | |
836 | lprintf("\nYour weapon is dulled by the %s",lastmonst); beep(); | |
837 | --ivenarg[c[WIELD]]; | |
838 | } | |
839 | if (flag) hitm(x,y,damag); | |
840 | if (monst == VAMPIRE) if (hitp[x][y]<25) { mitem[x][y]=BAT; know[x][y]=0; } | |
841 | } | |
842 | ||
843 | /* | |
844 | * hitm(x,y,amt) Function to just hit a monster at a given coordinates | |
845 | * int x,y,amt; | |
846 | * | |
847 | * Returns the number of hitpoints the monster absorbed | |
848 | * This routine is used to specifically damage a monster at a location (x,y) | |
849 | * Called by hitmonster(x,y) | |
850 | */ | |
851 | hitm(x,y,amt) | |
852 | int x,y; | |
853 | register amt; | |
854 | { | |
855 | register int monst; | |
856 | int hpoints,amt2; | |
857 | vxy(&x,&y); /* verify coordinates are within range */ | |
858 | amt2 = amt; /* save initial damage so we can return it */ | |
859 | monst = mitem[x][y]; | |
860 | if (c[HALFDAM]) amt >>= 1; /* if half damage curse adjust damage points */ | |
861 | if (amt<=0) amt2 = amt = 1; | |
862 | lasthx=x; lasthy=y; | |
863 | stealth[x][y]=1; /* make sure hitting monst breaks stealth condition */ | |
864 | c[HOLDMONST]=0; /* hit a monster breaks hold monster spell */ | |
865 | switch(monst) /* if a dragon and orb(s) of dragon slaying */ | |
866 | { | |
867 | case WHITEDRAGON: case REDDRAGON: case GREENDRAGON: | |
868 | case BRONZEDRAGON: case PLATINUMDRAGON: case SILVERDRAGON: | |
869 | amt *= 1+(c[SLAYING]<<1); break; | |
870 | } | |
871 | /* invincible monster fix is here */ | |
872 | if (hitp[x][y] > monster[monst].hitpoints) | |
873 | hitp[x][y] = monster[monst].hitpoints; | |
874 | if ((hpoints = hitp[x][y]) <= amt) | |
875 | { | |
876 | #ifdef EXTRA | |
877 | c[MONSTKILLED]++; | |
878 | #endif | |
879 | lprintf("\nThe %s died!",lastmonst); | |
880 | raiseexperience((long)monster[monst].experience); | |
881 | amt = monster[monst].gold; if (amt>0) dropgold(rnd(amt)+amt); | |
882 | dropsomething(monst); disappear(x,y); bottomline(); | |
883 | return(hpoints); | |
884 | } | |
885 | hitp[x][y] = hpoints-amt; return(amt2); | |
886 | } | |
887 | ||
888 | /* | |
889 | * hitplayer(x,y) Function for the monster to hit the player from (x,y) | |
890 | * int x,y; | |
891 | * | |
892 | * Function for the monster to hit the player with monster at location x,y | |
893 | * Returns nothing of value. | |
894 | */ | |
895 | hitplayer(x,y) | |
896 | int x,y; | |
897 | { | |
898 | register int dam,tmp,mster,bias; | |
899 | vxy(&x,&y); /* verify coordinates are within range */ | |
900 | lastnum = mster = mitem[x][y]; | |
901 | /* spirit naga's and poltergeist's do nothing if scarab of negate spirit */ | |
902 | if (c[NEGATESPIRIT] || c[SPIRITPRO]) if ((mster ==POLTERGEIST) || (mster ==SPIRITNAGA)) return; | |
903 | /* if undead and cube of undead control */ | |
904 | if (c[CUBEofUNDEAD] || c[UNDEADPRO]) if ((mster ==VAMPIRE) || (mster ==WRAITH) || (mster ==ZOMBIE)) return; | |
905 | if ((know[x][y]&1) == 0) | |
906 | { | |
907 | know[x][y]=1; show1cell(x,y); | |
908 | } | |
909 | bias = (c[HARDGAME]) + 1; | |
910 | hitflag = hit2flag = hit3flag = 1; | |
911 | yrepcount=0; | |
912 | cursors(); ifblind(x,y); | |
913 | if (c[INVISIBILITY]) if (rnd(33)<20) | |
914 | { | |
915 | lprintf("\nThe %s misses wildly",lastmonst); return; | |
916 | } | |
917 | if (c[CHARMCOUNT]) if (rnd(30)+5*monster[mster].level-c[CHARISMA]<30) | |
918 | { | |
919 | lprintf("\nThe %s is awestruck at your magnificence!",lastmonst); | |
920 | return; | |
921 | } | |
922 | if (mster==BAT) dam=1; | |
923 | else | |
924 | { | |
925 | dam = monster[mster].damage; | |
926 | dam += rnd((int)((dam<1)?1:dam)) + monster[mster].level; | |
927 | } | |
928 | tmp = 0; | |
929 | if (monster[mster].attack>0) | |
930 | if (((dam + bias + 8) > c[AC]) || (rnd((int)((c[AC]>0)?c[AC]:1))==1)) | |
931 | { if (spattack(monster[mster].attack,x,y)) { flushall(); return; } | |
932 | tmp = 1; bias -= 2; cursors(); } | |
933 | if (((dam + bias) > c[AC]) || (rnd((int)((c[AC]>0)?c[AC]:1))==1)) | |
934 | { | |
935 | lprintf("\n The %s hit you ",lastmonst); tmp = 1; | |
936 | if ((dam -= c[AC]) < 0) dam=0; | |
937 | if (dam > 0) { losehp(dam); bottomhp(); flushall(); } | |
938 | } | |
939 | if (tmp == 0) lprintf("\n The %s missed ",lastmonst); | |
940 | } | |
941 | ||
942 | /* | |
943 | * dropsomething(monst) Function to create an object when a monster dies | |
944 | * int monst; | |
945 | * | |
946 | * Function to create an object near the player when certain monsters are killed | |
947 | * Enter with the monster number | |
948 | * Returns nothing of value. | |
949 | */ | |
950 | dropsomething(monst) | |
951 | int monst; | |
952 | { | |
953 | switch(monst) | |
954 | { | |
955 | case ORC: case NYMPH: case ELF: case TROGLODYTE: | |
956 | case TROLL: case ROTHE: case VIOLETFUNGI: | |
957 | case PLATINUMDRAGON: case GNOMEKING: case REDDRAGON: | |
958 | something(level); return; | |
959 | ||
960 | case LEPRECHAUN: if (rnd(101)>=75) creategem(); | |
961 | if (rnd(5)==1) dropsomething(LEPRECHAUN); return; | |
962 | } | |
963 | } | |
964 | ||
965 | /* | |
966 | * dropgold(amount) Function to drop some gold around player | |
967 | * int amount; | |
968 | * | |
969 | * Enter with the number of gold pieces to drop | |
970 | * Returns nothing of value. | |
971 | */ | |
972 | dropgold(amount) | |
973 | register int amount; | |
974 | { | |
975 | if (amount > 250) createitem(OMAXGOLD,amount/100); else createitem(OGOLDPILE,amount); | |
976 | } | |
977 | ||
978 | /* | |
979 | * something(level) Function to create a random item around player | |
980 | * int level; | |
981 | * | |
982 | * Function to create an item from a designed probability around player | |
983 | * Enter with the cave level on which something is to be dropped | |
984 | * Returns nothing of value. | |
985 | */ | |
986 | something(level) | |
987 | int level; | |
988 | { | |
989 | register int j; | |
990 | int i; | |
991 | if (level<0 || level>MAXLEVEL+MAXVLEVEL) return; /* correct level? */ | |
992 | if (rnd(101)<8) something(level); /* possibly more than one item */ | |
993 | j = newobject(level,&i); createitem(j,i); | |
994 | } | |
995 | ||
996 | /* | |
997 | * newobject(lev,i) Routine to return a randomly selected new object | |
998 | * int lev,*i; | |
999 | * | |
1000 | * Routine to return a randomly selected object to be created | |
1001 | * Returns the object number created, and sets *i for its argument | |
1002 | * Enter with the cave level and a pointer to the items arg | |
1003 | */ | |
1004 | static char nobjtab[] = { 0, OSCROLL, OSCROLL, OSCROLL, OSCROLL, OPOTION, | |
1005 | OPOTION, OPOTION, OPOTION, OGOLDPILE, OGOLDPILE, OGOLDPILE, OGOLDPILE, | |
1006 | OBOOK, OBOOK, OBOOK, OBOOK, ODAGGER, ODAGGER, ODAGGER, OLEATHER, OLEATHER, | |
1007 | OLEATHER, OREGENRING, OPROTRING, OENERGYRING, ODEXRING, OSTRRING, OSPEAR, | |
1008 | OBELT, ORING, OSTUDLEATHER, OSHIELD, OFLAIL, OCHAIN, O2SWORD, OPLATE, | |
1009 | OLONGSWORD }; | |
1010 | ||
1011 | newobject(lev,i) | |
1012 | register int lev,*i; | |
1013 | { | |
1014 | register int tmp=32,j; | |
1015 | if (level<0 || level>MAXLEVEL+MAXVLEVEL) return(0); /* correct level? */ | |
1016 | if (lev>6) tmp=37; else if (lev>4) tmp=35; | |
1017 | j = nobjtab[tmp=rnd(tmp)]; /* the object type */ | |
1018 | switch(tmp) | |
1019 | { | |
1020 | case 1: case 2: case 3: case 4: *i=newscroll(); break; | |
1021 | case 5: case 6: case 7: case 8: *i=newpotion(); break; | |
1022 | case 9: case 10: case 11: case 12: *i=rnd((lev+1)*10)+lev*10+10; break; | |
1023 | case 13: case 14: case 15: case 16: *i=lev; break; | |
1024 | case 17: case 18: case 19: if (!(*i=newdagger())) return(0); break; | |
1025 | case 20: case 21: case 22: if (!(*i=newleather())) return(0); break; | |
1026 | case 23: case 32: case 35: *i=rund(lev/3+1); break; | |
1027 | case 24: case 26: *i=rnd(lev/4+1); break; | |
1028 | case 25: *i=rund(lev/4+1); break; | |
1029 | case 27: *i=rnd(lev/2+1); break; | |
1030 | case 30: case 33: *i=rund(lev/2+1); break; | |
1031 | case 28: *i=rund(lev/3+1); if (*i==0) return(0); break; | |
1032 | case 29: case 31: *i=rund(lev/2+1); if (*i==0) return(0); break; | |
1033 | case 34: *i=newchain(); break; | |
1034 | case 36: *i=newplate(); break; | |
1035 | case 37: *i=newsword(); break; | |
1036 | } | |
1037 | return(j); | |
1038 | } | |
1039 | ||
1040 | /* | |
1041 | * spattack(atckno,xx,yy) Function to process special attacks from monsters | |
1042 | * int atckno,xx,yy; | |
1043 | * | |
1044 | * Enter with the special attack number, and the coordinates (xx,yy) | |
1045 | * of the monster that is special attacking | |
1046 | * Returns 1 if must do a show1cell(xx,yy) upon return, 0 otherwise | |
1047 | * | |
1048 | * atckno monster effect | |
1049 | * --------------------------------------------------- | |
1050 | * 0 none | |
1051 | * 1 rust monster eat armor | |
1052 | * 2 hell hound breathe light fire | |
1053 | * 3 dragon breathe fire | |
1054 | * 4 giant centipede weakening sing | |
1055 | * 5 white dragon cold breath | |
1056 | * 6 wraith drain level | |
1057 | * 7 waterlord water gusher | |
1058 | * 8 leprechaun steal gold | |
1059 | * 9 disenchantress disenchant weapon or armor | |
1060 | * 10 ice lizard hits with barbed tail | |
1061 | * 11 umber hulk confusion | |
1062 | * 12 spirit naga cast spells taken from special attacks | |
1063 | * 13 platinum dragon psionics | |
1064 | * 14 nymph steal objects | |
1065 | * 15 bugbear bite | |
1066 | * 16 osequip bite | |
1067 | * | |
1068 | * char rustarm[ARMORTYPES][2]; | |
1069 | * special array for maximum rust damage to armor from rustmonster | |
1070 | * format is: { armor type , minimum attribute | |
1071 | */ | |
1072 | #define ARMORTYPES 6 | |
1073 | static char rustarm[ARMORTYPES][2] = { OSTUDLEATHER,-2, ORING,-4, OCHAIN,-5, | |
1074 | OSPLINT,-6, OPLATE,-8, OPLATEARMOR,-9 }; | |
1075 | static char spsel[] = { 1, 2, 3, 5, 6, 8, 9, 11, 13, 14 }; | |
1076 | spattack(x,xx,yy) | |
1077 | int x,xx,yy; | |
1078 | { | |
1079 | register int i,j=0,k,m; | |
1080 | register char *p=0; | |
1081 | if (c[CANCELLATION]) return(0); | |
1082 | vxy(&xx,&yy); /* verify x & y coordinates */ | |
1083 | switch(x) | |
1084 | { | |
1085 | case 1: /* rust your armor, j=1 when rusting has occurred */ | |
1086 | m = k = c[WEAR]; | |
1087 | if ((i=c[SHIELD]) != -1) | |
1088 | if (--ivenarg[i] < -1) ivenarg[i]= -1; else j=1; | |
1089 | if ((j==0) && (k != -1)) | |
1090 | { | |
1091 | m = iven[k]; | |
1092 | for (i=0; i<ARMORTYPES; i++) | |
1093 | if (m == rustarm[i][0]) /* find his armor in table */ | |
1094 | { | |
1095 | if (--ivenarg[k]< rustarm[i][1]) | |
1096 | ivenarg[k]= rustarm[i][1]; else j=1; | |
1097 | break; | |
1098 | } | |
1099 | } | |
1100 | if (j==0) /* if rusting did not occur */ | |
1101 | switch(m) | |
1102 | { | |
1103 | case OLEATHER: p = "\nThe %s hit you -- Your lucky you have leather on"; | |
1104 | break; | |
1105 | case OSSPLATE: p = "\nThe %s hit you -- Your fortunate to have stainless steel armor!"; | |
1106 | break; | |
1107 | } | |
1108 | else { beep(); p = "\nThe %s hit you -- your armor feels weaker"; } | |
1109 | break; | |
1110 | ||
1111 | case 2: i = rnd(15)+8-c[AC]; | |
1112 | spout: p="\nThe %s breathes fire at you!"; | |
1113 | if (c[FIRERESISTANCE]) | |
1114 | p="\nThe %s's flame doesn't phase you!"; | |
1115 | else | |
1116 | spout2: if (p) { lprintf(p,lastmonst); beep(); } | |
1117 | checkloss(i); | |
1118 | return(0); | |
1119 | ||
1120 | case 3: i = rnd(20)+25-c[AC]; goto spout; | |
1121 | ||
1122 | case 4: if (c[STRENGTH]>3) | |
1123 | { | |
1124 | p="\nThe %s stung you! You feel weaker"; beep(); | |
1125 | --c[STRENGTH]; | |
1126 | } | |
1127 | else p="\nThe %s stung you!"; | |
1128 | break; | |
1129 | ||
1130 | case 5: p="\nThe %s blasts you with his cold breath"; | |
1131 | i = rnd(15)+18-c[AC]; goto spout2; | |
1132 | ||
1133 | case 6: lprintf("\nThe %s drains you of your life energy!",lastmonst); | |
1134 | loselevel(); beep(); return(0); | |
1135 | ||
1136 | case 7: p="\nThe %s got you with a gusher!"; | |
1137 | i = rnd(15)+25-c[AC]; goto spout2; | |
1138 | ||
1139 | case 8: if (c[NOTHEFT]) return(0); /* he has a device of no theft */ | |
1140 | if (c[GOLD]) | |
1141 | { | |
1142 | p="\nThe %s hit you -- Your purse feels lighter"; | |
1143 | if (c[GOLD]>32767) c[GOLD]>>=1; | |
1144 | else c[GOLD] -= rnd((int)(1+(c[GOLD]>>1))); | |
1145 | if (c[GOLD] < 0) c[GOLD]=0; | |
1146 | } | |
1147 | else p="\nThe %s couldn't find any gold to steal"; | |
1148 | lprintf(p,lastmonst); disappear(xx,yy); beep(); | |
1149 | bottomgold(); return(1); | |
1150 | ||
1151 | case 9: for(j=50; ; ) /* disenchant */ | |
1152 | { | |
1153 | i=rund(26); m=iven[i]; /* randomly select item */ | |
1154 | if (m>0 && ivenarg[i]>0 && m!=OSCROLL && m!=OPOTION) | |
1155 | { | |
1156 | if ((ivenarg[i] -= 3)<0) ivenarg[i]=0; | |
1157 | lprintf("\nThe %s hits you -- you feel a sense of loss",lastmonst); | |
1158 | srcount=0; beep(); show3(i); bottomline(); return(0); | |
1159 | } | |
1160 | if (--j<=0) | |
1161 | { | |
1162 | p="\nThe %s nearly misses"; break; | |
1163 | } | |
1164 | break; | |
1165 | } | |
1166 | break; | |
1167 | ||
1168 | case 10: p="\nThe %s hit you with his barbed tail"; | |
1169 | i = rnd(25)-c[AC]; goto spout2; | |
1170 | ||
1171 | case 11: p="\nThe %s has confused you"; beep(); | |
1172 | c[CONFUSE]+= 10+rnd(10); break; | |
1173 | ||
1174 | case 12: /* performs any number of other special attacks */ | |
1175 | return(spattack(spsel[rund(10)],xx,yy)); | |
1176 | ||
1177 | case 13: p="\nThe %s flattens you with his psionics!"; | |
1178 | i = rnd(15)+30-c[AC]; goto spout2; | |
1179 | ||
1180 | case 14: if (c[NOTHEFT]) return(0); /* he has device of no theft */ | |
1181 | if (emptyhanded()==1) | |
1182 | { | |
1183 | p="\nThe %s couldn't find anything to steal"; | |
1184 | break; | |
1185 | } | |
1186 | lprintf("\nThe %s picks your pocket and takes:",lastmonst); | |
1187 | beep(); | |
1188 | if (stealsomething()==0) lprcat(" nothing"); disappear(xx,yy); | |
1189 | bottomline(); return(1); | |
1190 | ||
1191 | case 15: i= rnd(10)+ 5-c[AC]; | |
1192 | spout3: p="\nThe %s bit you!"; | |
1193 | goto spout2; | |
1194 | ||
1195 | case 16: i= rnd(15)+10-c[AC]; goto spout3; | |
1196 | }; | |
1197 | if (p) { lprintf(p,lastmonst); bottomline(); } | |
1198 | return(0); | |
1199 | } | |
1200 | ||
1201 | /* | |
1202 | * checkloss(x) Routine to subtract hp from user and flag bottomline display | |
1203 | * int x; | |
1204 | * | |
1205 | * Routine to subtract hitpoints from the user and flag the bottomline display | |
1206 | * Enter with the number of hit points to lose | |
1207 | * Note: if x > c[HP] this routine could kill the player! | |
1208 | */ | |
1209 | checkloss(x) | |
1210 | int x; | |
1211 | { | |
1212 | if (x>0) { losehp(x); bottomhp(); } | |
1213 | } | |
1214 | ||
1215 | /* | |
1216 | * annihilate() Routine to annihilate all monsters around player (playerx,playery) | |
1217 | * | |
1218 | * Gives player experience, but no dropped objects | |
1219 | * Returns the experience gained from all monsters killed | |
1220 | */ | |
1221 | annihilate() | |
1222 | { | |
1223 | int i,j; | |
1224 | register long k; | |
1225 | register char *p; | |
1226 | for (k=0, i=playerx-1; i<=playerx+1; i++) | |
1227 | for (j=playery-1; j<=playery+1; j++) | |
1228 | if (!vxy(&i,&j)) /* if not out of bounds */ | |
1229 | if (*(p= &mitem[i][j])) /* if a monster there */ | |
1230 | if (*p<DEMONLORD+2) | |
1231 | { | |
1232 | k += monster[*p].experience; *p=know[i][j]=0; | |
1233 | } | |
1234 | else | |
1235 | { | |
1236 | lprintf("\nThe %s barely escapes being annihilated!",monster[*p].name); | |
1237 | hitp[i][j] = (hitp[i][j]>>1) + 1; /* lose half hit points*/ | |
1238 | } | |
1239 | if (k>0) | |
1240 | { | |
1241 | lprcat("\nYou hear loud screams of agony!"); raiseexperience((long)k); | |
1242 | } | |
1243 | return(k); | |
1244 | } | |
1245 | ||
1246 | /* | |
1247 | * newsphere(x,y,dir,lifetime) Function to create a new sphere of annihilation | |
1248 | * int x,y,dir,lifetime; | |
1249 | * | |
1250 | * Enter with the coordinates of the sphere in x,y | |
1251 | * the direction (0-8 diroffx format) in dir, and the lifespan of the | |
1252 | * sphere in lifetime (in turns) | |
1253 | * Returns the number of spheres currently in existence | |
1254 | */ | |
1255 | newsphere(x,y,dir,life) | |
1256 | int x,y,dir,life; | |
1257 | { | |
1258 | int m; | |
1259 | struct sphere *sp; | |
1260 | if (((sp=(struct sphere *)malloc(sizeof(struct sphere)))) == 0) | |
1261 | return(c[SPHCAST]); /* can't malloc, therefore failure */ | |
1262 | if (dir>=9) dir=0; /* no movement if direction not found */ | |
1263 | if (level==0) vxy(&x,&y); /* don't go out of bounds */ | |
1264 | else | |
1265 | { | |
1266 | if (x<1) x=1; if (x>=MAXX-1) x=MAXX-2; | |
1267 | if (y<1) y=1; if (y>=MAXY-1) y=MAXY-2; | |
1268 | } | |
1269 | if ((m=mitem[x][y]) >= DEMONLORD+4) /* demons dispel spheres */ | |
1270 | { | |
1271 | know[x][y]=1; show1cell(x,y); /* show the demon (ha ha) */ | |
1272 | cursors(); lprintf("\nThe %s dispels the sphere!",monster[m].name); | |
1273 | beep(); rmsphere(x,y); /* remove any spheres that are here */ | |
1274 | return(c[SPHCAST]); | |
1275 | } | |
1276 | if (m==DISENCHANTRESS) /* disenchantress cancels spheres */ | |
1277 | { | |
1278 | cursors(); lprintf("\nThe %s causes cancellation of the sphere!",monster[m].name); beep(); | |
1279 | boom: sphboom(x,y); /* blow up stuff around sphere */ | |
1280 | rmsphere(x,y); /* remove any spheres that are here */ | |
1281 | return(c[SPHCAST]); | |
1282 | } | |
1283 | if (c[CANCELLATION]) /* cancellation cancels spheres */ | |
1284 | { | |
1285 | cursors(); lprcat("\nAs the cancellation takes effect, you hear a great earth shaking blast!"); beep(); | |
1286 | goto boom; | |
1287 | } | |
1288 | if (item[x][y]==OANNIHILATION) /* collision of spheres detonates spheres */ | |
1289 | { | |
1290 | cursors(); lprcat("\nTwo spheres of annihilation collide! You hear a great earth shaking blast!"); beep(); | |
1291 | rmsphere(x,y); | |
1292 | goto boom; | |
1293 | } | |
1294 | if (playerx==x && playery==y) /* collision of sphere and player! */ | |
1295 | { | |
1296 | cursors(); | |
1297 | lprcat("\nYou have been enveloped by the zone of nothingness!\n"); | |
1298 | beep(); rmsphere(x,y); /* remove any spheres that are here */ | |
1299 | nap(4000); died(258); | |
1300 | } | |
1301 | item[x][y]=OANNIHILATION; mitem[x][y]=0; know[x][y]=1; | |
1302 | show1cell(x,y); /* show the new sphere */ | |
1303 | sp->x=x; sp->y=y; sp->lev=level; sp->dir=dir; sp->lifetime=life; sp->p=0; | |
1304 | if (spheres==0) spheres=sp; /* if first node in the sphere list */ | |
1305 | else /* add sphere to beginning of linked list */ | |
1306 | { | |
1307 | sp->p = spheres; spheres = sp; | |
1308 | } | |
1309 | return(++c[SPHCAST]); /* one more sphere in the world */ | |
1310 | } | |
1311 | ||
1312 | /* | |
1313 | * rmsphere(x,y) Function to delete a sphere of annihilation from list | |
1314 | * int x,y; | |
1315 | * | |
1316 | * Enter with the coordinates of the sphere (on current level) | |
1317 | * Returns the number of spheres currently in existence | |
1318 | */ | |
1319 | rmsphere(x,y) | |
1320 | int x,y; | |
1321 | { | |
1322 | register struct sphere *sp,*sp2=0; | |
1323 | for (sp=spheres; sp; sp2=sp,sp=sp->p) | |
1324 | if (level==sp->lev) /* is sphere on this level? */ | |
1325 | if ((x==sp->x) && (y==sp->y)) /* locate sphere at this location */ | |
1326 | { | |
1327 | item[x][y]=mitem[x][y]=0; know[x][y]=1; | |
1328 | show1cell(x,y); /* show the now missing sphere */ | |
1329 | --c[SPHCAST]; | |
1330 | if (sp==spheres) { sp2=sp; spheres=sp->p; free((char*)sp2); } | |
1331 | else | |
1332 | { sp2->p = sp->p; free((char*)sp); } | |
1333 | break; | |
1334 | } | |
1335 | return(c[SPHCAST]); /* return number of spheres in the world */ | |
1336 | } | |
1337 | ||
1338 | /* | |
1339 | * sphboom(x,y) Function to perform the effects of a sphere detonation | |
1340 | * int x,y; | |
1341 | * | |
1342 | * Enter with the coordinates of the blast, Returns no value | |
1343 | */ | |
1344 | sphboom(x,y) | |
1345 | int x,y; | |
1346 | { | |
1347 | register int i,j; | |
1348 | if (c[HOLDMONST]) c[HOLDMONST]=1; | |
1349 | if (c[CANCELLATION]) c[CANCELLATION]=1; | |
1350 | for (j=max(1,x-2); j<min(x+3,MAXX-1); j++) | |
1351 | for (i=max(1,y-2); i<min(y+3,MAXY-1); i++) | |
1352 | { | |
1353 | item[j][i]=mitem[j][i]=0; | |
1354 | show1cell(j,i); | |
1355 | if (playerx==j && playery==i) | |
1356 | { | |
1357 | cursors(); beep(); | |
1358 | lprcat("\nYou were too close to the sphere!"); | |
1359 | nap(3000); | |
1360 | died(283); /* player killed in explosion */ | |
1361 | } | |
1362 | } | |
1363 | } | |
1364 | ||
1365 | /* | |
1366 | * genmonst() Function to ask for monster and genocide from game | |
1367 | * | |
1368 | * This is done by setting a flag in the monster[] structure | |
1369 | */ | |
1370 | genmonst() | |
1371 | { | |
1372 | register int i,j; | |
1373 | cursors(); lprcat("\nGenocide what monster? "); | |
1374 | for (i=0; (!isalpha(i)) && (i!=' '); i=getchar()); | |
1375 | lprc(i); | |
1376 | for (j=0; j<MAXMONST; j++) /* search for the monster type */ | |
1377 | if (monstnamelist[j]==i) /* have we found it? */ | |
1378 | { | |
1379 | monster[j].genocided=1; /* genocided from game */ | |
1380 | lprintf(" There will be no more %s's",monster[j].name); | |
1381 | /* now wipe out monsters on this level */ | |
1382 | newcavelevel(level); draws(0,MAXX,0,MAXY); bot_linex(); | |
1383 | return; | |
1384 | } | |
1385 | lprcat(" You sense failure!"); | |
1386 | } | |
1387 |