ftp.nice.ch/pub/next/games/strategic/NetHack.s.tar.gz#/NetHackSource/src/mon.c

This is mon.c in view mode; [Download] [Up]

/*	SCCS Id: @(#)mon.c	3.0	89/11/22
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed.  See license for details. */

/* Aztec C on amiga doesn't recognize defined() at this point! */
#ifndef AZTEC_C
#if defined(MICROPORT_BUG) || (!defined(LINT) && !defined(__STDC__))
#define MKROOM_H
#endif /* Avoid the microport bug */
#endif

#include "hack.h"
#include "mfndpos.h"

#ifdef WORM
#  include "wseg.h"
#endif

#ifdef HARD
OSTATIC boolean FDECL(restrap,(struct monst *));
#endif
#ifdef INFERNO
#  include <ctype.h>
#endif

static struct obj *FDECL(make_corpse,(struct monst *));
OSTATIC void NDECL(dmonsfree);
static void FDECL(m_detach,(struct monst *));

#ifdef OVL1

long lastwarntime;
int lastwarnlev;
const char *warnings[] = {
	"white", "pink", "red", "ruby", "purple", "black" };

#endif /* OVL1 */
#ifdef OVLB

struct monst *fdmon;  /* chain of dead monsters, need not be saved */
		      /* otherwise used only in priest.c */

/* Creates a monster corpse, a "special" corpse, or nothing if it doesn't
 * leave corpses.  Monsters which leave "special" corpses should have
 * G_NOCORPSE set in order to prevent wishing for one, finding tins of one,
 * etc....
 */
static struct obj *
make_corpse(mtmp)
register struct monst *mtmp;
{
	register struct permonst *mdat = mtmp->data;
#ifdef GOLEMS
	int pieces;
#endif
	struct obj *obj = 0;
	int x = mtmp->mx, y = mtmp->my;

	switch(monsndx(mdat)) {
	    case PM_WHITE_UNICORN:
	    case PM_GRAY_UNICORN:
	    case PM_BLACK_UNICORN:
		(void) mksobj_at(UNICORN_HORN, x, y);
		goto default_1;
#ifdef WORM
	    case PM_LONG_WORM:
		(void) mksobj_at(WORM_TOOTH, x, y);
		goto default_1;
#endif
	    case PM_KOBOLD_MUMMY:
		obj = mksobj_at(MUMMY_WRAPPING, x, y); /* and fall through */
	    case PM_KOBOLD_ZOMBIE:
		obj = mksobj_at(CORPSE, x, y);
		obj->corpsenm = PM_KOBOLD;
		obj->age -= 50;			/* this is an *OLD* corpse */
		break;
	    case PM_GNOME_MUMMY:
		obj = mksobj_at(MUMMY_WRAPPING, x, y); /* and fall through */
	    case PM_GNOME_ZOMBIE:
		obj = mksobj_at(CORPSE, x, y);
		obj->corpsenm = PM_GNOME;
		obj->age -= 50;			/* this is an *OLD* corpse */
		break;
	    case PM_ORC_MUMMY:
		obj = mksobj_at(MUMMY_WRAPPING, x, y); /* and fall through */
	    case PM_ORC_ZOMBIE:
		obj = mksobj_at(CORPSE, x, y);
		obj->corpsenm = PM_ORC;
		obj->age -= 50;			/* this is an *OLD* corpse */
		break;
	    case PM_ELF_MUMMY:
		obj = mksobj_at(MUMMY_WRAPPING, x, y); /* and fall through */
	    case PM_ELF_ZOMBIE:
		obj = mksobj_at(CORPSE, x, y);
		obj->corpsenm = PM_ELF;
		obj->age -= 50;			/* this is an *OLD* corpse */
		break;
	    case PM_HUMAN_MUMMY:
		obj = mksobj_at(MUMMY_WRAPPING, x, y); /* and fall through */
	    case PM_HUMAN_ZOMBIE:
		obj = mksobj_at(CORPSE, x, y);
		obj->corpsenm = PM_HUMAN;
		obj->age -= 50;			/* this is an *OLD* corpse */
		break;
	    case PM_GIANT_MUMMY:
		obj = mksobj_at(MUMMY_WRAPPING, x, y); /* and fall through */
	    case PM_GIANT_ZOMBIE:
		obj = mksobj_at(CORPSE, x, y);
		obj->corpsenm = PM_GIANT;
		obj->age -= 50;			/* this is an *OLD* corpse */
		break;
	    case PM_ETTIN_MUMMY:
		obj = mksobj_at(MUMMY_WRAPPING, x, y); /* and fall through */
	    case PM_ETTIN_ZOMBIE:
		obj = mksobj_at(CORPSE, x, y);
		obj->corpsenm = PM_ETTIN;
		obj->age -= 50;			/* this is an *OLD* corpse */
		break;
#ifdef GOLEMS
	    case PM_IRON_GOLEM:
		pieces = d(2,6);
		while (pieces--)
			obj = mksobj_at(IRON_CHAIN, x, y);
		break;
	    case PM_CLAY_GOLEM:
		obj = mksobj_at(ROCK, x, y);
		obj->quan = rn2(20) + 100;
		obj->owt = weight(obj);
		break;
	    case PM_STONE_GOLEM:
		obj = mkcorpstat(STATUE, mdat, x, y);
		break;
	    case PM_WOOD_GOLEM:
		pieces = d(2,4);
		while(pieces--)
			obj = mksobj_at(QUARTERSTAFF, x, y);
		break;
	    case PM_LEATHER_GOLEM:
		pieces = d(2,4);
		while(pieces--)
			obj = mksobj_at(LEATHER_ARMOR, x, y);
		break;
#endif
	    default_1:
	    default:
		if (mdat->geno & G_NOCORPSE)
			return (struct obj *)0;
		else obj = mkcorpstat(CORPSE, mdat, x, y);
		break;
	}
	/* All special cases should precede the G_NOCORPSE check */

	/* Note: oname() cannot be used generically for non-inventory objects
	 * unless you fix the link from the previous object in the chains.
	 * (Here we know it's the first one, so there was no link.)
	 */
	if (mtmp->mnamelth) {
		obj = oname(obj, NAME(mtmp), 0);
		fobj = obj;
		level.objects[x][y] = obj;
	}
	stackobj(fobj);
	newsym(x, y);
	return obj;
}

#endif /* OVLB */
#ifdef OVL2

XSTATIC void
dmonsfree(){
register struct monst *mtmp;
	while(mtmp = fdmon){
		fdmon = mtmp->nmon;
		free((genericptr_t) mtmp);
	}
}

#endif /* OVL2 */
#ifdef OVL1

void
movemon()
{
	register struct monst *mtmp;

	warnlevel = 0;

	while(1) {
		/*  Find a monster that we have not treated yet.
		 *  Note that mtmp or mtmp->nmon might get killed
		 *  while mtmp moves, so we cannot just walk down the
		 *  chain (even new monsters might get created!)
		 */
		/* Do tame monsters first.  Necessary so that when the tame
		 * monster attacks something, the something gets a chance to
		 * attack the tame monster back (which it's permitted to do
		 * only if it hasn't made its move yet).
		 */
		for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
			if(mtmp->mlstmv < moves && mtmp->mtame) goto next_mon;
		for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
			if(mtmp->mlstmv < moves && !mtmp->mtame) goto next_mon;
		/* treated all monsters */
		break;

	next_mon:
		mtmp->mlstmv = moves;

		/* most monsters drown in pools */
		{ boolean inpool,iseel,isgremlin;
#ifdef FOUNTAINS
		  boolean infountain;
#endif

		  inpool = is_pool(mtmp->mx,mtmp->my);
		  iseel = mtmp->data->mlet == S_EEL;
		  isgremlin = mtmp->data->mlet == S_GREMLIN;
#ifdef FOUNTAINS
		  infountain = IS_FOUNTAIN(levl[mtmp->mx][mtmp->my].typ);
#endif
		/* Gremlin multiplying won't go on forever since the hit points
		 * keep going down, and when it gets to 1 hit point the clone
		 * function will fail.
		 */
		  if((inpool
#ifdef FOUNTAINS
			     || infountain
#endif
					  ) && isgremlin && rn2(3)) {
			struct monst *mtmp2 = clone_mon(mtmp);

			if (mtmp2) {
			    mtmp2->mhpmax = (mtmp->mhpmax /= 2);
			    if(cansee(mtmp->mx,mtmp->my))
				pline("%s multiplies.", Monnam(mtmp));
			}
#ifdef FOUNTAINS
			if (infountain) dryup();
#endif
		  } else
		  if(inpool && !is_flyer(mtmp->data) && !is_swimmer(mtmp->data)) {
			if(cansee(mtmp->mx,mtmp->my))
			    pline("%s drowns.", Monnam(mtmp));
			mondead(mtmp);
			continue;
		  } else
		/* but eels have a difficult time outside */
		  if(iseel && !inpool) {
			if(mtmp->mhp > 1) mtmp->mhp--;
			mtmp->mflee = 1;
			mtmp->mfleetim += 2;
		  }
		}
		if(mtmp->mblinded && !--mtmp->mblinded)
			mtmp->mcansee = 1;
		if(mtmp->mfrozen && !--mtmp->mfrozen)
			mtmp->mcanmove = 1;
		if(mtmp->mfleetim && !--mtmp->mfleetim)
			mtmp->mflee = 0;
#ifdef HARD
		/* unwatched mimics and piercers may hide again  [MRS] */
		if(is_hider(mtmp->data) && restrap(mtmp))   continue;
#endif
		if(mtmp->mimic) continue;
		if(mtmp->mspeed != MSLOW || !(moves%2)){
		/* continue if the monster died fighting */
		  if (Conflict && !mtmp->iswiz && mtmp->mcansee) {
/* Note: A couple of notes on conflict here.
	 1. Conflict does not take effect in the first round.  Therefore, 
	    A monster in a stepping into the area will get to swing at you.
	 2. Conflict still works when you are invisible.  (?)
	 3. Certain areas (namely castle) you can be in 3 "rooms" at once!
	    Polyself into Xorn wearing ring of conflict and it can be done.
	    This code only allows for two.  This needs to be changed if more
	    areas (with diggable walls and > 2 rooms) are put into the game.
*/
		    xchar clx = 0, chx = 0, cly = 0, chy = 0,
			  clx2 = 0, chx2 = 0, cly2 = 0, chy2 = 0;
		    /* seelx etc. are not set if blind or blindfolded! */
		    getcorners(&clx, &chx, &cly, &chy,
			       &clx2, &chx2, &cly2, &chy2);
		    if ((dist(mtmp->mx,mtmp->my) < 3) || 
		    /* if the monster is next to you OR */
		 	(levl[u.ux][u.uy].lit &&
			 levl[mtmp->mx][mtmp->my].lit &&
		    /* both you and it are lit AND */
			 ((clx <= mtmp->mx && mtmp->mx <= chx &&
			   cly <= mtmp->my && mtmp->my <= chy) ||
			  (clx2 <= mtmp->mx && mtmp->mx <= chx2 &&
			   cly2 <= mtmp->my && mtmp->my <= chy2))))
		    /* you *could* see it (ie it can see you) */
		      if (fightm(mtmp) != 3)
		      /* have it fight if it choses to */
			continue;
		  }
		  if(dochugw(mtmp))
		  /* otherwise just move the monster */
		    continue;
		}
		if(mtmp->mspeed == MFAST && dochugw(mtmp))
			continue;
	}
#ifdef NAMED_ITEMS
	if (warnlevel == 100) {
		Your("%s %s!", aobjnam(uwep, "glow"),
			Hallucination ? hcolor() : light_blue);
		warnlevel = 0;
	}
#endif
	warnlevel -= u.ulevel;
	if(warnlevel >= SIZE(warnings))
		warnlevel = SIZE(warnings)-1;
	if(!Blind && warnlevel >= 0)
	if(warnlevel > lastwarnlev || moves > lastwarntime + 5){
	    register const char *rr;
	
	    switch((int) (Warning & (LEFT_RING | RIGHT_RING))){
	    case LEFT_RING:
		rr = "Your left ring glows";
		break;
	    case RIGHT_RING:
		rr = "Your right ring glows";
		break;
	    case LEFT_RING | RIGHT_RING:
		rr = "Both your rings glow";
		break;
	    default:
		{ char buf[33];
		Sprintf(buf, "Your %s glow", makeplural(body_part(FINGERTIP)));
		rr = buf;
		}
		break;
	    }
	    pline("%s %s!", rr, Hallucination ? hcolor() : warnings[warnlevel]);
	    lastwarntime = moves;
	    lastwarnlev = warnlevel;
	}

	dmonsfree();	/* remove all dead monsters */
}

#endif /* OVL1 */
#ifdef OVLB

void
meatgold(mtmp)
	register struct monst *mtmp;
{
	register struct gold *gold;
	register struct obj *otmp;

	/* Eats gold if it is there */
	if(gold = g_at(mtmp->mx, mtmp->my)){
		if (cansee(mtmp->mx, mtmp->my) && flags.verbose)
			pline("%s eats some gold!", Monnam(mtmp));
		mtmp->meating = (int)((gold->amount + 500L)/1000L);
		freegold(gold);
		/* Left behind a pile? */
		if(rnd(25) < 3) (void) mksobj_at(ROCK, mtmp->mx, mtmp->my);
		newsym(mtmp->mx, mtmp->my);
	}
	/* Eats topmost metal object if it is there */
	for (otmp = level.objects[mtmp->mx][mtmp->my];
						    otmp; otmp = otmp->nexthere)
	    if (objects[otmp->otyp].oc_material > WOOD &&
		objects[otmp->otyp].oc_material < MINERAL) {
		    if (cansee(mtmp->mx,mtmp->my) && flags.verbose)
			pline("%s eats %s!", Monnam(mtmp),
				distant_name(otmp,doname));
		    else if (flags.soundok && flags.verbose)
			You("hear a crunching sound.");
		    mtmp->meating = otmp->owt/2 - 1;
		    /* Heal up to the object's weight in hp */
		    if (mtmp->mhp < mtmp->mhpmax) {
			mtmp->mhp += objects[otmp->otyp].oc_weight;
			if (mtmp->mhp > mtmp->mhpmax) mtmp->mhp = mtmp->mhpmax;
		    }
		    if(otmp == uball) {
			unpunish();
			freeobj(otmp);
		    } else if(otmp == uchain)
			unpunish();	/* frees uchain */
		    else
			freeobj(otmp);
		    /* Left behind a pile? */
		    if(rnd(25) < 3) (void) mksobj_at(ROCK, mtmp->mx, mtmp->my);
		    newsym(mtmp->mx, mtmp->my);
		    break;
	    }
}

void
meatobj(mtmp)		/* for gelatinous cubes */
	register struct monst *mtmp;
{
	register struct obj *otmp, *otmp2;

	/* Eats organic, glass, or wood objects if there */
	/* Engulfs others, except huge rocks and metal attached to player */
	for (otmp = level.objects[mtmp->mx][mtmp->my]; otmp; otmp = otmp2) {
	    otmp2 = otmp->nexthere;
	    if(objects[otmp->otyp].oc_material <= WOOD) {
		if (cansee(mtmp->mx,mtmp->my) && flags.verbose)
		    pline("%s eats %s!", Monnam(mtmp),
			    distant_name(otmp, doname));
		else if (flags.soundok && flags.verbose)
		    You("hear a slurping sound.");
		/* Heal up to the object's weight in hp */
		if (mtmp->mhp < mtmp->mhpmax) {
		    mtmp->mhp += objects[otmp->otyp].oc_weight;
		    if (mtmp->mhp > mtmp->mhpmax) mtmp->mhp = mtmp->mhpmax;
		}
		delobj(otmp);		/* munch */
	    } else if (otmp->olet != ROCK_SYM &&
				    otmp != uball && otmp != uchain) {
		if (cansee(mtmp->mx, mtmp->my) && flags.verbose)
		    pline("%s engulfs %s.", Monnam(mtmp),
			    distant_name(otmp,doname));
		freeobj(otmp);
		mpickobj(mtmp, otmp);	/* slurp */
	    }
	    /* Engulf & devour is instant, so don't set meating */
	    newsym(mtmp->mx, mtmp->my);
	}
}

void
mpickgold(mtmp)
	register struct monst *mtmp;
{
	register struct gold *gold;

	if(gold = g_at(mtmp->mx, mtmp->my)){
		mtmp->mgold += gold->amount;
		if (cansee(mtmp->mx, mtmp->my) && flags.verbose)
			pline("%s picks up some gold.", Monnam(mtmp));
		freegold(gold);
		if(levl[mtmp->mx][mtmp->my].scrsym == GOLD_SYM)
			newsym(mtmp->mx, mtmp->my);
	}
}

/* Now includes giants which pick up enormous rocks.  KAA */
void
mpickgems(mtmp)
	register struct monst *mtmp;
{
	register struct obj *otmp;

	for(otmp = level.objects[mtmp->mx][mtmp->my]; otmp; otmp=otmp->nexthere)
	    if(throws_rocks(mtmp->data) ? otmp->otyp == BOULDER :
			(otmp->olet == GEM_SYM && otmp->otyp < LAST_GEM+6))
		if(mtmp->data->mlet != S_UNICORN
			|| objects[otmp->otyp].g_val != 0){
		    if (cansee(mtmp->mx,mtmp->my) && flags.verbose)
			pline("%s picks up %s.", Monnam(mtmp),
				distant_name(otmp, doname));
		    freeobj(otmp);
		    mpickobj(mtmp, otmp);
		    newsym(mtmp->mx, mtmp->my);
		    return;	/* pick only one object */
		}
}

#endif /* OVLB */
#ifdef OVL0

int
curr_mon_load(mtmp)
register struct monst *mtmp;
{
	register int curload = 0;
	register struct obj *obj;

	for(obj = mtmp->minvent; obj; obj = obj->nobj) {
		if(obj->otyp != BOULDER || !throws_rocks(mtmp->data))
			curload += weight(obj);
	}

	return curload;
}

int
max_mon_load(mtmp)
register struct monst *mtmp;
{
	register int maxload;

	/* Base monster carrying capacity is equal to human maximum
	 * carrying capacity, or half human maximum if not strong.
	 * (for a polymorphed player, the value used would be the
	 * non-polymorphed carrying capacity instead of max/half max).
	 * This is then modified by the ratio between the monster weights
	 * and human weights (weight of a human=45).  Limits for corpseless
	 * monsters are arbitrary.
	 */
	maxload = (mtmp->data->cwt ? mtmp->data->cwt : mtmp->data->mlevel*6)
		* MAX_CARR_CAP / 45;
	if (!strongmonst(mtmp->data)) maxload /= 2;

	return maxload;
}

/* for restricting monsters' object-pickup */
boolean
can_carry(mtmp,otmp)
struct monst *mtmp;
struct obj *otmp;
{
	register int newload = weight(otmp);

	if (otmp->otyp == CORPSE && otmp->corpsenm == PM_COCKATRICE
						&& !resists_ston(mtmp->data))
		return(FALSE);
	if (mtmp->isshk) return(TRUE); /* no limit */
	if (mtmp->mpeaceful && !mtmp->mtame) return(FALSE);
	/* otherwise players might find themselves obligated to violate
	 * their alignment if the monster takes something they need
	 */
	
	/* special--boulder throwers carry unlimited amounts of boulders */
	if (throws_rocks(mtmp->data) && otmp->otyp == BOULDER)
		return(TRUE);
	
	/* nymphs deal in stolen merchandise, but not boulders or statues */
	if (mtmp->data->mlet == S_NYMPH)
		return !(otmp->olet == ROCK_SYM);

	if(curr_mon_load(mtmp) + newload > max_mon_load(mtmp)) return(FALSE);

	return(TRUE);
}

#endif /* OVL0 */
#ifdef OVL2

void
mpickstuff(mtmp, str)
	register struct monst *mtmp;
	register const char *str;
{
	register struct obj *otmp;

/*	prevent shopkeepers from leaving the door of their shop */
	if(mtmp->isshk && inhishop(mtmp)) return;

	for(otmp = level.objects[mtmp->mx][mtmp->my]; otmp; otmp=otmp->nexthere)
	    if(index(str, otmp->olet)) {
		if(!can_carry(mtmp,otmp)) return;
		if (cansee(mtmp->mx,mtmp->my) && flags.verbose)
			pline("%s picks up %s.", Monnam(mtmp), doname(otmp));
		freeobj(otmp);
		mpickobj(mtmp, otmp);
		if(index(str, (char) levl[mtmp->mx][mtmp->my].scrsym))
			newsym(mtmp->mx, mtmp->my);
		return;			/* pick only one object */
	    }
}

#endif /* OVL2 */
#ifdef OVL0

/* return number of acceptable neighbour positions */
int
mfndpos(mon, poss, info, flag)
	register struct monst *mon;
	coord *poss;	/* coord poss[9] */
	long *info;	/* long info[9] */
	long flag;
{
	register int x,y,nx,ny,cnt = 0;
	register uchar ntyp;
	uchar nowtyp;
	boolean wantpool,poolok,nodiag;

	x = mon->mx;
	y = mon->my;
	nowtyp = levl[x][y].typ;

	nodiag = (mon->data == &mons[PM_GRID_BUG]);
	wantpool = mon->data->mlet == S_EEL;
	poolok = is_flyer(mon->data) || (is_swimmer(mon->data) && !wantpool);
nexttry:	/* eels prefer the water, but if there is no water nearby,
		   they will crawl over land */
	if(mon->mconf) {
		flag |= ALLOW_ALL;
		flag &= ~NOTONL;
	}
	if(!mon->mcansee)
		flag |= ALLOW_SSM;
	for(nx = x-1; nx <= x+1; nx++) for(ny = y-1; ny <= y+1; ny++) {
	    if((nx == x && ny == y) || !isok(nx,ny)) continue;
	    if(nx != x && ny != y && nodiag) continue;
	    if(IS_ROCK(ntyp = levl[nx][ny].typ) && !(flag & ALLOW_WALL) &&
		!((flag & ALLOW_DIG) && may_dig(nx,ny))) continue;
	    if(IS_DOOR(ntyp) && !amorphous(mon->data) &&
	       ((levl[nx][ny].doormask & D_CLOSED && !(flag & OPENDOOR)) ||
		(levl[nx][ny].doormask & D_LOCKED && !(flag & UNLOCKDOOR))
	       ) && !(flag & (ALLOW_WALL|ALLOW_DIG|BUSTDOOR))) continue;
	    if(nx != x && ny != y &&
#ifdef REINCARNATION
	       ((IS_DOOR(nowtyp) &&
	         ((levl[x][y].doormask & ~D_BROKEN) || dlevel == rogue_level)) ||
		(IS_DOOR(ntyp) &&
		 ((levl[nx][ny].doormask & ~D_BROKEN) || dlevel == rogue_level))
#else
	       ((IS_DOOR(nowtyp) && (levl[x][y].doormask & ~D_BROKEN)) ||
		(IS_DOOR(ntyp) && (levl[nx][ny].doormask & ~D_BROKEN))
#endif
	       ))
		continue;
	    if(is_pool(nx,ny) == wantpool || poolok) {
		/* Displacement also displaces the Elbereth/scare monster,
		 * as long as you are visible.
		 */
		int dispx = (Displaced && (!Invis || perceives(mon->data)) &&
			(mon->mux==nx)) ? u.ux : nx;
		int dispy = (Displaced && (!Invis || perceives(mon->data)) &&
			(mon->muy==ny)) ? u.uy : ny;

		info[cnt] = 0;
		if(sobj_at(SCR_SCARE_MONSTER, dispx, dispy)
#ifdef ELBERETH
		   || sengr_at("Elbereth", dispx, dispy)
#endif
		  ) {
			if(!(flag & ALLOW_SSM)) continue;
			info[cnt] |= ALLOW_SSM;
		}
		if((nx == u.ux && ny == u.uy) ||
		   (nx == mon->mux && ny == mon->muy)) {
			if(!(flag & ALLOW_U)) continue;
			info[cnt] |= ALLOW_U;
		} else {
			if(MON_AT(nx, ny)) {
				if(!(flag & ALLOW_M)) continue;
				info[cnt] |= ALLOW_M;
				if((m_at(nx,ny))->mtame) {
					if(!(flag & ALLOW_TM)) continue;
					info[cnt] |= ALLOW_TM;
				}
			}
#if defined(ALTARS) && defined(THEOLOGY)
			/* Note: ALLOW_SANCT only prevents movement, not */
			/* attack, into a temple. */
			if(!in_temple(x, y) && in_temple(nx, ny) &&
					u_in_sanctuary(in_temple(nx, ny))) {
				if(!(flag & ALLOW_SANCT)) continue;
				info[cnt] |= ALLOW_SANCT;
			}
#endif
		}
		if(sobj_at(CLOVE_OF_GARLIC, nx, ny)) {
			if(flag & NOGARLIC) continue;
			info[cnt] |= NOGARLIC;
		}
		if(sobj_at(BOULDER, nx, ny)) {
			if(!(flag & ALLOW_ROCK)) continue;
			info[cnt] |= ALLOW_ROCK;
		}
		if((!Invis || perceives(mon->data)) && online(nx,ny)){
			if(flag & NOTONL) continue;
			info[cnt] |= NOTONL;
		}
		/* we cannot avoid traps of an unknown kind */
		{ register struct trap *ttmp = t_at(nx, ny);
		  register long tt;
			if(ttmp) {
/*				tt = 1L << ttmp->ttyp;*/
/* why don't we just have code look like what it's supposed to do? then it
/* might start working for every case. try this instead: -sac */
				tt = (ttmp->ttyp < TRAPNUM && ttmp->ttyp);
				/* below if added by GAN 02/06/87 to avoid
				 * traps out of range
				 */
				if(!(tt & ALLOW_TRAPS))  {
impossible("A monster looked at a very strange trap of type %d.", ttmp->ttyp);
					continue;
				}
				if(mon->mtrapseen & tt) {

					if(!(flag & tt)) continue;
					info[cnt] |= tt;
				}
			}
		}
		poss[cnt].x = nx;
		poss[cnt].y = ny;
		cnt++;
	    }
	}
	if(!cnt && wantpool && !is_pool(x,y)) {
		wantpool = FALSE;
		goto nexttry;
	}
	return(cnt);
}

#endif /* OVL0 */
#ifdef OVL1

int
dist(x, y)
register int x,y;
{
	register int dx = x - u.ux, dy = y - u.uy;
	return dx*dx + dy*dy;
}

boolean
monnear(mon, x, y)
register struct monst *mon;
register int x,y;
/* Is the square close enough for the monster to move or attack into? */
{
	register int distance = dist2(mon->mx, mon->my, x, y);
	if (distance==2 && mon->data==&mons[PM_GRID_BUG]) return 0;
	return (distance < 3);
}

#endif /* OVL1 */
#ifdef OVLB

static const char *poiseff[] = {

	" feel very weak", "r brain is on fire",
	" can't think straight", "r muscles won't obey you",
	" feel very sick", " break out in hives"
};

void
poisontell(typ)

	int	typ;
{
	pline("You%s.", poiseff[typ]);
}

void
poisoned(string, typ, pname, fatal)
register const char *string, *pname;
register int  typ, fatal;
{
	register int i, plural;
	boolean thrown_weapon = !strncmp(string, "poison", 6);
		/* admittedly a kludge... */

	if(strcmp(string, "blast") && !thrown_weapon) {
	    /* 'blast' has already given a 'poison gas' message */
	    /* so have "poison arrow", "poison dart", etc... */
	    plural = (string[strlen(string) - 1] == 's')? 1 : 0;
	    if(Blind)
		pline("%s poisoned.", plural ? "They were" : "It was");
#ifdef INFERNO
	    /* avoid "The" Orcus's sting was poisoned... */
	    else if(isupper(*string))
		pline("%s %s poisoned!", string, plural ? "were" : "was");
#endif
	    else
		pline("The %s %s poisoned!", string, plural ? "were" : "was");
	}

	if(Poison_resistance) {
		if(!strcmp(string, "blast")) shieldeff(u.ux, u.uy);
		pline("The poison doesn't seem to affect you.");
		return;
	}
	i = rn2(fatal + 20*thrown_weapon);
	if(i == 0 && typ != A_CHA) {
		u.uhp = -1;
		pline("The poison was deadly...");
	} else if(i <= 5) {
		You("%s!", poiseff[typ]);
		adjattrib(typ, thrown_weapon ? -1 : -rn1(3,3), TRUE);
	} else {
		losehp(thrown_weapon ? rnd(6) : rn1(10,6), pname, KILLED_BY_AN);
	}
	if(u.uhp < 1) {
		killer_format = KILLED_BY_AN;
		killer = pname;
		done(POISONING);
	}
}

static void
m_detach(mtmp)
register struct monst *mtmp;
{
#ifdef WALKIES
	if(mtmp->mleashed) m_unleash(mtmp);
#endif
	relobj(mtmp,1);
	unpmon(mtmp);
	relmon(mtmp);
	unstuck(mtmp);
}

void
mondead(mtmp)
register struct monst *mtmp;
{
	m_detach(mtmp);	
#ifdef KOPS
	if(mtmp->data->mlet == S_KOP && allow_kops) {
	    /* Dead Kops may come back. */
	    switch(rnd(5)) {
		case 1:	     /* returns near the stairs */
			(void) makemon(mtmp->data,xdnstair,ydnstair);
			break;
		case 2:	     /* randomly */
			(void) makemon(mtmp->data,0,0);
			break;
		default:
			break;
	    }
	}
#endif
	if(mtmp->isshk) shkdead(mtmp);
#ifdef WORM
	if(mtmp->wormno) wormdead(mtmp);
#endif
#ifdef HARD
	if(mtmp->iswiz) wizdead(mtmp);
#endif
#ifdef MEDUSA
	if(mtmp->data == &mons[PM_MEDUSA]) u.ukilled_medusa = TRUE;
#endif
	monfree(mtmp);
}

/* called when monster is moved to larger structure */
void
replmon(mtmp, mtmp2)
register struct monst *mtmp, *mtmp2;
{
	relmon(mtmp);
	monfree(mtmp);
	place_monster(mtmp2, mtmp2->mx, mtmp2->my);
	mtmp2->nmon = fmon;
	fmon = mtmp2;
	if(u.ustuck == mtmp) u.ustuck = mtmp2;
	if(mtmp2->isshk) replshk(mtmp,mtmp2);
#ifdef WORM
	if(mtmp2->wormno) {
		/* Each square the worm is on has a pointer; fix them all */
		register struct wseg *wtmp;

		for(wtmp=wsegs[mtmp2->wormno]; wtmp; wtmp=wtmp->nseg)
			place_worm_seg(mtmp2, wtmp->wx, wtmp->wy);
	}
#endif
}

void
relmon(mon)
register struct monst *mon;
{
	register struct monst *mtmp;

	if (fmon == 0)  panic ("relmon: no fmon available.");

	remove_monster(mon->mx, mon->my);

	if(mon == fmon) fmon = fmon->nmon;
	else {
		for(mtmp = fmon; mtmp && mtmp->nmon != mon; mtmp = mtmp->nmon) ;
		if(mtmp)    mtmp->nmon = mon->nmon;
		else	    panic("relmon: mon not in list.");
	}
}

/* we do not free monsters immediately, in order to have their name
   available shortly after their demise */
void
monfree(mtmp) register struct monst *mtmp; {
	mtmp->nmon = fdmon;
	fdmon = mtmp;
	remove_monster(mtmp->mx, mtmp->my);
}

void
unstuck(mtmp)
register struct monst *mtmp;
{
	if(u.ustuck == mtmp) {
		if(u.uswallow){
			u.ux = mtmp->mx;
			u.uy = mtmp->my;
			u.uswallow = 0;
			u.uswldtim = 0;
			setsee();
			docrt();
		}
		u.ustuck = 0;
	}
}

void
killed(mtmp)
register struct monst *mtmp;
{
	xkilled(mtmp, 1);
}

void
xkilled(mtmp, dest)
	register struct monst *mtmp;
/*
 * Dest=1, normal; dest=0, don't print message; dest=2, don't drop corpse
 * either; dest=3, message but no corpse
 */
	int	dest;
{
	register int tmp, nk, x, y;
	register struct permonst *mdat = mtmp->data;
	register struct obj *otmp;
	boolean chance;

	if (dest & 1) {
	    if(!cansee(mtmp->mx,mtmp->my)) You("destroy it!");
	    else {
		You("destroy %s!",
			mtmp->mtame ? a2_monnam(mtmp, "poor") : mon_nam(mtmp));
	    }
	}

	/* restore chameleon, lycanthropes to true form at death */
	/* cannot do this in make_corpse() since genociding monsters after
	 * MAXMONNO were killed does the wrong type
	 */
	if(mtmp->cham) mtmp->data = mdat = &mons[PM_CHAMELEON];
	if(mdat == &mons[PM_JACKALWERE])
		mtmp->data = mdat = &mons[PM_WEREJACKAL];
	if(mdat == &mons[PM_WOLFWERE])
		mtmp->data = mdat = &mons[PM_WEREWOLF];
	if(mdat == &mons[PM_RATWERE])
		mtmp->data = mdat = &mons[PM_WERERAT];

	/* if we have killed MAXMONNO monsters of a given type, and it
	 * can be done, genocide that monster.
	 */
	tmp = monsndx(mdat);
	u.nr_killed[tmp]++;
	nk = u.nr_killed[tmp];
#ifdef TOLKIEN
	if(nk > (tmp==PM_NAZGUL ? 9 : MAXMONNO) &&
				!(mons[tmp].geno & (G_NOGEN | G_GENOD))) {
#else
	if(nk > MAXMONNO && !(mons[tmp].geno & (G_NOGEN | G_GENOD))) {
#endif
#ifdef DEBUG
		pline("Automatically genocided %s.", makeplural(mons[tmp].mname));
#endif
		if (tmp != PM_WIZARD_OF_YENDOR)
			mons[tmp].geno |= G_GENOD;
	}
#ifdef MAIL
	/* If you kill the mail daemon, no more mail delivery.  -3. */
	else if(tmp==PM_MAIL_DAEMON) mons[tmp].geno |= G_GENOD;
#endif	

	/* punish bad behaviour */
	if(is_human(mdat) && !always_hostile(mdat) &&
	   (monsndx(mdat) < PM_ARCHEOLOGIST || monsndx(mdat) > PM_WIZARD) &&
	   u.ualigntyp != U_CHAOTIC) {
		HTelepat &= ~INTRINSIC;
		change_luck(-2);
	}
	if((mtmp->mpeaceful && !rn2(2)) || mtmp->mtame)	change_luck(-1);
	if ((mdat==&mons[PM_BLACK_UNICORN] && u.ualigntyp == U_CHAOTIC) ||
	    (mdat==&mons[PM_GRAY_UNICORN] && u.ualigntyp == U_NEUTRAL) ||
	    (mdat==&mons[PM_WHITE_UNICORN] && u.ualigntyp == U_LAWFUL))
		change_luck(-5);

	/* give experience points */
	tmp = experience(mtmp, nk);
	more_experienced(tmp, 0);
	newexplevel();		/* will decide if you go up */

	/* adjust alignment points */
	if(mtmp->mtame)
		adjalign(-15);	/* bad!! */
#if defined(ALTARS) && defined(THEOLOGY)
	else if (mtmp->ispriest && !p_coaligned(mtmp))
		adjalign(2);
#endif
	else if (mtmp->mpeaceful)
		adjalign(-5);
	/* malign was already adjusted for ualigntyp and randomization */
	adjalign(mtmp->malign);

	/* dispose of monster and make cadaver */
	if(stoned) {
		monstone(mtmp);
		return;
	}

	x = mtmp->mx;   y = mtmp->my;

	mondead(mtmp);

	if((dest & 2)
#ifdef REINCARNATION
		 || dlevel == rogue_level
#endif
					) return;

#ifdef MAIL
	if(mdat == &mons[PM_MAIL_DAEMON]) {
		(void) mksobj_at(SCR_MAIL, x, y);
		stackobj(fobj);
		newsym(x,y);
	}
#endif
	if(!accessible(x, y)) {
	    /* might be mimic in wall or dead eel*/
 	    newsym(x,y);
	} else if(x != u.ux || y != u.uy) {
		/* might be here after swallowed */
		if (!rn2(6) && !(mdat->geno & G_NOCORPSE)
#ifdef KOPS
					&& mdat->mlet != S_KOP
#endif
							) {
			int typ;

			otmp = mkobj_at(RANDOM_SYM, x, y);
			/* Don't create large objects from small monsters */
			typ = otmp->otyp;
			if (mdat->msize < MZ_HUMAN && typ != FOOD_RATION
#ifdef WALKIES
			    && typ != LEASH
#endif
			    && typ != FIGURINE
			    && (otmp->owt > 3 ||
				(typ >= SPEAR && typ <= LANCE) ||
				(typ >= SCIMITAR && typ <= KATANA) ||
				(typ == MORNING_STAR || typ == QUARTERSTAFF) ||
				(typ >= BARDICHE && typ <= VOULGE) ||
				(typ>=PLATE_MAIL && typ<=DRAGON_SCALE_MAIL) ||
				(typ == LARGE_SHIELD))) {
			    delobj(otmp);
			} else newsym(x,y);
		}
		/* Whether or not it always makes a corpse is, in theory,
		 * different from whether or not the corpse is "special";
		 * if we want both, we have to specify it explicitly.
		 */
		if (bigmonst(mdat) || mdat == &mons[PM_LIZARD]
#ifdef GOLEMS
				   || is_golem(mdat)
#endif
		   ) chance = 1;
		else chance = !rn2((int)
			(2 + ((mdat->geno & G_FREQ)<2) + verysmall(mdat)));
		if (chance)
			(void) make_corpse(mtmp);
	}
}

void
rescham() {	/* force all chameleons to become normal */

	register struct monst *mtmp;

	for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
		if(mtmp->cham) {
			mtmp->cham = 0;
			(void) newcham(mtmp, &mons[PM_CHAMELEON]);
		}
		if(is_were(mtmp->data) && mtmp->data->mlet != S_HUMAN)
			(void) new_were(mtmp);
	}
}

/* Let the chameleons change again -dgk */
void
restartcham() {

	register struct monst *mtmp;

	for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
		if (mtmp->data == &mons[PM_CHAMELEON])
			mtmp->cham = 1;
}

int
newcham(mtmp, mdat)	/* make a chameleon look like a new monster */
			/* returns 1 if the monster actually changed */
	register struct monst *mtmp;
	register struct permonst *mdat;
{
	register int mhp, hpn, hpd;
	int tryct;
       struct permonst *olddata = mtmp->data;

	/* mdat = 0 -> caller wants a random monster shape */
	tryct = 0;
	if(mdat == 0) {
		while (++tryct < 100) {
			static int num;
			mdat = &mons[num=rn2(NUMMONS)];
			if ((!is_human(mdat) || num == PM_NURSE)
				&& !type_is_pname(mdat)
				&& !is_were(mdat)
#ifdef MEDUSA
				&& num != PM_MEDUSA
#endif
#ifdef MAIL
				&& num != PM_MAIL_DAEMON
#endif
				)
				break;
		}
		if (tryct >= 100) return(0); /* Should never happen */
	}
  	if(mdat == mtmp->data) return(0);	/* still the same monster */

#ifdef WORM
	if(mtmp->wormno) wormdead(mtmp);	/* throw tail away */
#endif
	hpn = mtmp->mhp;
 	hpd = (mtmp->m_lev < 50) ? (mtmp->m_lev)*8 : mdat->mlevel;
 	if(!hpd) hpd = 4;

	mtmp->m_lev = adj_lev(mdat);		/* new monster level */

 	mhp = (mtmp->m_lev < 50) ? (mtmp->m_lev)*8 : mdat->mlevel;
 	if(!mhp) mhp = 4;

	/* new hp: same fraction of max as before */
#ifndef LINT
 	mtmp->mhp = (int)(((long)hpn*(long)mhp)/(long)hpd);
#endif
 	if(mtmp->mhp < 0) mtmp->mhp = hpn;	/* overflow */
/* Unlikely but not impossible; a 1HD creature with 1HP that changes into a
   0HD creature will require this statement */
 	if (!mtmp->mhp) mtmp->mhp = 1;

/* and the same for maximum hit points */
	hpn = mtmp->mhpmax;
#ifndef LINT
 	mtmp->mhpmax = (int)(((long)hpn*(long)mhp)/(long)hpd);
#endif
 	if(mtmp->mhpmax < 0) mtmp->mhpmax = hpn;	/* overflow */
 	if (!mtmp->mhpmax) mtmp->mhpmax = 1;

 	mtmp->data = mdat;
	mtmp->minvis = !!(mdat->mlet == S_STALKER);
	mtmp->mhide = !!hides_under(mdat);
	if (!mtmp->mhide) mtmp->mundetected = 0;
	if (u.ustuck == mtmp) {
		if(u.uswallow) {
			if(!attacktype(mdat,AT_ENGL)) {
				/* Does mdat care? */
				if (!noncorporeal(mdat) && !amorphous(mdat) && 
				    !is_whirly(mdat) && 
				    (mdat != &mons[PM_YELLOW_LIGHT])) {
					You("break out of %s%s!", mon_nam(mtmp),
					    (is_animal(mdat)? 
					    "'s stomach" : ""));
					mtmp->mhp = 1;  /* almost dead */
				}
				expels(mtmp, olddata, FALSE);
			}
		} else {
			if(!sticks(mdat)
#ifdef POLYSELF
				&& !sticks(uasmon)
#endif
				)
				unstuck(mtmp);
		}
	}

#ifdef WORM
	if(mdat == &mons[PM_LONG_WORM] && getwn(mtmp)) initworm(mtmp);
#endif
	unpmon(mtmp);	/* necessary for 'I' and to force pmon */
	pmon(mtmp);
	return(1);
}

void
mnexto(mtmp)	/* Make monster mtmp next to you (if possible) */
	struct monst *mtmp;
{
	coord mm;
	enexto(&mm, u.ux, u.uy, mtmp->data);
	remove_monster(mtmp->mx, mtmp->my);
	place_monster(mtmp, mm.x, mm.y);
	pmon(mtmp);
	set_apparxy(mtmp);
}

void
mnearto(mtmp,x,y,gz)	/* Make monster near (or at) location if possible */
	register struct monst *mtmp;
	xchar x, y;
	boolean gz;     
{
	coord mm;
	if(!gz || !goodpos(x,y,mtmp->data)) {
		enexto(&mm, x, y, mtmp->data);
		x = mm.x; y = mm.y;
	}
	if(x == mtmp->mx && y == mtmp->my) /* that was easy */
		return;
	remove_monster(mtmp->mx, mtmp->my);
	place_monster(mtmp, x, y);
	pmon(mtmp);
	set_apparxy(mtmp);
}

#endif /* OVLB */
#ifdef OVL2

void
setmangry(mtmp)
	register struct monst *mtmp;
{
	if(!mtmp->mpeaceful) return;
	if(mtmp->mtame) return;
	mtmp->mpeaceful = 0;
#if defined(ALTARS) && defined(THEOLOGY)
	if(mtmp->ispriest) {
		if(p_coaligned(mtmp)) adjalign(-5); /* very bad */
		else adjalign(2);
	} else
#endif
	adjalign(-1);		/* attacking peaceful monsters is bad */
	if(humanoid(mtmp->data) || mtmp->isshk || mtmp->isgd)
		pline("%s gets angry!", Monnam(mtmp));
#ifdef SOUNDS
	else if (flags.verbose && flags.soundok) growl(mtmp);
#endif
}

int
disturb(mtmp)		/* awaken monsters while in the same room.
			 * return a 1 if they have been woken.
			 */
	register struct monst *mtmp;
{
	/* wake up, or get out of here. */
	/* ettins are hard to surprise */
	/* Nymphs and Leprechauns do not easily wake up */
	if(cansee(mtmp->mx,mtmp->my) &&
		(!Stealth || (mtmp->data == &mons[PM_ETTIN] && rn2(10))) &&
		(!(mtmp->data->mlet == S_NYMPH
			|| mtmp->data == &mons[PM_JABBERWOCK]
			|| mtmp->data->mlet == S_LEPRECHAUN) || !rn2(50)) &&
		(Aggravate_monster ||
		 (mtmp->data->mlet == S_DOG || mtmp->data->mlet == S_HUMAN) ||
		(!rn2(7) && !mtmp->mimic))) {
		mtmp->msleep = 0;
		return(1);
	}
	if(Hallucination) pmon(mtmp);
	return(0);
}

#ifdef HARD
XSTATIC boolean
restrap(mtmp)
/* unwatched hiders may hide again,
 * if so, a 1 is returned.
 */
register struct monst *mtmp;
{
	if(mtmp->cham || mtmp->mcan || mtmp->mimic ||
	   cansee(mtmp->mx, mtmp->my) || rn2(3))
		return(FALSE);

	if(mtmp->data->mlet == S_MIMIC) {
		set_mimic_sym(mtmp);
		return(TRUE);
	} else
	    if(levl[mtmp->mx][mtmp->my].typ == ROOM)  {
		(void) maketrap(mtmp->mx, mtmp->my, MONST_TRAP);
		/* override type selection */
		ftrap->pm = monsndx(mtmp->data);
		mondead(mtmp);
		return(TRUE);
	    }

	return(FALSE);
}
#endif

#endif /* OVL2 */
#ifdef OVLB

/* drop (perhaps) a cadaver and remove monster */
void
mondied(mdef)
register struct monst *mdef;
{
	mondead(mdef);
	if(rn2(3)
#ifdef REINCARNATION
	   && dlevel != rogue_level
#endif
					)
		(void) make_corpse(mdef);
}

/* monster disappears, not dies */
void
mongone(mdef)
register struct monst *mdef;
{
	register struct obj *otmp, *otmp2;

	/* release monster's inventory */
	for (otmp = mdef->minvent; otmp; otmp = otmp2) {
		otmp2 = otmp->nobj;
		obfree(otmp, (struct obj *)0);
	}
	mdef->minvent = 0;
	mdef->mgold = 0;
	m_detach(mdef);
	monfree(mdef);
}

/* drop a statue or rock and remove monster */
void
monstone(mdef)
register struct monst *mdef;
{
	struct obj *otmp;

	if((int)mdef->data->msize > MZ_TINY ||
	   !rn2(2 + ((mdef->data->geno & G_FREQ) > 2))) {
		otmp = mk_named_object(STATUE, mdef->data, mdef->mx, mdef->my,
			NAME(mdef), (int)mdef->mnamelth);
		otmp->spe = 0; /* no book inside */
	} else
		(void) mksobj_at(ROCK, mdef->mx, mdef->my);

	stackobj(fobj);

	if(cansee(mdef->mx, mdef->my)){
		unpmon(mdef);
		atl(mdef->mx,mdef->my,Hallucination ? rndobjsym() : fobj->olet);
	}
	mondead(mdef);
}

#ifdef GOLEMS
void
golemeffects(mon, damtype, dam)
register struct monst *mon;
int damtype, dam;
{
	int heal=0, slow=0;

	if (mon->data != &mons[PM_FLESH_GOLEM]
					&& mon->data != &mons[PM_IRON_GOLEM])
		return;

	if (mon->data == &mons[PM_FLESH_GOLEM]) {
		if (damtype == AD_ELEC) heal = dam / 6;
		else if (damtype == AD_FIRE || damtype == AD_COLD) slow = 1;
	} else {
		if (damtype == AD_ELEC) slow = 1;
		else if (damtype == AD_FIRE) heal = dam;
	}
	if (slow) {
		if (mon->mspeed != MSLOW) {
			if (mon->mspeed == MFAST) mon->mspeed = 0;
			else mon->mspeed = MSLOW;
			if (cansee(mon->mx, mon->my))
				pline("%s seems to be moving slower.",
					Monnam(mon));
		}
	}
	if (heal) {
		if (mon->mhp < mon->mhpmax) {
			mon->mhp += dam;
			if (mon->mhp > mon->mhpmax) mon->mhp = mon->mhpmax;
			if (cansee(mon->mx, mon->my))
				pline("%s seems healthier.", Monnam(mon));
		}
	}
}
#endif /* GOLEMS */

#endif /* OVLB */

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.