freebsd-dev/games/larn/monster.c
Jordan K. Hubbard 554eb505f8 Bring in the 4.4 Lite games directory, modulo man page changes and segregation
of the x11 based games.  I'm not going to tag the originals with bsd_44_lite
and do this in two stages since it's just not worth it for this collection,
and I've got directory renames to deal with that way.  Bleah.
Submitted by:	jkh
1994-09-04 04:03:31 +00:00

1388 lines
41 KiB
C

/*
* monster.c Larn is copyrighted 1986 by Noah Morgan.
*
* This file contains the following functions:
* ----------------------------------------------------------------------------
*
* createmonster(monstno) Function to create a monster next to the player
* int monstno;
*
* int cgood(x,y,itm,monst) Function to check location for emptiness
* int x,y,itm,monst;
*
* createitem(it,arg) Routine to place an item next to the player
* int it,arg;
*
* cast() Subroutine called by parse to cast a spell for the user
*
* speldamage(x) Function to perform spell functions cast by the player
* int x;
*
* loseint() Routine to decrement your int (intelligence) if > 3
*
* isconfuse() Routine to check to see if player is confused
*
* nospell(x,monst) Routine to return 1 if a spell doesn't affect a monster
* int x,monst;
*
* fullhit(xx) Function to return full damage against a monst (aka web)
* int xx;
*
* direct(spnum,dam,str,arg) Routine to direct spell damage 1 square in 1 dir
* int spnum,dam,arg;
* char *str;
*
* godirect(spnum,dam,str,delay,cshow) Function to perform missile attacks
* int spnum,dam,delay;
* char *str,cshow;
*
* ifblind(x,y) Routine to put "monster" or the monster name into lastmosnt
* int x,y;
*
* tdirect(spnum) Routine to teleport away a monster
* int spnum;
*
* omnidirect(sp,dam,str) Routine to damage all monsters 1 square from player
* int sp,dam;
* char *str;
*
* dirsub(x,y) Routine to ask for direction, then modify x,y for it
* int *x,*y;
*
* vxy(x,y) Routine to verify/fix (*x,*y) for being within bounds
* int *x,*y;
*
* dirpoly(spnum) Routine to ask for a direction and polymorph a monst
* int spnum;
*
* hitmonster(x,y) Function to hit a monster at the designated coordinates
* int x,y;
*
* hitm(x,y,amt) Function to just hit a monster at a given coordinates
* int x,y,amt;
*
* hitplayer(x,y) Function for the monster to hit the player from (x,y)
* int x,y;
*
* dropsomething(monst) Function to create an object when a monster dies
* int monst;
*
* dropgold(amount) Function to drop some gold around player
* int amount;
*
* something(level) Function to create a random item around player
* int level;
*
* newobject(lev,i) Routine to return a randomly selected new object
* int lev,*i;
*
* spattack(atckno,xx,yy) Function to process special attacks from monsters
* int atckno,xx,yy;
*
* checkloss(x) Routine to subtract hp from user and flag bottomline display
* int x;
*
* annihilate() Routine to annihilate monsters around player, playerx,playery
*
* newsphere(x,y,dir,lifetime) Function to create a new sphere of annihilation
* int x,y,dir,lifetime;
*
* rmsphere(x,y) Function to delete a sphere of annihilation from list
* int x,y;
*
* sphboom(x,y) Function to perform the effects of a sphere detonation
* int x,y;
*
* genmonst() Function to ask for monster and genocide from game
*
*/
#include "header.h"
struct isave /* used for altar reality */
{
char type; /* 0=item, 1=monster */
char id; /* item number or monster number */
short arg; /* the type of item or hitpoints of monster */
};
/*
* createmonster(monstno) Function to create a monster next to the player
* int monstno;
*
* Enter with the monster number (1 to MAXMONST+8)
* Returns no value.
*/
createmonster(mon)
int mon;
{
register int x,y,k,i;
if (mon<1 || mon>MAXMONST+8) /* check for monster number out of bounds */
{
beep(); lprintf("\ncan't createmonst(%d)\n",(long)mon); nap(3000); return;
}
while (monster[mon].genocided && mon<MAXMONST) mon++; /* genocided? */
for (k=rnd(8), i= -8; i<0; i++,k++) /* choose direction, then try all */
{
if (k>8) k=1; /* wraparound the diroff arrays */
x = playerx + diroffx[k]; y = playery + diroffy[k];
if (cgood(x,y,0,1)) /* if we can create here */
{
mitem[x][y] = mon;
hitp[x][y] = monster[mon].hitpoints;
stealth[x][y]=know[x][y]=0;
switch(mon)
{
case ROTHE: case POLTERGEIST: case VAMPIRE: stealth[x][y]=1;
};
return;
}
}
}
/*
* int cgood(x,y,itm,monst) Function to check location for emptiness
* int x,y,itm,monst;
*
* Routine to return TRUE if a location does not have itm or monst there
* returns FALSE (0) otherwise
* Enter with itm or monst TRUE or FALSE if checking it
* Example: if itm==TRUE check for no item at this location
* if monst==TRUE check for no monster at this location
* This routine will return FALSE if at a wall or the dungeon exit on level 1
*/
int cgood(x,y,itm,monst)
register int x,y;
int itm,monst;
{
if ((y>=0) && (y<=MAXY-1) && (x>=0) && (x<=MAXX-1)) /* within bounds? */
if (item[x][y]!=OWALL) /* can't make anything on walls */
if (itm==0 || (item[x][y]==0)) /* is it free of items? */
if (monst==0 || (mitem[x][y]==0)) /* is it free of monsters? */
if ((level!=1) || (x!=33) || (y!=MAXY-1)) /* not exit to level 1 */
return(1);
return(0);
}
/*
* createitem(it,arg) Routine to place an item next to the player
* int it,arg;
*
* Enter with the item number and its argument (iven[], ivenarg[])
* Returns no value, thus we don't know about createitem() failures.
*/
createitem(it,arg)
int it,arg;
{
register int x,y,k,i;
if (it >= MAXOBJ) return; /* no such object */
for (k=rnd(8), i= -8; i<0; i++,k++) /* choose direction, then try all */
{
if (k>8) k=1; /* wraparound the diroff arrays */
x = playerx + diroffx[k]; y = playery + diroffy[k];
if (cgood(x,y,1,0)) /* if we can create here */
{
item[x][y] = it; know[x][y]=0; iarg[x][y]=arg; return;
}
}
}
/*
* cast() Subroutine called by parse to cast a spell for the user
*
* No arguments and no return value.
*/
static char eys[] = "\nEnter your spell: ";
cast()
{
register int i,j,a,b,d;
cursors();
if (c[SPELLS]<=0) { lprcat("\nYou don't have any spells!"); return; }
lprcat(eys); --c[SPELLS];
while ((a=getchar())=='D')
{ seemagic(-1); cursors(); lprcat(eys); }
if (a=='\33') goto over; /* to escape casting a spell */
if ((b=getchar())=='\33') goto over; /* to escape casting a spell */
if ((d=getchar())=='\33')
{ over: lprcat(aborted); c[SPELLS]++; return; } /* to escape casting a spell */
#ifdef EXTRA
c[SPELLSCAST]++;
#endif
for (lprc('\n'),j= -1,i=0; i<SPNUM; i++) /*seq search for his spell, hash?*/
if ((spelcode[i][0]==a) && (spelcode[i][1]==b) && (spelcode[i][2]==d))
if (spelknow[i])
{ speldamage(i); j = 1; i=SPNUM; }
if (j == -1) lprcat(" Nothing Happened ");
bottomline();
}
static int dirsub();
/*
* speldamage(x) Function to perform spell functions cast by the player
* int x;
*
* Enter with the spell number, returns no value.
* Please insure that there are 2 spaces before all messages here
*/
speldamage(x)
int x;
{
register int i,j,clev;
int xl,xh,yl,yh;
register char *p,*kn,*pm;
if (x>=SPNUM) return; /* no such spell */
if (c[TIMESTOP]) { lprcat(" It didn't seem to work"); return; } /* not if time stopped */
clev = c[LEVEL];
if ((rnd(23)==7) || (rnd(18) > c[INTELLIGENCE]))
{ lprcat(" It didn't work!"); return; }
if (clev*3+2 < x) { lprcat(" Nothing happens. You seem inexperienced at this"); return; }
switch(x)
{
/* ----- LEVEL 1 SPELLS ----- */
case 0: if (c[PROTECTIONTIME]==0) c[MOREDEFENSES]+=2; /* protection field +2 */
c[PROTECTIONTIME] += 250; return;
case 1: i = rnd(((clev+1)<<1)) + clev + 3;
godirect(x,i,(clev>=2)?" Your missiles hit the %s":" Your missile hit the %s",100,'+'); /* magic missile */
return;
case 2: if (c[DEXCOUNT]==0) c[DEXTERITY]+=3; /* dexterity */
c[DEXCOUNT] += 400; return;
case 3: i=rnd(3)+1;
p=" While the %s slept, you smashed it %d times";
ws: direct(x,fullhit(i),p,i); /* sleep */ return;
case 4: /* charm monster */ c[CHARMCOUNT] += c[CHARISMA]<<1; return;
case 5: godirect(x,rnd(10)+15+clev," The sound damages the %s",70,'@'); /* sonic spear */
return;
/* ----- LEVEL 2 SPELLS ----- */
case 6: i=rnd(3)+2; p=" While the %s is entangled, you hit %d times";
goto ws; /* web */
case 7: if (c[STRCOUNT]==0) c[STREXTRA]+=3; /* strength */
c[STRCOUNT] += 150+rnd(100); return;
case 8: yl = playery-5; /* enlightenment */
yh = playery+6; xl = playerx-15; xh = playerx+16;
vxy(&xl,&yl); vxy(&xh,&yh); /* check bounds */
for (i=yl; i<=yh; i++) /* enlightenment */
for (j=xl; j<=xh; j++) know[j][i]=1;
draws(xl,xh+1,yl,yh+1); return;
case 9: raisehp(20+(clev<<1)); return; /* healing */
case 10: c[BLINDCOUNT]=0; return; /* cure blindness */
case 11: createmonster(makemonst(level+1)+8); return;
case 12: if (rnd(11)+7 <= c[WISDOM]) direct(x,rnd(20)+20+clev," The %s believed!",0);
else lprcat(" It didn't believe the illusions!");
return;
case 13: /* if he has the amulet of invisibility then add more time */
for (j=i=0; i<26; i++)
if (iven[i]==OAMULET) j+= 1+ivenarg[i];
c[INVISIBILITY] += (j<<7)+12; return;
/* ----- LEVEL 3 SPELLS ----- */
case 14: godirect(x,rnd(25+clev)+25+clev," The fireball hits the %s",40,'*'); return; /* fireball */
case 15: godirect(x,rnd(25)+20+clev," Your cone of cold strikes the %s",60,'O'); /* cold */
return;
case 16: dirpoly(x); return; /* polymorph */
case 17: c[CANCELLATION]+= 5+clev; return; /* cancellation */
case 18: c[HASTESELF]+= 7+clev; return; /* haste self */
case 19: omnidirect(x,30+rnd(10)," The %s gasps for air"); /* cloud kill */
return;
case 20: xh = min(playerx+1,MAXX-2); yh = min(playery+1,MAXY-2);
for (i=max(playerx-1,1); i<=xh; i++) /* vaporize rock */
for (j=max(playery-1,1); j<=yh; j++)
{
kn = &know[i][j]; pm = &mitem[i][j];
switch(*(p= &item[i][j]))
{
case OWALL: if (level < MAXLEVEL+MAXVLEVEL-1)
*p = *kn = 0;
break;
case OSTATUE: if (c[HARDGAME]<3)
{
*p=OBOOK; iarg[i][j]=level; *kn=0;
}
break;
case OTHRONE: *pm=GNOMEKING; *kn=0; *p= OTHRONE2;
hitp[i][j]=monster[GNOMEKING].hitpoints; break;
case OALTAR: *pm=DEMONPRINCE; *kn=0;
hitp[i][j]=monster[DEMONPRINCE].hitpoints; break;
};
switch(*pm)
{
case XORN: ifblind(i,j); hitm(i,j,200); break; /* Xorn takes damage from vpr */
}
}
return;
/* ----- LEVEL 4 SPELLS ----- */
case 21: direct(x,100+clev," The %s shrivels up",0); /* dehydration */
return;
case 22: godirect(x,rnd(25)+20+(clev<<1)," A lightning bolt hits the %s",1,'~'); /* lightning */
return;
case 23: i=min(c[HP]-1,c[HPMAX]/2); /* drain life */
direct(x,i+i,"",0); c[HP] -= i; return;
case 24: if (c[GLOBE]==0) c[MOREDEFENSES] += 10;
c[GLOBE] += 200; loseint(); /* globe of invulnerability */
return;
case 25: omnidirect(x,32+clev," The %s struggles for air in your flood!"); /* flood */
return;
case 26: if (rnd(151)==63) { beep(); lprcat("\nYour heart stopped!\n"); nap(4000); died(270); return; }
if (c[WISDOM]>rnd(10)+10) direct(x,2000," The %s's heart stopped",0); /* finger of death */
else lprcat(" It didn't work"); return;
/* ----- LEVEL 5 SPELLS ----- */
case 27: c[SCAREMONST] += rnd(10)+clev; return; /* scare monster */
case 28: c[HOLDMONST] += rnd(10)+clev; return; /* hold monster */
case 29: c[TIMESTOP] += rnd(20)+(clev<<1); return; /* time stop */
case 30: tdirect(x); return; /* teleport away */
case 31: omnidirect(x,35+rnd(10)+clev," The %s cringes from the flame"); /* magic fire */
return;
/* ----- LEVEL 6 SPELLS ----- */
case 32: if ((rnd(23)==5) && (wizard==0)) /* sphere of annihilation */
{
beep(); lprcat("\nYou have been enveloped by the zone of nothingness!\n");
nap(4000); died(258); return;
}
xl=playerx; yl=playery;
loseint();
i=dirsub(&xl,&yl); /* get direction of sphere */
newsphere(xl,yl,i,rnd(20)+11); /* make a sphere */
return;
case 33: genmonst(); spelknow[33]=0; /* genocide */
loseint();
return;
case 34: /* summon demon */
if (rnd(100) > 30) { direct(x,150," The demon strikes at the %s",0); return; }
if (rnd(100) > 15) { lprcat(" Nothing seems to have happened"); return; }
lprcat(" The demon turned on you and vanished!"); beep();
i=rnd(40)+30; lastnum=277;
losehp(i); /* must say killed by a demon */ return;
case 35: /* walk through walls */
c[WTW] += rnd(10)+5; return;
case 36: /* alter reality */
{
struct isave *save; /* pointer to item save structure */
int sc; sc=0; /* # items saved */
save = (struct isave *)malloc(sizeof(struct isave)*MAXX*MAXY*2);
for (j=0; j<MAXY; j++)
for (i=0; i<MAXX; i++) /* save all items and monsters */
{
xl = item[i][j];
if (xl && xl!=OWALL && xl!=OANNIHILATION)
{
save[sc].type=0; save[sc].id=item[i][j];
save[sc++].arg=iarg[i][j];
}
if (mitem[i][j])
{
save[sc].type=1; save[sc].id=mitem[i][j];
save[sc++].arg=hitp[i][j];
}
item[i][j]=OWALL; mitem[i][j]=0;
if (wizard) know[i][j]=1; else know[i][j]=0;
}
eat(1,1); if (level==1) item[33][MAXY-1]=0;
for (j=rnd(MAXY-2), i=1; i<MAXX-1; i++) item[i][j]=0;
while (sc>0) /* put objects back in level */
{
--sc;
if (save[sc].type == 0)
{
int trys;
for (trys=100, i=j=1; --trys>0 && item[i][j]; i=rnd(MAXX-1), j=rnd(MAXY-1));
if (trys) { item[i][j]=save[sc].id; iarg[i][j]=save[sc].arg; }
}
else
{ /* put monsters back in */
int trys;
for (trys=100, i=j=1; --trys>0 && (item[i][j]==OWALL || mitem[i][j]); i=rnd(MAXX-1), j=rnd(MAXY-1));
if (trys) { mitem[i][j]=save[sc].id; hitp[i][j]=save[sc].arg; }
}
}
loseint();
draws(0,MAXX,0,MAXY); if (wizard==0) spelknow[36]=0;
free((char*)save); positionplayer(); return;
}
case 37: /* permanence */ adjtime(-99999L); spelknow[37]=0; /* forget */
loseint();
return;
default: lprintf(" spell %d not available!",(long)x); beep(); return;
};
}
/*
* loseint() Routine to subtract 1 from your int (intelligence) if > 3
*
* No arguments and no return value
*/
loseint()
{
if (--c[INTELLIGENCE]<3) c[INTELLIGENCE]=3;
}
/*
* isconfuse() Routine to check to see if player is confused
*
* This routine prints out a message saying "You can't aim your magic!"
* returns 0 if not confused, non-zero (time remaining confused) if confused
*/
isconfuse()
{
if (c[CONFUSE]) { lprcat(" You can't aim your magic!"); beep(); }
return(c[CONFUSE]);
}
/*
* nospell(x,monst) Routine to return 1 if a spell doesn't affect a monster
* int x,monst;
*
* Subroutine to return 1 if the spell can't affect the monster
* otherwise returns 0
* Enter with the spell number in x, and the monster number in monst.
*/
nospell(x,monst)
int x,monst;
{
register int tmp;
if (x>=SPNUM || monst>=MAXMONST+8 || monst<0 || x<0) return(0); /* bad spell or monst */
if ((tmp=spelweird[monst-1][x])==0) return(0);
cursors(); lprc('\n'); lprintf(spelmes[tmp],monster[monst].name); return(1);
}
/*
* fullhit(xx) Function to return full damage against a monster (aka web)
* int xx;
*
* Function to return hp damage to monster due to a number of full hits
* Enter with the number of full hits being done
*/
fullhit(xx)
int xx;
{
register int i;
if (xx<0 || xx>20) return(0); /* fullhits are out of range */
if (c[LANCEDEATH]) return(10000); /* lance of death */
i = xx * ((c[WCLASS]>>1)+c[STRENGTH]+c[STREXTRA]-c[HARDGAME]-12+c[MOREDAM]);
return( (i>=1) ? i : xx );
}
/*
* direct(spnum,dam,str,arg) Routine to direct spell damage 1 square in 1 dir
* int spnum,dam,arg;
* char *str;
*
* Routine to ask for a direction to a spell and then hit the monster
* Enter with the spell number in spnum, the damage to be done in dam,
* lprintf format string in str, and lprintf's argument in arg.
* Returns no value.
*/
direct(spnum,dam,str,arg)
int spnum,dam,arg;
char *str;
{
int x,y;
register int m;
if (spnum<0 || spnum>=SPNUM || str==0) return; /* bad arguments */
if (isconfuse()) return;
dirsub(&x,&y);
m = mitem[x][y];
if (item[x][y]==OMIRROR)
{
if (spnum==3) /* sleep */
{
lprcat("You fall asleep! "); beep();
fool:
arg += 2;
while (arg-- > 0) { parse2(); nap(1000); }
return;
}
else if (spnum==6) /* web */
{
lprcat("You get stuck in your own web! "); beep();
goto fool;
}
else
{
lastnum=278;
lprintf(str,"spell caster (thats you)",(long)arg);
beep(); losehp(dam); return;
}
}
if (m==0)
{ lprcat(" There wasn't anything there!"); return; }
ifblind(x,y);
if (nospell(spnum,m)) { lasthx=x; lasthy=y; return; }
lprintf(str,lastmonst,(long)arg); hitm(x,y,dam);
}
/*
* godirect(spnum,dam,str,delay,cshow) Function to perform missile attacks
* int spnum,dam,delay;
* char *str,cshow;
*
* Function to hit in a direction from a missile weapon and have it keep
* on going in that direction until its power is exhausted
* Enter with the spell number in spnum, the power of the weapon in hp,
* lprintf format string in str, the # of milliseconds to delay between
* locations in delay, and the character to represent the weapon in cshow.
* Returns no value.
*/
godirect(spnum,dam,str,delay,cshow)
int spnum,dam,delay;
char *str,cshow;
{
register char *p;
register int x,y,m;
int dx,dy;
if (spnum<0 || spnum>=SPNUM || str==0 || delay<0) return; /* bad args */
if (isconfuse()) return;
dirsub(&dx,&dy); x=dx; y=dy;
dx = x-playerx; dy = y-playery; x = playerx; y = playery;
while (dam>0)
{
x += dx; y += dy;
if ((x > MAXX-1) || (y > MAXY-1) || (x < 0) || (y < 0))
{
dam=0; break; /* out of bounds */
}
if ((x==playerx) && (y==playery)) /* if energy hits player */
{
cursors(); lprcat("\nYou are hit my your own magic!"); beep();
lastnum=278; losehp(dam); return;
}
if (c[BLINDCOUNT]==0) /* if not blind show effect */
{
cursor(x+1,y+1); lprc(cshow); nap(delay); show1cell(x,y);
}
if ((m=mitem[x][y])) /* is there a monster there? */
{
ifblind(x,y);
if (nospell(spnum,m)) { lasthx=x; lasthy=y; return; }
cursors(); lprc('\n');
lprintf(str,lastmonst); dam -= hitm(x,y,dam);
show1cell(x,y); nap(1000); x -= dx; y -= dy;
}
else switch (*(p= &item[x][y]))
{
case OWALL: cursors(); lprc('\n'); lprintf(str,"wall");
if (dam>=50+c[HARDGAME]) /* enough damage? */
if (level<MAXLEVEL+MAXVLEVEL-1) /* not on V3 */
if ((x<MAXX-1) && (y<MAXY-1) && (x) && (y))
{
lprcat(" The wall crumbles");
god3: *p=0;
god: know[x][y]=0;
show1cell(x,y);
}
god2: dam = 0; break;
case OCLOSEDDOOR: cursors(); lprc('\n'); lprintf(str,"door");
if (dam>=40)
{
lprcat(" The door is blasted apart");
goto god3;
}
goto god2;
case OSTATUE: cursors(); lprc('\n'); lprintf(str,"statue");
if (c[HARDGAME]<3)
if (dam>44)
{
lprcat(" The statue crumbles");
*p=OBOOK; iarg[x][y]=level;
goto god;
}
goto god2;
case OTHRONE: cursors(); lprc('\n'); lprintf(str,"throne");
if (dam>39)
{
mitem[x][y]=GNOMEKING; hitp[x][y]=monster[GNOMEKING].hitpoints;
*p = OTHRONE2;
goto god;
}
goto god2;
case OMIRROR: dx *= -1; dy *= -1; break;
};
dam -= 3 + (c[HARDGAME]>>1);
}
}
/*
* ifblind(x,y) Routine to put "monster" or the monster name into lastmosnt
* int x,y;
*
* Subroutine to copy the word "monster" into lastmonst if the player is blind
* Enter with the coordinates (x,y) of the monster
* Returns no value.
*/
ifblind(x,y)
int x,y;
{
char *p;
vxy(&x,&y); /* verify correct x,y coordinates */
if (c[BLINDCOUNT]) { lastnum=279; p="monster"; }
else { lastnum=mitem[x][y]; p=monster[lastnum].name; }
strcpy(lastmonst,p);
}
/*
* tdirect(spnum) Routine to teleport away a monster
* int spnum;
*
* Routine to ask for a direction to a spell and then teleport away monster
* Enter with the spell number that wants to teleport away
* Returns no value.
*/
tdirect(spnum)
int spnum;
{
int x,y;
register int m;
if (spnum<0 || spnum>=SPNUM) return; /* bad args */
if (isconfuse()) return;
dirsub(&x,&y);
if ((m=mitem[x][y])==0)
{ lprcat(" There wasn't anything there!"); return; }
ifblind(x,y);
if (nospell(spnum,m)) { lasthx=x; lasthy=y; return; }
fillmonst(m); mitem[x][y]=know[x][y]=0;
}
/*
* omnidirect(sp,dam,str) Routine to damage all monsters 1 square from player
* int sp,dam;
* char *str;
*
* Routine to cast a spell and then hit the monster in all directions
* Enter with the spell number in sp, the damage done to wach square in dam,
* and the lprintf string to identify the spell in str.
* Returns no value.
*/
omnidirect(spnum,dam,str)
int spnum,dam;
char *str;
{
register int x,y,m;
if (spnum<0 || spnum>=SPNUM || str==0) return; /* bad args */
for (x=playerx-1; x<playerx+2; x++)
for (y=playery-1; y<playery+2; y++)
{
if (m=mitem[x][y])
if (nospell(spnum,m) == 0)
{
ifblind(x,y);
cursors(); lprc('\n'); lprintf(str,lastmonst);
hitm(x,y,dam); nap(800);
}
else { lasthx=x; lasthy=y; }
}
}
/*
* static dirsub(x,y) Routine to ask for direction, then modify x,y for it
* int *x,*y;
*
* Function to ask for a direction and modify an x,y for that direction
* Enter with the origination coordinates in (x,y).
* Returns index into diroffx[] (0-8).
*/
static int
dirsub(x,y)
int *x,*y;
{
register int i;
lprcat("\nIn What Direction? ");
for (i=0; ; )
switch(getchar())
{
case 'b': i++;
case 'n': i++;
case 'y': i++;
case 'u': i++;
case 'h': i++;
case 'k': i++;
case 'l': i++;
case 'j': i++; goto out;
};
out:
*x = playerx+diroffx[i]; *y = playery+diroffy[i];
vxy(x,y); return(i);
}
/*
* vxy(x,y) Routine to verify/fix coordinates for being within bounds
* int *x,*y;
*
* Function to verify x & y are within the bounds for a level
* If *x or *y is not within the absolute bounds for a level, fix them so that
* they are on the level.
* Returns TRUE if it was out of bounds, and the *x & *y in the calling
* routine are affected.
*/
vxy(x,y)
int *x,*y;
{
int flag=0;
if (*x<0) { *x=0; flag++; }
if (*y<0) { *y=0; flag++; }
if (*x>=MAXX) { *x=MAXX-1; flag++; }
if (*y>=MAXY) { *y=MAXY-1; flag++; }
return(flag);
}
/*
* dirpoly(spnum) Routine to ask for a direction and polymorph a monst
* int spnum;
*
* Subroutine to polymorph a monster and ask for the direction its in
* Enter with the spell number in spmun.
* Returns no value.
*/
dirpoly(spnum)
int spnum;
{
int x,y,m;
if (spnum<0 || spnum>=SPNUM) return; /* bad args */
if (isconfuse()) return; /* if he is confused, he can't aim his magic */
dirsub(&x,&y);
if (mitem[x][y]==0)
{ lprcat(" There wasn't anything there!"); return; }
ifblind(x,y);
if (nospell(spnum,mitem[x][y])) { lasthx=x; lasthy=y; return; }
while ( monster[m = mitem[x][y] = rnd(MAXMONST+7)].genocided );
hitp[x][y] = monster[m].hitpoints;
show1cell(x,y); /* show the new monster */
}
/*
* hitmonster(x,y) Function to hit a monster at the designated coordinates
* int x,y;
*
* This routine is used for a bash & slash type attack on a monster
* Enter with the coordinates of the monster in (x,y).
* Returns no value.
*/
hitmonster(x,y)
int x,y;
{
register int tmp,monst,damag,flag;
if (c[TIMESTOP]) return; /* not if time stopped */
vxy(&x,&y); /* verify coordinates are within range */
if ((monst = mitem[x][y]) == 0) return;
hit3flag=1; ifblind(x,y);
tmp = monster[monst].armorclass + c[LEVEL] + c[DEXTERITY] + c[WCLASS]/4 - 12;
cursors();
if ((rnd(20) < tmp-c[HARDGAME]) || (rnd(71) < 5)) /* need at least random chance to hit */
{
lprcat("\nYou hit"); flag=1;
damag = fullhit(1);
if (damag<9999) damag=rnd(damag)+1;
}
else
{
lprcat("\nYou missed"); flag=0;
}
lprcat(" the "); lprcat(lastmonst);
if (flag) /* if the monster was hit */
if ((monst==RUSTMONSTER) || (monst==DISENCHANTRESS) || (monst==CUBE))
if (c[WIELD]>0)
if (ivenarg[c[WIELD]] > -10)
{
lprintf("\nYour weapon is dulled by the %s",lastmonst); beep();
--ivenarg[c[WIELD]];
}
if (flag) hitm(x,y,damag);
if (monst == VAMPIRE) if (hitp[x][y]<25) { mitem[x][y]=BAT; know[x][y]=0; }
}
/*
* hitm(x,y,amt) Function to just hit a monster at a given coordinates
* int x,y,amt;
*
* Returns the number of hitpoints the monster absorbed
* This routine is used to specifically damage a monster at a location (x,y)
* Called by hitmonster(x,y)
*/
hitm(x,y,amt)
int x,y;
register amt;
{
register int monst;
int hpoints,amt2;
vxy(&x,&y); /* verify coordinates are within range */
amt2 = amt; /* save initial damage so we can return it */
monst = mitem[x][y];
if (c[HALFDAM]) amt >>= 1; /* if half damage curse adjust damage points */
if (amt<=0) amt2 = amt = 1;
lasthx=x; lasthy=y;
stealth[x][y]=1; /* make sure hitting monst breaks stealth condition */
c[HOLDMONST]=0; /* hit a monster breaks hold monster spell */
switch(monst) /* if a dragon and orb(s) of dragon slaying */
{
case WHITEDRAGON: case REDDRAGON: case GREENDRAGON:
case BRONZEDRAGON: case PLATINUMDRAGON: case SILVERDRAGON:
amt *= 1+(c[SLAYING]<<1); break;
}
/* invincible monster fix is here */
if (hitp[x][y] > monster[monst].hitpoints)
hitp[x][y] = monster[monst].hitpoints;
if ((hpoints = hitp[x][y]) <= amt)
{
#ifdef EXTRA
c[MONSTKILLED]++;
#endif
lprintf("\nThe %s died!",lastmonst);
raiseexperience((long)monster[monst].experience);
amt = monster[monst].gold; if (amt>0) dropgold(rnd(amt)+amt);
dropsomething(monst); disappear(x,y); bottomline();
return(hpoints);
}
hitp[x][y] = hpoints-amt; return(amt2);
}
/*
* hitplayer(x,y) Function for the monster to hit the player from (x,y)
* int x,y;
*
* Function for the monster to hit the player with monster at location x,y
* Returns nothing of value.
*/
hitplayer(x,y)
int x,y;
{
register int dam,tmp,mster,bias;
vxy(&x,&y); /* verify coordinates are within range */
lastnum = mster = mitem[x][y];
/* spirit naga's and poltergeist's do nothing if scarab of negate spirit */
if (c[NEGATESPIRIT] || c[SPIRITPRO]) if ((mster ==POLTERGEIST) || (mster ==SPIRITNAGA)) return;
/* if undead and cube of undead control */
if (c[CUBEofUNDEAD] || c[UNDEADPRO]) if ((mster ==VAMPIRE) || (mster ==WRAITH) || (mster ==ZOMBIE)) return;
if ((know[x][y]&1) == 0)
{
know[x][y]=1; show1cell(x,y);
}
bias = (c[HARDGAME]) + 1;
hitflag = hit2flag = hit3flag = 1;
yrepcount=0;
cursors(); ifblind(x,y);
if (c[INVISIBILITY]) if (rnd(33)<20)
{
lprintf("\nThe %s misses wildly",lastmonst); return;
}
if (c[CHARMCOUNT]) if (rnd(30)+5*monster[mster].level-c[CHARISMA]<30)
{
lprintf("\nThe %s is awestruck at your magnificence!",lastmonst);
return;
}
if (mster==BAT) dam=1;
else
{
dam = monster[mster].damage;
dam += rnd((int)((dam<1)?1:dam)) + monster[mster].level;
}
tmp = 0;
if (monster[mster].attack>0)
if (((dam + bias + 8) > c[AC]) || (rnd((int)((c[AC]>0)?c[AC]:1))==1))
{ if (spattack(monster[mster].attack,x,y)) { flushall(); return; }
tmp = 1; bias -= 2; cursors(); }
if (((dam + bias) > c[AC]) || (rnd((int)((c[AC]>0)?c[AC]:1))==1))
{
lprintf("\n The %s hit you ",lastmonst); tmp = 1;
if ((dam -= c[AC]) < 0) dam=0;
if (dam > 0) { losehp(dam); bottomhp(); flushall(); }
}
if (tmp == 0) lprintf("\n The %s missed ",lastmonst);
}
/*
* dropsomething(monst) Function to create an object when a monster dies
* int monst;
*
* Function to create an object near the player when certain monsters are killed
* Enter with the monster number
* Returns nothing of value.
*/
dropsomething(monst)
int monst;
{
switch(monst)
{
case ORC: case NYMPH: case ELF: case TROGLODYTE:
case TROLL: case ROTHE: case VIOLETFUNGI:
case PLATINUMDRAGON: case GNOMEKING: case REDDRAGON:
something(level); return;
case LEPRECHAUN: if (rnd(101)>=75) creategem();
if (rnd(5)==1) dropsomething(LEPRECHAUN); return;
}
}
/*
* dropgold(amount) Function to drop some gold around player
* int amount;
*
* Enter with the number of gold pieces to drop
* Returns nothing of value.
*/
dropgold(amount)
register int amount;
{
if (amount > 250) createitem(OMAXGOLD,amount/100); else createitem(OGOLDPILE,amount);
}
/*
* something(level) Function to create a random item around player
* int level;
*
* Function to create an item from a designed probability around player
* Enter with the cave level on which something is to be dropped
* Returns nothing of value.
*/
something(level)
int level;
{
register int j;
int i;
if (level<0 || level>MAXLEVEL+MAXVLEVEL) return; /* correct level? */
if (rnd(101)<8) something(level); /* possibly more than one item */
j = newobject(level,&i); createitem(j,i);
}
/*
* newobject(lev,i) Routine to return a randomly selected new object
* int lev,*i;
*
* Routine to return a randomly selected object to be created
* Returns the object number created, and sets *i for its argument
* Enter with the cave level and a pointer to the items arg
*/
static char nobjtab[] = { 0, OSCROLL, OSCROLL, OSCROLL, OSCROLL, OPOTION,
OPOTION, OPOTION, OPOTION, OGOLDPILE, OGOLDPILE, OGOLDPILE, OGOLDPILE,
OBOOK, OBOOK, OBOOK, OBOOK, ODAGGER, ODAGGER, ODAGGER, OLEATHER, OLEATHER,
OLEATHER, OREGENRING, OPROTRING, OENERGYRING, ODEXRING, OSTRRING, OSPEAR,
OBELT, ORING, OSTUDLEATHER, OSHIELD, OFLAIL, OCHAIN, O2SWORD, OPLATE,
OLONGSWORD };
newobject(lev,i)
register int lev,*i;
{
register int tmp=32,j;
if (level<0 || level>MAXLEVEL+MAXVLEVEL) return(0); /* correct level? */
if (lev>6) tmp=37; else if (lev>4) tmp=35;
j = nobjtab[tmp=rnd(tmp)]; /* the object type */
switch(tmp)
{
case 1: case 2: case 3: case 4: *i=newscroll(); break;
case 5: case 6: case 7: case 8: *i=newpotion(); break;
case 9: case 10: case 11: case 12: *i=rnd((lev+1)*10)+lev*10+10; break;
case 13: case 14: case 15: case 16: *i=lev; break;
case 17: case 18: case 19: if (!(*i=newdagger())) return(0); break;
case 20: case 21: case 22: if (!(*i=newleather())) return(0); break;
case 23: case 32: case 35: *i=rund(lev/3+1); break;
case 24: case 26: *i=rnd(lev/4+1); break;
case 25: *i=rund(lev/4+1); break;
case 27: *i=rnd(lev/2+1); break;
case 30: case 33: *i=rund(lev/2+1); break;
case 28: *i=rund(lev/3+1); if (*i==0) return(0); break;
case 29: case 31: *i=rund(lev/2+1); if (*i==0) return(0); break;
case 34: *i=newchain(); break;
case 36: *i=newplate(); break;
case 37: *i=newsword(); break;
}
return(j);
}
/*
* spattack(atckno,xx,yy) Function to process special attacks from monsters
* int atckno,xx,yy;
*
* Enter with the special attack number, and the coordinates (xx,yy)
* of the monster that is special attacking
* Returns 1 if must do a show1cell(xx,yy) upon return, 0 otherwise
*
* atckno monster effect
* ---------------------------------------------------
* 0 none
* 1 rust monster eat armor
* 2 hell hound breathe light fire
* 3 dragon breathe fire
* 4 giant centipede weakening sing
* 5 white dragon cold breath
* 6 wraith drain level
* 7 waterlord water gusher
* 8 leprechaun steal gold
* 9 disenchantress disenchant weapon or armor
* 10 ice lizard hits with barbed tail
* 11 umber hulk confusion
* 12 spirit naga cast spells taken from special attacks
* 13 platinum dragon psionics
* 14 nymph steal objects
* 15 bugbear bite
* 16 osequip bite
*
* char rustarm[ARMORTYPES][2];
* special array for maximum rust damage to armor from rustmonster
* format is: { armor type , minimum attribute
*/
#define ARMORTYPES 6
static char rustarm[ARMORTYPES][2] = { OSTUDLEATHER,-2, ORING,-4, OCHAIN,-5,
OSPLINT,-6, OPLATE,-8, OPLATEARMOR,-9 };
static char spsel[] = { 1, 2, 3, 5, 6, 8, 9, 11, 13, 14 };
spattack(x,xx,yy)
int x,xx,yy;
{
register int i,j=0,k,m;
register char *p=0;
if (c[CANCELLATION]) return(0);
vxy(&xx,&yy); /* verify x & y coordinates */
switch(x)
{
case 1: /* rust your armor, j=1 when rusting has occurred */
m = k = c[WEAR];
if ((i=c[SHIELD]) != -1)
if (--ivenarg[i] < -1) ivenarg[i]= -1; else j=1;
if ((j==0) && (k != -1))
{
m = iven[k];
for (i=0; i<ARMORTYPES; i++)
if (m == rustarm[i][0]) /* find his armor in table */
{
if (--ivenarg[k]< rustarm[i][1])
ivenarg[k]= rustarm[i][1]; else j=1;
break;
}
}
if (j==0) /* if rusting did not occur */
switch(m)
{
case OLEATHER: p = "\nThe %s hit you -- Your lucky you have leather on";
break;
case OSSPLATE: p = "\nThe %s hit you -- Your fortunate to have stainless steel armor!";
break;
}
else { beep(); p = "\nThe %s hit you -- your armor feels weaker"; }
break;
case 2: i = rnd(15)+8-c[AC];
spout: p="\nThe %s breathes fire at you!";
if (c[FIRERESISTANCE])
p="\nThe %s's flame doesn't phase you!";
else
spout2: if (p) { lprintf(p,lastmonst); beep(); }
checkloss(i);
return(0);
case 3: i = rnd(20)+25-c[AC]; goto spout;
case 4: if (c[STRENGTH]>3)
{
p="\nThe %s stung you! You feel weaker"; beep();
--c[STRENGTH];
}
else p="\nThe %s stung you!";
break;
case 5: p="\nThe %s blasts you with his cold breath";
i = rnd(15)+18-c[AC]; goto spout2;
case 6: lprintf("\nThe %s drains you of your life energy!",lastmonst);
loselevel(); beep(); return(0);
case 7: p="\nThe %s got you with a gusher!";
i = rnd(15)+25-c[AC]; goto spout2;
case 8: if (c[NOTHEFT]) return(0); /* he has a device of no theft */
if (c[GOLD])
{
p="\nThe %s hit you -- Your purse feels lighter";
if (c[GOLD]>32767) c[GOLD]>>=1;
else c[GOLD] -= rnd((int)(1+(c[GOLD]>>1)));
if (c[GOLD] < 0) c[GOLD]=0;
}
else p="\nThe %s couldn't find any gold to steal";
lprintf(p,lastmonst); disappear(xx,yy); beep();
bottomgold(); return(1);
case 9: for(j=50; ; ) /* disenchant */
{
i=rund(26); m=iven[i]; /* randomly select item */
if (m>0 && ivenarg[i]>0 && m!=OSCROLL && m!=OPOTION)
{
if ((ivenarg[i] -= 3)<0) ivenarg[i]=0;
lprintf("\nThe %s hits you -- you feel a sense of loss",lastmonst);
srcount=0; beep(); show3(i); bottomline(); return(0);
}
if (--j<=0)
{
p="\nThe %s nearly misses"; break;
}
break;
}
break;
case 10: p="\nThe %s hit you with his barbed tail";
i = rnd(25)-c[AC]; goto spout2;
case 11: p="\nThe %s has confused you"; beep();
c[CONFUSE]+= 10+rnd(10); break;
case 12: /* performs any number of other special attacks */
return(spattack(spsel[rund(10)],xx,yy));
case 13: p="\nThe %s flattens you with his psionics!";
i = rnd(15)+30-c[AC]; goto spout2;
case 14: if (c[NOTHEFT]) return(0); /* he has device of no theft */
if (emptyhanded()==1)
{
p="\nThe %s couldn't find anything to steal";
break;
}
lprintf("\nThe %s picks your pocket and takes:",lastmonst);
beep();
if (stealsomething()==0) lprcat(" nothing"); disappear(xx,yy);
bottomline(); return(1);
case 15: i= rnd(10)+ 5-c[AC];
spout3: p="\nThe %s bit you!";
goto spout2;
case 16: i= rnd(15)+10-c[AC]; goto spout3;
};
if (p) { lprintf(p,lastmonst); bottomline(); }
return(0);
}
/*
* checkloss(x) Routine to subtract hp from user and flag bottomline display
* int x;
*
* Routine to subtract hitpoints from the user and flag the bottomline display
* Enter with the number of hit points to lose
* Note: if x > c[HP] this routine could kill the player!
*/
checkloss(x)
int x;
{
if (x>0) { losehp(x); bottomhp(); }
}
/*
* annihilate() Routine to annihilate all monsters around player (playerx,playery)
*
* Gives player experience, but no dropped objects
* Returns the experience gained from all monsters killed
*/
annihilate()
{
int i,j;
register long k;
register char *p;
for (k=0, i=playerx-1; i<=playerx+1; i++)
for (j=playery-1; j<=playery+1; j++)
if (!vxy(&i,&j)) /* if not out of bounds */
if (*(p= &mitem[i][j])) /* if a monster there */
if (*p<DEMONLORD+2)
{
k += monster[*p].experience; *p=know[i][j]=0;
}
else
{
lprintf("\nThe %s barely escapes being annihilated!",monster[*p].name);
hitp[i][j] = (hitp[i][j]>>1) + 1; /* lose half hit points*/
}
if (k>0)
{
lprcat("\nYou hear loud screams of agony!"); raiseexperience((long)k);
}
return(k);
}
/*
* newsphere(x,y,dir,lifetime) Function to create a new sphere of annihilation
* int x,y,dir,lifetime;
*
* Enter with the coordinates of the sphere in x,y
* the direction (0-8 diroffx format) in dir, and the lifespan of the
* sphere in lifetime (in turns)
* Returns the number of spheres currently in existence
*/
newsphere(x,y,dir,life)
int x,y,dir,life;
{
int m;
struct sphere *sp;
if (((sp=(struct sphere *)malloc(sizeof(struct sphere)))) == 0)
return(c[SPHCAST]); /* can't malloc, therefore failure */
if (dir>=9) dir=0; /* no movement if direction not found */
if (level==0) vxy(&x,&y); /* don't go out of bounds */
else
{
if (x<1) x=1; if (x>=MAXX-1) x=MAXX-2;
if (y<1) y=1; if (y>=MAXY-1) y=MAXY-2;
}
if ((m=mitem[x][y]) >= DEMONLORD+4) /* demons dispel spheres */
{
know[x][y]=1; show1cell(x,y); /* show the demon (ha ha) */
cursors(); lprintf("\nThe %s dispels the sphere!",monster[m].name);
beep(); rmsphere(x,y); /* remove any spheres that are here */
return(c[SPHCAST]);
}
if (m==DISENCHANTRESS) /* disenchantress cancels spheres */
{
cursors(); lprintf("\nThe %s causes cancellation of the sphere!",monster[m].name); beep();
boom: sphboom(x,y); /* blow up stuff around sphere */
rmsphere(x,y); /* remove any spheres that are here */
return(c[SPHCAST]);
}
if (c[CANCELLATION]) /* cancellation cancels spheres */
{
cursors(); lprcat("\nAs the cancellation takes effect, you hear a great earth shaking blast!"); beep();
goto boom;
}
if (item[x][y]==OANNIHILATION) /* collision of spheres detonates spheres */
{
cursors(); lprcat("\nTwo spheres of annihilation collide! You hear a great earth shaking blast!"); beep();
rmsphere(x,y);
goto boom;
}
if (playerx==x && playery==y) /* collision of sphere and player! */
{
cursors();
lprcat("\nYou have been enveloped by the zone of nothingness!\n");
beep(); rmsphere(x,y); /* remove any spheres that are here */
nap(4000); died(258);
}
item[x][y]=OANNIHILATION; mitem[x][y]=0; know[x][y]=1;
show1cell(x,y); /* show the new sphere */
sp->x=x; sp->y=y; sp->lev=level; sp->dir=dir; sp->lifetime=life; sp->p=0;
if (spheres==0) spheres=sp; /* if first node in the sphere list */
else /* add sphere to beginning of linked list */
{
sp->p = spheres; spheres = sp;
}
return(++c[SPHCAST]); /* one more sphere in the world */
}
/*
* rmsphere(x,y) Function to delete a sphere of annihilation from list
* int x,y;
*
* Enter with the coordinates of the sphere (on current level)
* Returns the number of spheres currently in existence
*/
rmsphere(x,y)
int x,y;
{
register struct sphere *sp,*sp2=0;
for (sp=spheres; sp; sp2=sp,sp=sp->p)
if (level==sp->lev) /* is sphere on this level? */
if ((x==sp->x) && (y==sp->y)) /* locate sphere at this location */
{
item[x][y]=mitem[x][y]=0; know[x][y]=1;
show1cell(x,y); /* show the now missing sphere */
--c[SPHCAST];
if (sp==spheres) { sp2=sp; spheres=sp->p; free((char*)sp2); }
else
{ sp2->p = sp->p; free((char*)sp); }
break;
}
return(c[SPHCAST]); /* return number of spheres in the world */
}
/*
* sphboom(x,y) Function to perform the effects of a sphere detonation
* int x,y;
*
* Enter with the coordinates of the blast, Returns no value
*/
sphboom(x,y)
int x,y;
{
register int i,j;
if (c[HOLDMONST]) c[HOLDMONST]=1;
if (c[CANCELLATION]) c[CANCELLATION]=1;
for (j=max(1,x-2); j<min(x+3,MAXX-1); j++)
for (i=max(1,y-2); i<min(y+3,MAXY-1); i++)
{
item[j][i]=mitem[j][i]=0;
show1cell(j,i);
if (playerx==j && playery==i)
{
cursors(); beep();
lprcat("\nYou were too close to the sphere!");
nap(3000);
died(283); /* player killed in explosion */
}
}
}
/*
* genmonst() Function to ask for monster and genocide from game
*
* This is done by setting a flag in the monster[] structure
*/
genmonst()
{
register int i,j;
cursors(); lprcat("\nGenocide what monster? ");
for (i=0; (!isalpha(i)) && (i!=' '); i=getchar());
lprc(i);
for (j=0; j<MAXMONST; j++) /* search for the monster type */
if (monstnamelist[j]==i) /* have we found it? */
{
monster[j].genocided=1; /* genocided from game */
lprintf(" There will be no more %s's",monster[j].name);
/* now wipe out monsters on this level */
newcavelevel(level); draws(0,MAXX,0,MAXY); bot_linex();
return;
}
lprcat(" You sense failure!");
}