2 * monster.c Larn is copyrighted 1986 by Noah Morgan.
3 * $FreeBSD: src/games/larn/monster.c,v 1.6 1999/11/16 11:47:40 marcel Exp $
4 * $DragonFly: src/games/larn/monster.c,v 1.4 2006/08/26 17:05:05 pavalos Exp $
6 * This file contains the following functions:
7 * ----------------------------------------------------------------------------
9 * createmonster(monstno) Function to create a monster next to the player
12 * int cgood(x,y,itm,monst) Function to check location for emptiness
15 * createitem(it,arg) Routine to place an item next to the player
18 * cast() Subroutine called by parse to cast a spell for the user
20 * speldamage(x) Function to perform spell functions cast by the player
23 * loseint() Routine to decrement your int (intelligence) if > 3
25 * isconfuse() Routine to check to see if player is confused
27 * nospell(x,monst) Routine to return 1 if a spell doesn't affect a monster
30 * fullhit(xx) Function to return full damage against a monst (aka web)
33 * direct(spnum,dam,str,arg) Routine to direct spell damage 1 square in 1 dir
37 * godirect(spnum,dam,str,delay,cshow) Function to perform missile attacks
38 * int spnum,dam,delay;
41 * ifblind(x,y) Routine to put "monster" or the monster name into lastmosnt
44 * tdirect(spnum) Routine to teleport away a monster
47 * omnidirect(sp,dam,str) Routine to damage all monsters 1 square from player
51 * dirsub(x,y) Routine to ask for direction, then modify x,y for it
54 * vxy(x,y) Routine to verify/fix (*x,*y) for being within bounds
57 * dirpoly(spnum) Routine to ask for a direction and polymorph a monst
60 * hitmonster(x,y) Function to hit a monster at the designated coordinates
63 * hitm(x,y,amt) Function to just hit a monster at a given coordinates
66 * hitplayer(x,y) Function for the monster to hit the player from (x,y)
69 * dropsomething(monst) Function to create an object when a monster dies
72 * dropgold(amount) Function to drop some gold around player
75 * something(level) Function to create a random item around player
78 * newobject(lev,i) Routine to return a randomly selected new object
81 * spattack(atckno,xx,yy) Function to process special attacks from monsters
84 * checkloss(x) Routine to subtract hp from user and flag bottomline display
87 * annihilate() Routine to annihilate monsters around player, playerx,playery
89 * newsphere(x,y,dir,lifetime) Function to create a new sphere of annihilation
90 * int x,y,dir,lifetime;
92 * rmsphere(x,y) Function to delete a sphere of annihilation from list
95 * sphboom(x,y) Function to perform the effects of a sphere detonation
98 * genmonst() Function to ask for monster and genocide from game
103 struct isave
/* used for altar reality */
105 char type
; /* 0=item, 1=monster */
106 char id
; /* item number or monster number */
107 short arg
; /* the type of item or hitpoints of monster */
110 static int cgood(int, int, int, int);
111 static void speldamage(int);
112 static void loseint(void);
113 static long isconfuse(void);
114 static int nospell(int, int);
115 static int fullhit(int);
116 static void direct(int, int, const char *, int);
117 static void ifblind(int, int);
118 static void tdirect(int);
119 static void omnidirect(int, int, const char *);
120 static int dirsub(int *, int *);
121 static void dirpoly(int);
122 static void dropsomething(int);
123 static int spattack(int, int, int);
124 static void sphboom(int, int);
125 static void genmonst(void);
128 * createmonster(monstno) Function to create a monster next to the player
131 * Enter with the monster number (1 to MAXMONST+8)
135 createmonster(int mon
)
138 if (mon
<1 || mon
>MAXMONST
+8) /* check for monster number out of bounds */
140 beep(); lprintf("\ncan't createmonst(%d)\n",(long)mon
); nap(3000); return;
142 while (monster
[mon
].genocided
&& mon
<MAXMONST
) mon
++; /* genocided? */
143 for (k
=rnd(8), i
= -8; i
<0; i
++,k
++) /* choose direction, then try all */
145 if (k
>8) k
=1; /* wraparound the diroff arrays */
146 x
= playerx
+ diroffx
[k
]; y
= playery
+ diroffy
[k
];
147 if (cgood(x
,y
,0,1)) /* if we can create here */
150 hitp
[x
][y
] = monster
[mon
].hitpoints
;
151 stealth
[x
][y
]=know
[x
][y
]=0;
154 case ROTHE
: case POLTERGEIST
: case VAMPIRE
: stealth
[x
][y
]=1;
162 * int cgood(x,y,itm,monst) Function to check location for emptiness
165 * Routine to return TRUE if a location does not have itm or monst there
166 * returns FALSE (0) otherwise
167 * Enter with itm or monst TRUE or FALSE if checking it
168 * Example: if itm==TRUE check for no item at this location
169 * if monst==TRUE check for no monster at this location
170 * This routine will return FALSE if at a wall or the dungeon exit on level 1
173 cgood(int x
, int y
, int itm
, int monst
)
175 if ((y
>=0) && (y
<=MAXY
-1) && (x
>=0) && (x
<=MAXX
-1)) /* within bounds? */
176 if (item
[x
][y
]!=OWALL
) /* can't make anything on walls */
177 if (itm
==0 || (item
[x
][y
]==0)) /* is it free of items? */
178 if (monst
==0 || (mitem
[x
][y
]==0)) /* is it free of monsters? */
179 if ((level
!=1) || (x
!=33) || (y
!=MAXY
-1)) /* not exit to level 1 */
185 * createitem(it,arg) Routine to place an item next to the player
188 * Enter with the item number and its argument (iven[], ivenarg[])
189 * Returns no value, thus we don't know about createitem() failures.
192 createitem(int it
, int arg
)
195 if (it
>= MAXOBJ
) return; /* no such object */
196 for (k
=rnd(8), i
= -8; i
<0; i
++,k
++) /* choose direction, then try all */
198 if (k
>8) k
=1; /* wraparound the diroff arrays */
199 x
= playerx
+ diroffx
[k
]; y
= playery
+ diroffy
[k
];
200 if (cgood(x
,y
,1,0)) /* if we can create here */
202 item
[x
][y
] = it
; know
[x
][y
]=0; iarg
[x
][y
]=arg
; return;
208 * cast() Subroutine called by parse to cast a spell for the user
210 * No arguments and no return value.
212 static const char eys
[] = "\nEnter your spell: ";
219 if (c
[SPELLS
]<=0) { lprcat("\nYou don't have any spells!"); return; }
220 lprcat(eys
); --c
[SPELLS
];
221 while ((a
=getchr())=='D')
222 { seemagic(-1); cursors(); lprcat(eys
); }
223 if (a
=='\33') goto over
; /* to escape casting a spell */
224 if ((b
=getchr())=='\33') goto over
; /* to escape casting a spell */
225 if ((d
=getchr())=='\33')
226 { over
: lprcat(aborted
); c
[SPELLS
]++; return; } /* to escape casting a spell */
230 for (lprc('\n'),j
= -1,i
=0; i
<SPNUM
; i
++) /*seq search for his spell, hash?*/
231 if ((spelcode
[i
][0]==a
) && (spelcode
[i
][1]==b
) && (spelcode
[i
][2]==d
))
233 { speldamage(i
); j
= 1; i
=SPNUM
; }
235 if (j
== -1) lprcat(" Nothing Happened ");
240 * speldamage(x) Function to perform spell functions cast by the player
243 * Enter with the spell number, returns no value.
244 * Please insure that there are 2 spaces before all messages here
253 if (x
>=SPNUM
) return; /* no such spell */
254 if (c
[TIMESTOP
]) { lprcat(" It didn't seem to work"); return; } /* not if time stopped */
256 if ((rnd(23)==7) || (rnd(18) > c
[INTELLIGENCE
]))
257 { lprcat(" It didn't work!"); return; }
258 if (clev
*3+2 < x
) { lprcat(" Nothing happens. You seem inexperienced at this"); return; }
262 /* ----- LEVEL 1 SPELLS ----- */
264 case 0: if (c
[PROTECTIONTIME
]==0) c
[MOREDEFENSES
]+=2; /* protection field +2 */
265 c
[PROTECTIONTIME
] += 250; return;
267 case 1: i
= rnd(((clev
+1)<<1)) + clev
+ 3;
268 godirect(x
,i
,(clev
>=2)?" Your missiles hit the %s":" Your missile hit the %s",100,'+'); /* magic missile */
272 case 2: if (c
[DEXCOUNT
]==0) c
[DEXTERITY
]+=3; /* dexterity */
273 c
[DEXCOUNT
] += 400; return;
276 cp
=" While the %s slept, you smashed it %d times";
277 ws
: direct(x
,fullhit(i
),cp
,i
); /* sleep */ return;
279 case 4: /* charm monster */ c
[CHARMCOUNT
] += c
[CHARISMA
]<<1; return;
281 case 5: godirect(x
,rnd(10)+15+clev
," The sound damages the %s",70,'@'); /* sonic spear */
284 /* ----- LEVEL 2 SPELLS ----- */
286 case 6: i
=rnd(3)+2; cp
=" While the %s is entangled, you hit %d times";
289 case 7: if (c
[STRCOUNT
]==0) c
[STREXTRA
]+=3; /* strength */
290 c
[STRCOUNT
] += 150+rnd(100); return;
292 case 8: yl
= playery
-5; /* enlightenment */
293 yh
= playery
+6; xl
= playerx
-15; xh
= playerx
+16;
294 vxy(&xl
,&yl
); vxy(&xh
,&yh
); /* check bounds */
295 for (i
=yl
; i
<=yh
; i
++) /* enlightenment */
296 for (j
=xl
; j
<=xh
; j
++) know
[j
][i
]=1;
297 draws(xl
,xh
+1,yl
,yh
+1); return;
299 case 9: raisehp(20+(clev
<<1)); return; /* healing */
301 case 10: c
[BLINDCOUNT
]=0; return; /* cure blindness */
303 case 11: createmonster(makemonst(level
+1)+8); return;
305 case 12: if (rnd(11)+7 <= c
[WISDOM
]) direct(x
,rnd(20)+20+clev
," The %s believed!",0);
306 else lprcat(" It didn't believe the illusions!");
309 case 13: /* if he has the amulet of invisibility then add more time */
310 for (j
=i
=0; i
<26; i
++)
311 if (iven
[i
]==OAMULET
) j
+= 1+ivenarg
[i
];
312 c
[INVISIBILITY
] += (j
<<7)+12; return;
314 /* ----- LEVEL 3 SPELLS ----- */
316 case 14: godirect(x
,rnd(25+clev
)+25+clev
," The fireball hits the %s",40,'*'); return; /* fireball */
318 case 15: godirect(x
,rnd(25)+20+clev
," Your cone of cold strikes the %s",60,'O'); /* cold */
321 case 16: dirpoly(x
); return; /* polymorph */
323 case 17: c
[CANCELLATION
]+= 5+clev
; return; /* cancellation */
325 case 18: c
[HASTESELF
]+= 7+clev
; return; /* haste self */
327 case 19: omnidirect(x
,30+rnd(10)," The %s gasps for air"); /* cloud kill */
330 case 20: xh
= min(playerx
+1,MAXX
-2); yh
= min(playery
+1,MAXY
-2);
331 for (i
=max(playerx
-1,1); i
<=xh
; i
++) /* vaporize rock */
332 for (j
=max(playery
-1,1); j
<=yh
; j
++)
334 kn
= &know
[i
][j
]; pm
= &mitem
[i
][j
];
335 switch(*(p
= &item
[i
][j
]))
337 case OWALL
: if (level
< MAXLEVEL
+MAXVLEVEL
-1)
341 case OSTATUE
: if (c
[HARDGAME
]<3)
343 *p
=OBOOK
; iarg
[i
][j
]=level
; *kn
=0;
347 case OTHRONE
: *pm
=GNOMEKING
; *kn
=0; *p
= OTHRONE2
;
348 hitp
[i
][j
]=monster
[GNOMEKING
].hitpoints
; break;
350 case OALTAR
: *pm
=DEMONPRINCE
; *kn
=0;
351 hitp
[i
][j
]=monster
[DEMONPRINCE
].hitpoints
; break;
355 case XORN
: ifblind(i
,j
); hitm(i
,j
,200); break; /* Xorn takes damage from vpr */
360 /* ----- LEVEL 4 SPELLS ----- */
362 case 21: direct(x
,100+clev
," The %s shrivels up",0); /* dehydration */
365 case 22: godirect(x
,rnd(25)+20+(clev
<<1)," A lightning bolt hits the %s",1,'~'); /* lightning */
368 case 23: i
=min(c
[HP
]-1,c
[HPMAX
]/2); /* drain life */
369 direct(x
,i
+i
,"",0); c
[HP
] -= i
; return;
371 case 24: if (c
[GLOBE
]==0) c
[MOREDEFENSES
] += 10;
372 c
[GLOBE
] += 200; loseint(); /* globe of invulnerability */
375 case 25: omnidirect(x
,32+clev
," The %s struggles for air in your flood!"); /* flood */
378 case 26: if (rnd(151)==63) { beep(); lprcat("\nYour heart stopped!\n"); nap(4000); died(270); return; }
379 if (c
[WISDOM
]>rnd(10)+10) direct(x
,2000," The %s's heart stopped",0); /* finger of death */
380 else lprcat(" It didn't work"); return;
382 /* ----- LEVEL 5 SPELLS ----- */
384 case 27: c
[SCAREMONST
] += rnd(10)+clev
; return; /* scare monster */
386 case 28: c
[HOLDMONST
] += rnd(10)+clev
; return; /* hold monster */
388 case 29: c
[TIMESTOP
] += rnd(20)+(clev
<<1); return; /* time stop */
390 case 30: tdirect(x
); return; /* teleport away */
392 case 31: omnidirect(x
,35+rnd(10)+clev
," The %s cringes from the flame"); /* magic fire */
395 /* ----- LEVEL 6 SPELLS ----- */
397 case 32: if ((rnd(23)==5) && (wizard
==0)) /* sphere of annihilation */
399 beep(); lprcat("\nYou have been enveloped by the zone of nothingness!\n");
400 nap(4000); died(258); return;
402 xl
=playerx
; yl
=playery
;
404 i
=dirsub(&xl
,&yl
); /* get direction of sphere */
405 newsphere(xl
,yl
,i
,rnd(20)+11); /* make a sphere */
408 case 33: genmonst(); spelknow
[33]=0; /* genocide */
412 case 34: /* summon demon */
413 if (rnd(100) > 30) { direct(x
,150," The demon strikes at the %s",0); return; }
414 if (rnd(100) > 15) { lprcat(" Nothing seems to have happened"); return; }
415 lprcat(" The demon turned on you and vanished!"); beep();
416 i
=rnd(40)+30; lastnum
=277;
417 losehp(i
); /* must say killed by a demon */ return;
419 case 35: /* walk through walls */
420 c
[WTW
] += rnd(10)+5; return;
422 case 36: /* alter reality */
424 struct isave
*save
; /* pointer to item save structure */
425 int sc
; sc
=0; /* # items saved */
426 save
= (struct isave
*)malloc(sizeof(struct isave
)*MAXX
*MAXY
*2);
427 for (j
=0; j
<MAXY
; j
++)
428 for (i
=0; i
<MAXX
; i
++) /* save all items and monsters */
431 if (xl
&& xl
!=OWALL
&& xl
!=OANNIHILATION
)
433 save
[sc
].type
=0; save
[sc
].id
=item
[i
][j
];
434 save
[sc
++].arg
=iarg
[i
][j
];
438 save
[sc
].type
=1; save
[sc
].id
=mitem
[i
][j
];
439 save
[sc
++].arg
=hitp
[i
][j
];
441 item
[i
][j
]=OWALL
; mitem
[i
][j
]=0;
442 if (wizard
) know
[i
][j
]=1; else know
[i
][j
]=0;
444 eat(1,1); if (level
==1) item
[33][MAXY
-1]=0;
445 for (j
=rnd(MAXY
-2), i
=1; i
<MAXX
-1; i
++) item
[i
][j
]=0;
446 while (sc
>0) /* put objects back in level */
449 if (save
[sc
].type
== 0)
452 for (trys
=100, i
=j
=1; --trys
>0 && item
[i
][j
]; i
=rnd(MAXX
-1), j
=rnd(MAXY
-1));
453 if (trys
) { item
[i
][j
]=save
[sc
].id
; iarg
[i
][j
]=save
[sc
].arg
; }
456 { /* put monsters back in */
458 for (trys
=100, i
=j
=1; --trys
>0 && (item
[i
][j
]==OWALL
|| mitem
[i
][j
]); i
=rnd(MAXX
-1), j
=rnd(MAXY
-1));
459 if (trys
) { mitem
[i
][j
]=save
[sc
].id
; hitp
[i
][j
]=save
[sc
].arg
; }
463 draws(0,MAXX
,0,MAXY
); if (wizard
==0) spelknow
[36]=0;
464 free((char*)save
); positionplayer(); return;
467 case 37: /* permanence */ larn_adjtime(-99999L); spelknow
[37]=0; /* forget */
471 default: lprintf(" spell %d not available!",(long)x
); beep(); return;
476 * loseint() Routine to subtract 1 from your int (intelligence) if > 3
478 * No arguments and no return value
483 if (--c
[INTELLIGENCE
]<3) c
[INTELLIGENCE
]=3;
487 * isconfuse() Routine to check to see if player is confused
489 * This routine prints out a message saying "You can't aim your magic!"
490 * returns 0 if not confused, non-zero (time remaining confused) if confused
495 if (c
[CONFUSE
]) { lprcat(" You can't aim your magic!"); beep(); }
500 * nospell(x,monst) Routine to return 1 if a spell doesn't affect a monster
503 * Subroutine to return 1 if the spell can't affect the monster
504 * otherwise returns 0
505 * Enter with the spell number in x, and the monster number in monst.
508 nospell(int x
, int monst
)
511 if (x
>=SPNUM
|| monst
>=MAXMONST
+8 || monst
<0 || x
<0) return(0); /* bad spell or monst */
512 if ((tmp
=spelweird
[monst
-1][x
])==0) return(0);
513 cursors(); lprc('\n'); lprintf(spelmes
[tmp
],monster
[monst
].name
); return(1);
517 * fullhit(xx) Function to return full damage against a monster (aka web)
520 * Function to return hp damage to monster due to a number of full hits
521 * Enter with the number of full hits being done
527 if (xx
<0 || xx
>20) return(0); /* fullhits are out of range */
528 if (c
[LANCEDEATH
]) return(10000); /* lance of death */
529 i
= xx
* ((c
[WCLASS
]>>1)+c
[STRENGTH
]+c
[STREXTRA
]-c
[HARDGAME
]-12+c
[MOREDAM
]);
530 return( (i
>=1) ? i
: xx
);
534 * direct(spnum,dam,str,arg) Routine to direct spell damage 1 square in 1 dir
538 * Routine to ask for a direction to a spell and then hit the monster
539 * Enter with the spell number in spnum, the damage to be done in dam,
540 * lprintf format string in str, and lprintf's argument in arg.
544 direct(int spnum
, int dam
, const char *str
, int arg
)
548 if (spnum
<0 || spnum
>=SPNUM
|| str
==0) return; /* bad arguments */
549 if (isconfuse()) return;
552 if (item
[x
][y
]==OMIRROR
)
554 if (spnum
==3) /* sleep */
556 lprcat("You fall asleep! "); beep();
559 while (arg
-- > 0) { parse2(); nap(1000); }
562 else if (spnum
==6) /* web */
564 lprcat("You get stuck in your own web! "); beep();
570 lprintf(str
,"spell caster (thats you)",(long)arg
);
571 beep(); losehp(dam
); return;
575 { lprcat(" There wasn't anything there!"); return; }
577 if (nospell(spnum
,m
)) { lasthx
=x
; lasthy
=y
; return; }
578 lprintf(str
,lastmonst
,(long)arg
); hitm(x
,y
,dam
);
582 * godirect(spnum,dam,str,delay,cshow) Function to perform missile attacks
583 * int spnum,dam,delay;
586 * Function to hit in a direction from a missile weapon and have it keep
587 * on going in that direction until its power is exhausted
588 * Enter with the spell number in spnum, the power of the weapon in hp,
589 * lprintf format string in str, the # of milliseconds to delay between
590 * locations in delay, and the character to represent the weapon in cshow.
594 godirect(int spnum
, int dam
, const char *str
, int delay
, char cshow
)
599 if (spnum
<0 || spnum
>=SPNUM
|| str
==0 || delay
<0) return; /* bad args */
600 if (isconfuse()) return;
601 dirsub(&dx
,&dy
); x
=dx
; y
=dy
;
602 dx
= x
-playerx
; dy
= y
-playery
; x
= playerx
; y
= playery
;
606 if ((x
> MAXX
-1) || (y
> MAXY
-1) || (x
< 0) || (y
< 0))
608 dam
=0; break; /* out of bounds */
610 if ((x
==playerx
) && (y
==playery
)) /* if energy hits player */
612 cursors(); lprcat("\nYou are hit my your own magic!"); beep();
613 lastnum
=278; losehp(dam
); return;
615 if (c
[BLINDCOUNT
]==0) /* if not blind show effect */
617 cursor(x
+1,y
+1); lprc(cshow
); nap(delay
); show1cell(x
,y
);
619 if ((m
=mitem
[x
][y
])) /* is there a monster there? */
622 if (nospell(spnum
,m
)) { lasthx
=x
; lasthy
=y
; return; }
623 cursors(); lprc('\n');
624 lprintf(str
,lastmonst
); dam
-= hitm(x
,y
,dam
);
625 show1cell(x
,y
); nap(1000); x
-= dx
; y
-= dy
;
627 else switch (*(p
= &item
[x
][y
]))
629 case OWALL
: cursors(); lprc('\n'); lprintf(str
,"wall");
630 if (dam
>=50+c
[HARDGAME
]) /* enough damage? */
631 if (level
<MAXLEVEL
+MAXVLEVEL
-1) /* not on V3 */
632 if ((x
<MAXX
-1) && (y
<MAXY
-1) && (x
) && (y
))
634 lprcat(" The wall crumbles");
639 god2
: dam
= 0; break;
641 case OCLOSEDDOOR
: cursors(); lprc('\n'); lprintf(str
,"door");
644 lprcat(" The door is blasted apart");
649 case OSTATUE
: cursors(); lprc('\n'); lprintf(str
,"statue");
653 lprcat(" The statue crumbles");
654 *p
=OBOOK
; iarg
[x
][y
]=level
;
659 case OTHRONE
: cursors(); lprc('\n'); lprintf(str
,"throne");
662 mitem
[x
][y
]=GNOMEKING
; hitp
[x
][y
]=monster
[GNOMEKING
].hitpoints
;
668 case OMIRROR
: dx
*= -1; dy
*= -1; break;
670 dam
-= 3 + (c
[HARDGAME
]>>1);
675 * ifblind(x,y) Routine to put "monster" or the monster name into lastmosnt
678 * Subroutine to copy the word "monster" into lastmonst if the player is blind
679 * Enter with the coordinates (x,y) of the monster
683 ifblind(int x
, int y
)
686 vxy(&x
,&y
); /* verify correct x,y coordinates */
687 if (c
[BLINDCOUNT
]) { lastnum
=279; p
="monster"; }
688 else { lastnum
=mitem
[x
][y
]; p
=monster
[lastnum
].name
; }
693 * tdirect(spnum) Routine to teleport away a monster
696 * Routine to ask for a direction to a spell and then teleport away monster
697 * Enter with the spell number that wants to teleport away
705 if (spnum
<0 || spnum
>=SPNUM
) return; /* bad args */
706 if (isconfuse()) return;
708 if ((m
=mitem
[x
][y
])==0)
709 { lprcat(" There wasn't anything there!"); return; }
711 if (nospell(spnum
,m
)) { lasthx
=x
; lasthy
=y
; return; }
712 fillmonst(m
); mitem
[x
][y
]=know
[x
][y
]=0;
716 * omnidirect(sp,dam,str) Routine to damage all monsters 1 square from player
720 * Routine to cast a spell and then hit the monster in all directions
721 * Enter with the spell number in sp, the damage done to wach square in dam,
722 * and the lprintf string to identify the spell in str.
726 omnidirect(int spnum
, int dam
, const char *str
)
729 if (spnum
<0 || spnum
>=SPNUM
|| str
==0) return; /* bad args */
730 for (x
=playerx
-1; x
<playerx
+2; x
++)
731 for (y
=playery
-1; y
<playery
+2; y
++)
735 if (nospell(spnum
,m
) == 0)
738 cursors(); lprc('\n'); lprintf(str
,lastmonst
);
739 hitm(x
,y
,dam
); nap(800);
741 else { lasthx
=x
; lasthy
=y
; }
747 * static dirsub(x,y) Routine to ask for direction, then modify x,y for it
750 * Function to ask for a direction and modify an x,y for that direction
751 * Enter with the origination coordinates in (x,y).
752 * Returns index into diroffx[] (0-8).
755 dirsub(int *x
, int *y
)
758 lprcat("\nIn What Direction? ");
769 case 'j': i
++; goto out
;
772 *x
= playerx
+diroffx
[i
]; *y
= playery
+diroffy
[i
];
777 * vxy(x,y) Routine to verify/fix coordinates for being within bounds
780 * Function to verify x & y are within the bounds for a level
781 * If *x or *y is not within the absolute bounds for a level, fix them so that
782 * they are on the level.
783 * Returns TRUE if it was out of bounds, and the *x & *y in the calling
784 * routine are affected.
790 if (*x
<0) { *x
=0; flag
++; }
791 if (*y
<0) { *y
=0; flag
++; }
792 if (*x
>=MAXX
) { *x
=MAXX
-1; flag
++; }
793 if (*y
>=MAXY
) { *y
=MAXY
-1; flag
++; }
798 * dirpoly(spnum) Routine to ask for a direction and polymorph a monst
801 * Subroutine to polymorph a monster and ask for the direction its in
802 * Enter with the spell number in spmun.
809 if (spnum
<0 || spnum
>=SPNUM
) return; /* bad args */
810 if (isconfuse()) return; /* if he is confused, he can't aim his magic */
813 { lprcat(" There wasn't anything there!"); return; }
815 if (nospell(spnum
,mitem
[x
][y
])) { lasthx
=x
; lasthy
=y
; return; }
816 while ( monster
[m
= mitem
[x
][y
] = rnd(MAXMONST
+7)].genocided
);
817 hitp
[x
][y
] = monster
[m
].hitpoints
;
818 show1cell(x
,y
); /* show the new monster */
822 * hitmonster(x,y) Function to hit a monster at the designated coordinates
825 * This routine is used for a bash & slash type attack on a monster
826 * Enter with the coordinates of the monster in (x,y).
830 hitmonster(int x
, int y
)
832 int tmp
,monst
,damag
=0,flag
;
833 if (c
[TIMESTOP
]) return; /* not if time stopped */
834 vxy(&x
,&y
); /* verify coordinates are within range */
835 if ((monst
= mitem
[x
][y
]) == 0) return;
836 hit3flag
=1; ifblind(x
,y
);
837 tmp
= monster
[monst
].armorclass
+ c
[LEVEL
] + c
[DEXTERITY
] + c
[WCLASS
]/4 - 12;
839 if ((rnd(20) < tmp
-c
[HARDGAME
]) || (rnd(71) < 5)) /* need at least random chance to hit */
841 lprcat("\nYou hit"); flag
=1;
843 if (damag
<9999) damag
=rnd(damag
)+1;
847 lprcat("\nYou missed"); flag
=0;
849 lprcat(" the "); lprcat(lastmonst
);
850 if (flag
) /* if the monster was hit */
851 if ((monst
==RUSTMONSTER
) || (monst
==DISENCHANTRESS
) || (monst
==CUBE
))
853 if (ivenarg
[c
[WIELD
]] > -10)
855 lprintf("\nYour weapon is dulled by the %s",lastmonst
); beep();
858 if (flag
) hitm(x
,y
,damag
);
859 if (monst
== VAMPIRE
) if (hitp
[x
][y
]<25) { mitem
[x
][y
]=BAT
; know
[x
][y
]=0; }
863 * hitm(x,y,amt) Function to just hit a monster at a given coordinates
866 * Returns the number of hitpoints the monster absorbed
867 * This routine is used to specifically damage a monster at a location (x,y)
868 * Called by hitmonster(x,y)
871 hitm(int x
, int y
, int amt
)
875 vxy(&x
,&y
); /* verify coordinates are within range */
876 amt2
= amt
; /* save initial damage so we can return it */
878 if (c
[HALFDAM
]) amt
>>= 1; /* if half damage curse adjust damage points */
879 if (amt
<=0) amt2
= amt
= 1;
881 stealth
[x
][y
]=1; /* make sure hitting monst breaks stealth condition */
882 c
[HOLDMONST
]=0; /* hit a monster breaks hold monster spell */
883 switch(monst
) /* if a dragon and orb(s) of dragon slaying */
885 case WHITEDRAGON
: case REDDRAGON
: case GREENDRAGON
:
886 case BRONZEDRAGON
: case PLATINUMDRAGON
: case SILVERDRAGON
:
887 amt
*= 1+(c
[SLAYING
]<<1); break;
889 /* invincible monster fix is here */
890 if (hitp
[x
][y
] > monster
[monst
].hitpoints
)
891 hitp
[x
][y
] = monster
[monst
].hitpoints
;
892 if ((hpoints
= hitp
[x
][y
]) <= amt
)
897 lprintf("\nThe %s died!",lastmonst
);
898 raiseexperience((long)monster
[monst
].experience
);
899 amt
= monster
[monst
].gold
; if (amt
>0) dropgold(rnd(amt
)+amt
);
900 dropsomething(monst
); disappear(x
,y
); bottomline();
903 hitp
[x
][y
] = hpoints
-amt
; return(amt2
);
907 * hitplayer(x,y) Function for the monster to hit the player from (x,y)
910 * Function for the monster to hit the player with monster at location x,y
911 * Returns nothing of value.
914 hitplayer(int x
, int y
)
916 int dam
,tmp
,mster
,bias
;
917 vxy(&x
,&y
); /* verify coordinates are within range */
918 lastnum
= mster
= mitem
[x
][y
];
919 /* spirit naga's and poltergeist's do nothing if scarab of negate spirit */
920 if (c
[NEGATESPIRIT
] || c
[SPIRITPRO
]) if ((mster
==POLTERGEIST
) || (mster
==SPIRITNAGA
)) return;
921 /* if undead and cube of undead control */
922 if (c
[CUBEofUNDEAD
] || c
[UNDEADPRO
]) if ((mster
==VAMPIRE
) || (mster
==WRAITH
) || (mster
==ZOMBIE
)) return;
923 if ((know
[x
][y
]&1) == 0)
925 know
[x
][y
]=1; show1cell(x
,y
);
927 bias
= (c
[HARDGAME
]) + 1;
928 hitflag
= hit2flag
= hit3flag
= 1;
930 cursors(); ifblind(x
,y
);
931 if (c
[INVISIBILITY
]) if (rnd(33)<20)
933 lprintf("\nThe %s misses wildly",lastmonst
); return;
935 if (c
[CHARMCOUNT
]) if (rnd(30)+5*monster
[mster
].level
-c
[CHARISMA
]<30)
937 lprintf("\nThe %s is awestruck at your magnificence!",lastmonst
);
940 if (mster
==BAT
) dam
=1;
943 dam
= monster
[mster
].damage
;
944 dam
+= rnd((int)((dam
<1)?1:dam
)) + monster
[mster
].level
;
947 if (monster
[mster
].attack
>0)
948 if (((dam
+ bias
+ 8) > c
[AC
]) || (rnd((int)((c
[AC
]>0)?c
[AC
]:1))==1))
949 { if (spattack(monster
[mster
].attack
,x
,y
)) { flushall(); return; }
950 tmp
= 1; bias
-= 2; cursors(); }
951 if (((dam
+ bias
) > c
[AC
]) || (rnd((int)((c
[AC
]>0)?c
[AC
]:1))==1))
953 lprintf("\n The %s hit you ",lastmonst
); tmp
= 1;
954 if ((dam
-= c
[AC
]) < 0) dam
=0;
955 if (dam
> 0) { losehp(dam
); bottomhp(); flushall(); }
957 if (tmp
== 0) lprintf("\n The %s missed ",lastmonst
);
961 * dropsomething(monst) Function to create an object when a monster dies
964 * Function to create an object near the player when certain monsters are killed
965 * Enter with the monster number
966 * Returns nothing of value.
969 dropsomething(int monst
)
973 case ORC
: case NYMPH
: case ELF
: case TROGLODYTE
:
974 case TROLL
: case ROTHE
: case VIOLETFUNGI
:
975 case PLATINUMDRAGON
: case GNOMEKING
: case REDDRAGON
:
976 something(level
); return;
978 case LEPRECHAUN
: if (rnd(101)>=75) creategem();
979 if (rnd(5)==1) dropsomething(LEPRECHAUN
); return;
984 * dropgold(amount) Function to drop some gold around player
987 * Enter with the number of gold pieces to drop
988 * Returns nothing of value.
993 if (amount
> 250) createitem(OMAXGOLD
,amount
/100); else createitem(OGOLDPILE
,amount
);
997 * something(lvl) Function to create a random item around player
1000 * Function to create an item from a designed probability around player
1001 * Enter with the cave level on which something is to be dropped
1002 * Returns nothing of value.
1009 if (lvl
<0 || lvl
>MAXLEVEL
+MAXVLEVEL
) return; /* correct level? */
1010 if (rnd(101)<8) something(lvl
); /* possibly more than one item */
1011 j
= newobject(lvl
,&i
); createitem(j
,i
);
1015 * newobject(lev,i) Routine to return a randomly selected new object
1018 * Routine to return a randomly selected object to be created
1019 * Returns the object number created, and sets *i for its argument
1020 * Enter with the cave level and a pointer to the items arg
1022 static char nobjtab
[] = { 0, OSCROLL
, OSCROLL
, OSCROLL
, OSCROLL
, OPOTION
,
1023 OPOTION
, OPOTION
, OPOTION
, OGOLDPILE
, OGOLDPILE
, OGOLDPILE
, OGOLDPILE
,
1024 OBOOK
, OBOOK
, OBOOK
, OBOOK
, ODAGGER
, ODAGGER
, ODAGGER
, OLEATHER
, OLEATHER
,
1025 OLEATHER
, OREGENRING
, OPROTRING
, OENERGYRING
, ODEXRING
, OSTRRING
, OSPEAR
,
1026 OBELT
, ORING
, OSTUDLEATHER
, OSHIELD
, OFLAIL
, OCHAIN
, O2SWORD
, OPLATE
,
1030 newobject(int lev
, int *i
)
1033 if (level
<0 || level
>MAXLEVEL
+MAXVLEVEL
) return(0); /* correct level? */
1034 if (lev
>6) tmp
=37; else if (lev
>4) tmp
=35;
1035 j
= nobjtab
[tmp
=rnd(tmp
)]; /* the object type */
1038 case 1: case 2: case 3: case 4: *i
=newscroll(); break;
1039 case 5: case 6: case 7: case 8: *i
=newpotion(); break;
1040 case 9: case 10: case 11: case 12: *i
=rnd((lev
+1)*10)+lev
*10+10; break;
1041 case 13: case 14: case 15: case 16: *i
=lev
; break;
1042 case 17: case 18: case 19: if (!(*i
=newdagger())) return(0); break;
1043 case 20: case 21: case 22: if (!(*i
=newleather())) return(0); break;
1044 case 23: case 32: case 35: *i
=rund(lev
/3+1); break;
1045 case 24: case 26: *i
=rnd(lev
/4+1); break;
1046 case 25: *i
=rund(lev
/4+1); break;
1047 case 27: *i
=rnd(lev
/2+1); break;
1048 case 30: case 33: *i
=rund(lev
/2+1); break;
1049 case 28: *i
=rund(lev
/3+1); if (*i
==0) return(0); break;
1050 case 29: case 31: *i
=rund(lev
/2+1); if (*i
==0) return(0); break;
1051 case 34: *i
=newchain(); break;
1052 case 36: *i
=newplate(); break;
1053 case 37: *i
=newsword(); break;
1059 * spattack(atckno,xx,yy) Function to process special attacks from monsters
1062 * Enter with the special attack number, and the coordinates (xx,yy)
1063 * of the monster that is special attacking
1064 * Returns 1 if must do a show1cell(xx,yy) upon return, 0 otherwise
1066 * atckno monster effect
1067 * ---------------------------------------------------
1069 * 1 rust monster eat armor
1070 * 2 hell hound breathe light fire
1071 * 3 dragon breathe fire
1072 * 4 giant centipede weakening sing
1073 * 5 white dragon cold breath
1074 * 6 wraith drain level
1075 * 7 waterlord water gusher
1076 * 8 leprechaun steal gold
1077 * 9 disenchantress disenchant weapon or armor
1078 * 10 ice lizard hits with barbed tail
1079 * 11 umber hulk confusion
1080 * 12 spirit naga cast spells taken from special attacks
1081 * 13 platinum dragon psionics
1082 * 14 nymph steal objects
1086 * char rustarm[ARMORTYPES][2];
1087 * special array for maximum rust damage to armor from rustmonster
1088 * format is: { armor type , minimum attribute
1090 #define ARMORTYPES 6
1091 static char rustarm
[ARMORTYPES
][2] = {
1092 { OSTUDLEATHER
, -2 }, { ORING
, -4 }, { OCHAIN
, -5 },
1093 { OSPLINT
, -6}, { OPLATE
, -8}, { OPLATEARMOR
, -9 }
1095 static char spsel
[] = { 1, 2, 3, 5, 6, 8, 9, 11, 13, 14 };
1098 spattack(int x
, int xx
, int yy
)
1102 if (c
[CANCELLATION
]) return(0);
1103 vxy(&xx
,&yy
); /* verify x & y coordinates */
1106 case 1: /* rust your armor, j=1 when rusting has occurred */
1108 if ((i
=c
[SHIELD
]) != -1)
1110 if (--ivenarg
[i
] < -1) ivenarg
[i
]= -1; else j
=1;
1112 if ((j
==0) && (k
!= -1))
1115 for (i
=0; i
<ARMORTYPES
; i
++)
1116 if (m
== rustarm
[i
][0]) /* find his armor in table */
1118 if (--ivenarg
[k
]< rustarm
[i
][1])
1119 ivenarg
[k
]= rustarm
[i
][1]; else j
=1;
1123 if (j
==0) /* if rusting did not occur */
1126 case OLEATHER
: p
= "\nThe %s hit you -- Your lucky you have leather on";
1128 case OSSPLATE
: p
= "\nThe %s hit you -- Your fortunate to have stainless steel armor!";
1131 else { beep(); p
= "\nThe %s hit you -- your armor feels weaker"; }
1134 case 2: i
= rnd(15)+8-c
[AC
];
1135 spout
: p
="\nThe %s breathes fire at you!";
1136 if (c
[FIRERESISTANCE
])
1137 p
="\nThe %s's flame doesn't phase you!";
1139 spout2
: if (p
) { lprintf(p
,lastmonst
); beep(); }
1143 case 3: i
= rnd(20)+25-c
[AC
]; goto spout
;
1145 case 4: if (c
[STRENGTH
]>3)
1147 p
="\nThe %s stung you! You feel weaker"; beep();
1150 else p
="\nThe %s stung you!";
1153 case 5: p
="\nThe %s blasts you with his cold breath";
1154 i
= rnd(15)+18-c
[AC
]; goto spout2
;
1156 case 6: lprintf("\nThe %s drains you of your life energy!",lastmonst
);
1157 loselevel(); beep(); return(0);
1159 case 7: p
="\nThe %s got you with a gusher!";
1160 i
= rnd(15)+25-c
[AC
]; goto spout2
;
1162 case 8: if (c
[NOTHEFT
]) return(0); /* he has a device of no theft */
1165 p
="\nThe %s hit you -- Your purse feels lighter";
1166 if (c
[GOLD
]>32767) c
[GOLD
]>>=1;
1167 else c
[GOLD
] -= rnd((int)(1+(c
[GOLD
]>>1)));
1168 if (c
[GOLD
] < 0) c
[GOLD
]=0;
1170 else p
="\nThe %s couldn't find any gold to steal";
1171 lprintf(p
,lastmonst
); disappear(xx
,yy
); beep();
1172 bottomgold(); return(1);
1174 case 9: for(j
=50; ; ) /* disenchant */
1176 i
=rund(26); m
=iven
[i
]; /* randomly select item */
1177 if (m
>0 && ivenarg
[i
]>0 && m
!=OSCROLL
&& m
!=OPOTION
)
1179 if ((ivenarg
[i
] -= 3)<0) ivenarg
[i
]=0;
1180 lprintf("\nThe %s hits you -- you feel a sense of loss",lastmonst
);
1181 srcount
=0; beep(); show3(i
); bottomline(); return(0);
1185 p
="\nThe %s nearly misses"; break;
1191 case 10: p
="\nThe %s hit you with his barbed tail";
1192 i
= rnd(25)-c
[AC
]; goto spout2
;
1194 case 11: p
="\nThe %s has confused you"; beep();
1195 c
[CONFUSE
]+= 10+rnd(10); break;
1197 case 12: /* performs any number of other special attacks */
1198 return(spattack(spsel
[rund(10)],xx
,yy
));
1200 case 13: p
="\nThe %s flattens you with his psionics!";
1201 i
= rnd(15)+30-c
[AC
]; goto spout2
;
1203 case 14: if (c
[NOTHEFT
]) return(0); /* he has device of no theft */
1204 if (emptyhanded()==1)
1206 p
="\nThe %s couldn't find anything to steal";
1209 lprintf("\nThe %s picks your pocket and takes:",lastmonst
);
1211 if (stealsomething()==0) lprcat(" nothing"); disappear(xx
,yy
);
1212 bottomline(); return(1);
1214 case 15: i
= rnd(10)+ 5-c
[AC
];
1215 spout3
: p
="\nThe %s bit you!";
1218 case 16: i
= rnd(15)+10-c
[AC
]; goto spout3
;
1220 if (p
) { lprintf(p
,lastmonst
); bottomline(); }
1225 * checkloss(x) Routine to subtract hp from user and flag bottomline display
1228 * Routine to subtract hitpoints from the user and flag the bottomline display
1229 * Enter with the number of hit points to lose
1230 * Note: if x > c[HP] this routine could kill the player!
1235 if (x
>0) { losehp(x
); bottomhp(); }
1239 * annihilate() Routine to annihilate all monsters around player (playerx,playery)
1241 * Gives player experience, but no dropped objects
1242 * Returns the experience gained from all monsters killed
1250 for (k
=0, i
=playerx
-1; i
<=playerx
+1; i
++)
1251 for (j
=playery
-1; j
<=playery
+1; j
++)
1252 if (!vxy(&i
,&j
)) /* if not out of bounds */
1254 if (*(p
= &mitem
[i
][j
])) /* if a monster there */
1258 k
+= monster
[(int)*p
].experience
; *p
=know
[i
][j
]=0;
1262 lprintf("\nThe %s barely escapes being annihilated!",monster
[(int)*p
].name
);
1263 hitp
[i
][j
] = (hitp
[i
][j
]>>1) + 1; /* lose half hit points*/
1269 lprcat("\nYou hear loud screams of agony!"); raiseexperience((long)k
);
1275 * newsphere(x,y,dir,lifetime) Function to create a new sphere of annihilation
1276 * int x,y,dir,lifetime;
1278 * Enter with the coordinates of the sphere in x,y
1279 * the direction (0-8 diroffx format) in dir, and the lifespan of the
1280 * sphere in lifetime (in turns)
1281 * Returns the number of spheres currently in existence
1284 newsphere(int x
, int y
, int dir
, int life
)
1288 if (((sp
=(struct sphere
*)malloc(sizeof(struct sphere
)))) == 0)
1289 return(c
[SPHCAST
]); /* can't malloc, therefore failure */
1290 if (dir
>=9) dir
=0; /* no movement if direction not found */
1291 if (level
==0) vxy(&x
,&y
); /* don't go out of bounds */
1294 if (x
<1) x
=1; if (x
>=MAXX
-1) x
=MAXX
-2;
1295 if (y
<1) y
=1; if (y
>=MAXY
-1) y
=MAXY
-2;
1297 if ((m
=mitem
[x
][y
]) >= DEMONLORD
+4) /* demons dispel spheres */
1299 know
[x
][y
]=1; show1cell(x
,y
); /* show the demon (ha ha) */
1300 cursors(); lprintf("\nThe %s dispels the sphere!",monster
[m
].name
);
1301 beep(); rmsphere(x
,y
); /* remove any spheres that are here */
1304 if (m
==DISENCHANTRESS
) /* disenchantress cancels spheres */
1306 cursors(); lprintf("\nThe %s causes cancellation of the sphere!",monster
[m
].name
); beep();
1307 boom
: sphboom(x
,y
); /* blow up stuff around sphere */
1308 rmsphere(x
,y
); /* remove any spheres that are here */
1311 if (c
[CANCELLATION
]) /* cancellation cancels spheres */
1313 cursors(); lprcat("\nAs the cancellation takes effect, you hear a great earth shaking blast!"); beep();
1316 if (item
[x
][y
]==OANNIHILATION
) /* collision of spheres detonates spheres */
1318 cursors(); lprcat("\nTwo spheres of annihilation collide! You hear a great earth shaking blast!"); beep();
1322 if (playerx
==x
&& playery
==y
) /* collision of sphere and player! */
1325 lprcat("\nYou have been enveloped by the zone of nothingness!\n");
1326 beep(); rmsphere(x
,y
); /* remove any spheres that are here */
1327 nap(4000); died(258);
1329 item
[x
][y
]=OANNIHILATION
; mitem
[x
][y
]=0; know
[x
][y
]=1;
1330 show1cell(x
,y
); /* show the new sphere */
1331 sp
->x
=x
; sp
->y
=y
; sp
->lev
=level
; sp
->dir
=dir
; sp
->lifetime
=life
; sp
->p
=0;
1332 if (spheres
==0) spheres
=sp
; /* if first node in the sphere list */
1333 else /* add sphere to beginning of linked list */
1335 sp
->p
= spheres
; spheres
= sp
;
1337 return(++c
[SPHCAST
]); /* one more sphere in the world */
1341 * rmsphere(x,y) Function to delete a sphere of annihilation from list
1344 * Enter with the coordinates of the sphere (on current level)
1345 * Returns the number of spheres currently in existence
1348 rmsphere(int x
, int y
)
1350 struct sphere
*sp
,*sp2
=0;
1351 for (sp
=spheres
; sp
; sp2
=sp
,sp
=sp
->p
)
1352 if (level
==sp
->lev
) /* is sphere on this level? */
1353 if ((x
==sp
->x
) && (y
==sp
->y
)) /* locate sphere at this location */
1355 item
[x
][y
]=mitem
[x
][y
]=0; know
[x
][y
]=1;
1356 show1cell(x
,y
); /* show the now missing sphere */
1358 if (sp
==spheres
) { sp2
=sp
; spheres
=sp
->p
; free((char*)sp2
); }
1360 { sp2
->p
= sp
->p
; free((char*)sp
); }
1363 return(c
[SPHCAST
]); /* return number of spheres in the world */
1367 * sphboom(x,y) Function to perform the effects of a sphere detonation
1370 * Enter with the coordinates of the blast, Returns no value
1373 sphboom(int x
, int y
)
1376 if (c
[HOLDMONST
]) c
[HOLDMONST
]=1;
1377 if (c
[CANCELLATION
]) c
[CANCELLATION
]=1;
1378 for (j
=max(1,x
-2); j
<min(x
+3,MAXX
-1); j
++)
1379 for (i
=max(1,y
-2); i
<min(y
+3,MAXY
-1); i
++)
1381 item
[j
][i
]=mitem
[j
][i
]=0;
1383 if (playerx
==j
&& playery
==i
)
1386 lprcat("\nYou were too close to the sphere!");
1388 died(283); /* player killed in explosion */
1394 * genmonst() Function to ask for monster and genocide from game
1396 * This is done by setting a flag in the monster[] structure
1402 cursors(); lprcat("\nGenocide what monster? ");
1403 for (i
=0; (!isalpha(i
)) && (i
!=' '); i
=getchr());
1405 for (j
=0; j
<MAXMONST
; j
++) /* search for the monster type */
1406 if (monstnamelist
[j
]==i
) /* have we found it? */
1408 monster
[j
].genocided
=1; /* genocided from game */
1409 lprintf(" There will be no more %s's",monster
[j
].name
);
1410 /* now wipe out monsters on this level */
1411 newcavelevel(level
); draws(0,MAXX
,0,MAXY
); bot_linex();
1414 lprcat(" You sense failure!");