view arogue7/chase.c @ 302:fa70bba6bb3f rel2021.03

Update the README.
author John "Elwin" Edwards
date Thu, 18 Mar 2021 20:53:49 -0400
parents f9ef86cf22b2
children e52a8a7ad4c5
line wrap: on
line source

/*
 * chase.c  -  Code for one object to chase another
 *
 * Advanced Rogue
 * Copyright (C) 1984, 1985, 1986 Michael Morgan, Ken Dalka and AT&T
 * All rights reserved.
 *
 * Based on "Rogue: Exploring the Dungeons of Doom"
 * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
 * All rights reserved.
 *
 * See the file LICENSE.TXT for full copyright and licensing information.
 */

/*
 * Code for one object to chase another
 *
 */

#include <ctype.h>
#include <limits.h>
#include <stdlib.h>
#include "curses.h"
#include "rogue.h"
#define	MAXINT	INT_MAX
#define	MININT	INT_MIN

bool straight_shot(int ery, int erx, int eey, int eex, coord *shooting);

/*
 * Canblink checks if the monster can teleport (blink).  If so, it will
 * try to blink the monster next to the player.
 */

bool
can_blink(struct thing *tp)
{
    register int y, x, index=9;
    coord tryp;	/* To hold the coordinates for use in diag_ok */
    bool spots[9], found_one=FALSE;

    /*
     * First, can the monster even blink?  And if so, there is only a 50%
     * chance that it will do so.  And it won't blink if it is running or
     * held.
     */
    if (off(*tp, CANBLINK) || (on(*tp, ISHELD)) ||
	on(*tp, ISFLEE) ||
	tp->t_action == A_FREEZE ||
	(rnd(12) < 6)) return(FALSE);


    /* Initialize the spots as illegal */
    do {
	spots[--index] = FALSE;
    } while (index > 0);

    /* Find a suitable spot next to the player */
    for (y=hero.y-1; y<hero.y+2; y++)
	for (x=hero.x-1; x<hero.x+2; x++, index++) {
	    /* Make sure x coordinate is in range and that we are
	     * not at the player's position
	     */
	    if (x<0 || x >= cols || index == 4) continue;

	    /* Is it OK to move there? */
	    if (step_ok(y, x, NOMONST, tp) &&
		(!isatrap(mvwinch(cw, y, x)) ||
		  rnd(10) >= tp->t_stats.s_intel ||
		  on(*tp, ISFLY))) {
		/* OK, we can go here.  But don't go there if
		 * monster can't get at player from there
		 */
		tryp.y = y;
		tryp.x = x;
		if (diag_ok(&tryp, &hero, tp)) {
		    spots[index] = TRUE;
		    found_one = TRUE;
		}
	    }
	}

    /* If we found one, go to it */
    if (found_one) {
	char rch;	/* What's really where the creatures moves to */

	/* Find a legal spot */
	while (spots[index=rnd(9)] == FALSE) continue;

	/* Get the coordinates */
	y = hero.y + (index/3) - 1;
	x = hero.x + (index % 3) - 1;

	/* Move the monster from the old space */
	mvwaddch(cw, tp->t_pos.y, tp->t_pos.x, tp->t_oldch);

	/* Move it to the new space */
	tp->t_oldch = CCHAR( mvwinch(cw, y, x) );

	/* Display the creature if our hero can see it */
	if (cansee(y, x) &&
	    off(*tp, ISINWALL) &&
	    !invisible(tp))
	    mvwaddch(cw, y, x, tp->t_type);

	/* Fix the monster window */
	mvwaddch(mw, tp->t_pos.y, tp->t_pos.x, ' '); /* Clear old position */
	mvwaddch(mw, y, x, tp->t_type);

	/* Record the new position */
	tp->t_pos.y = y;
	tp->t_pos.x = x;

	/* If the monster is on a trap, trap it */
	rch = CCHAR( mvinch(y, x) );
	if (isatrap(rch)) {
	    if (cansee(y, x)) tp->t_oldch = rch;
	    be_trapped(tp, &(tp->t_pos));
	}
    }

    return(found_one);
}

/* 
 * Can_shoot determines if the monster (er) has a direct line of shot 
 * at the prey (ee).  If so, it returns the direction in which to shoot.
 */

coord *
can_shoot(coord *er, coord *ee)
{
    static coord shoot_dir;

    /* 
     * They must be in the same room or very close (at door)
     */
    if (roomin(er) != roomin(ee) && DISTANCE(er->y,er->x,ee->y,ee->x) > 1)
	return(NULL);

    /* Do we have a straight shot? */
    if (!straight_shot(er->y, er->x, ee->y, ee->x, &shoot_dir)) return(NULL);
    else return(&shoot_dir);
}

/*
 * chase:
 *	Find the spot for the chaser(er) to move closer to the
 *	chasee(ee).  Rer is the room of the chaser, and ree is the
 *	room of the creature being chased (chasee).  Flee is true if 
 *	destination (ee) is player and monster is running away
 *	or the player is in a wall and the monster can't get to it
 */

void
chase(struct thing *tp, coord *ee, struct room *rer, struct room *ree, 
      bool flee)
{
    int dist, thisdist, monst_dist = MAXINT; 
    register coord *er = &tp->t_pos; 
    struct thing *prey;			/* What we are chasing */
    coord ch_ret;			/* Where chasing takes you */
    char ch, mch;
    bool next_player = FALSE;

    /* 
     * set the distance from the chas(er) to the chas(ee) here and then
     * we won't have to reset it unless the chas(er) moves (instead of shoots)
     */
    dist = DISTANCE(er->y, er->x, ee->y, ee->x);

    /*
     * See if our destination is a monster or player.  If so, make "prey" point
     * to it.
     */
    if (ce(hero, *ee)) prey = &player;	/* Is it the player? */
    else if (tp->t_dest && ce(*(tp->t_dest), *ee)) {	/* Is it a monster? */
	struct linked_list *item;

	/* What is the monster we're chasing? */
	item = find_mons(ee->y, ee->x);
	if (item != NULL) prey = THINGPTR(item);
	else prey = NULL;
    }
    else prey = NULL;

    /* We will use at least one movement period */
    tp->t_no_move = movement(tp);
    if (on(*tp, ISFLY)) /* If the creature is flying, speed it up */
	tp->t_no_move /= 2;

    /*
     * If the thing is confused or it can't see the player,
     * let it move randomly. 
     */
    if ((on(*tp, ISHUH) && rnd(10) < 8) ||
	(prey && on(*prey, ISINVIS) && off(*tp, CANSEE))) { /* invisible prey */
	/*
	 * get a valid random move
	 */
	tp->t_newpos = *rndmove(tp);
	dist = DISTANCE(tp->t_newpos.y, tp->t_newpos.x, ee->y, ee->x);
    }

    /*
     * Otherwise, find the empty spot next to the chaser that is
     * closest to the chasee.
     */
    else {
	register int ey, ex, x, y;
	int dist_to_old = MININT; /* Dist from goal to old position */

	/*
	 * This will eventually hold where we move to get closer
	 * If we can't find an empty spot, we stay where we are.
	 */
	dist = flee ? 0 : MAXINT;
	ch_ret = *er;

	/* Are we at our goal already? */
	if (!flee && ce(ch_ret, *ee)) {
	    turn_off(*tp, ISRUN);	/* So stop running! */
	    return;
	}

	ey = er->y + 1;
	ex = er->x + 1;

	/* Check all possible moves */
	for (x = er->x - 1; x <= ex; x++) {
	    if (x < 0 || x >= cols) /* Don't try off the board */
		continue;
	    for (y = er->y - 1; y <= ey; y++) {
		coord tryp;

		if ((y < 1) || (y >= lines - 2)) /* Don't try off the board */
		    continue;

		/* Don't try the player if not going after the player */
		if ((flee || !ce(hero, *ee) || on(*tp, ISFRIENDLY)) &&
		    x == hero.x && y == hero.y) {
		    next_player = TRUE;
		    continue;
		}

		tryp.x = x;
		tryp.y = y;

		/* Is there a monster on this spot closer to our goal?
		 * Don't look in our spot or where we were.
		 */
		if (!ce(tryp, *er) && !ce(tryp, tp->t_oldpos) &&
		    isalpha(mch = CCHAR( mvwinch(mw, y, x) ) )) {
		    int test_dist;

		    test_dist = DISTANCE(y, x, ee->y, ee->x);
		    if (test_dist <= 25 &&   /* Let's be fairly close */
			test_dist < monst_dist) {
			/* Could we really move there? */
			mvwaddch(mw, y, x, ' '); /* Temporarily blank monst */
			if (diag_ok(er, &tryp, tp)) monst_dist = test_dist;
			mvwaddch(mw, y, x, mch); /* Restore monster */
		    }
		}

		/* Can we move onto the spot? */	
		if (!diag_ok(er, &tryp, tp)) continue;

		ch = CCHAR( mvwinch(cw, y, x) );	/* Screen character */

		/*
		 * Stepping on player is NOT okay if we are fleeing.
		 * If we are friendly to the player and there is a monster
		 * in the way that is not of our race, it is okay to move
		 * there.
		 */
		if (step_ok(y, x, FIGHTOK, tp) &&
		    (off(*tp, ISFLEE) || ch != PLAYER))
		{
		    /*
		     * If it is a trap, an intelligent monster may not
		     * step on it (unless our hero is on top!)
		     */
		    if ((isatrap(ch))			&& 
			(rnd(10) < tp->t_stats.s_intel) &&
			(!on(*tp, ISFLY))		&&
			(y != hero.y || x != hero.x)) 
			    continue;

		    /*
		     * OK -- this place counts
		     */
		    thisdist = DISTANCE(y, x, ee->y, ee->x);

		    /* Adjust distance if we are being shot at */
		    if (tp->t_wasshot && tp->t_stats.s_intel > 5 &&
			prey != NULL) {
			/* Move out of line of sight */
			if (straight_shot(tryp.y, tryp.x, ee->y, ee->x, NULL)) {
			    if (flee) thisdist -= SHOTPENALTY;
			    else thisdist += SHOTPENALTY;
			}

			/* But do we want to leave the room? */
			else if (rer && rer == ree && ch == DOOR)
			    thisdist += DOORPENALTY;
		    }

		    /* Don't move to the last position if we can help it
		     * (unless out prey just moved there)
		     */
		    if (ce(tryp, tp->t_oldpos) && (flee || !ce(tryp, hero)))
			dist_to_old = thisdist;

		    else if ((flee && (thisdist > dist)) ||
			(!flee && (thisdist < dist)))
		    {
			ch_ret = tryp;
			dist = thisdist;
		    }
		}
	    }
	}

	/* If we aren't trying to get the player, but he is in our way,
	 * hit him (unless we have been turned or are friendly).  next_player
	 * being TRUE -> we are next to the player but don't want to hit him.
	 *
	 * If we are friendly to the player, following him, and standing next
	 * to him, we will try to help him out in battle.
	 */
	if (next_player && off(*tp, WASTURNED)) {
	    if (off(*tp, ISFRIENDLY) &&
		((flee && ce(ch_ret, *er)) ||
		 (!flee && DISTANCE(er->y, er->x, ee->y, ee->x) < dist)) &&
		step_ok(tp->t_dest->y, tp->t_dest->x, NOMONST, tp)) {
		/* Okay to hit player */
		debug("Switching to hero.");
		tp->t_newpos = hero;
		tp->t_action = A_MOVE;
		return;
	    }
	    else if (on(*tp, ISFRIENDLY) && !flee && ce(*ee, hero)) {
		/*
		 * Look all around the player.  If there is a fightable
		 * creature next to both of us, hit it.  Otherwise, if
		 * there is a fightable creature next to the player, try
		 * to move next to it.
		 */
		dist = MAXINT;
		for (x = hero.x - 1; x <= hero.x + 1; x++) {
		    if (x < 0 || x >= cols) /* Don't try off the board */
			continue;
		    for (y = hero.y - 1; y <= hero.y + 1; y++) {
			if ((y < 1) || (y >= lines - 2)) /* Stay on the board */
			    continue;

			/* Is there a fightable monster here? */
			if (isalpha(mvwinch(mw, y, x)) &&
			    step_ok(y, x, FIGHTOK, tp) &&
			    off(*tp, ISSTONE)) {
			    thisdist = DISTANCE(er->y, er->x, y, x);
			    if (thisdist < dist) {
				dist = thisdist;
				ch_ret.y = y;
				ch_ret.x = x;
			    }
			}
		    }
		}

		/* Are we next to a bad guy? */
		if (dist <= 2) {	/* Get him! */
		    tp->t_newpos = ch_ret;
		    tp->t_action = A_MOVE;
		}

		/* Try to move to the bad guy */
		else if (dist < MAXINT)
		    chase(tp, &ch_ret,
			  roomin(&tp->t_pos), roomin(&ch_ret), FALSE);

		else tp->t_action = A_NIL;

		return;
	    }
	}


	/*
	 * If we have decided that we can move onto a monster (we are
	 * friendly to the player, go to it.
	 */
	if (!ce(ch_ret, *er) && isalpha(mvwinch(mw, ch_ret.y, ch_ret.x))) {
	    debug("Attack monster");
	    tp->t_newpos = ch_ret;
	    tp->t_action = A_MOVE;
	    return;
	}

	/* If we can't get closer to the player (if that's our goal)
	 * because other monsters are in the way, just stay put
	 */
	if (!flee && ce(hero, *ee) && monst_dist < MAXINT &&
	    DISTANCE(er->y, er->x, hero.y, hero.x) < dist) {
		tp->t_action = A_NIL; /* do nothing for awhile */
		return;
	}

	/* Do we want to go back to the last position? */
	else if (dist_to_old != MININT &&      /* It is possible to move back */
	    ((flee && dist == 0) ||	/* No other possible moves */
	     (!flee && dist == MAXINT))) {
	    /* Do we move back or just stay put (default)? */
	    dist = DISTANCE(er->y, er->x, ee->y, ee->x); /* Current distance */
	    if (!flee || (flee && (dist_to_old > dist))) ch_ret = tp->t_oldpos;
	}

	/* Record the new destination */
	tp->t_newpos = ch_ret;
    }

    /*
     * Do we want to fight or move?  If our selected destination (ch_ret)
     * is our hero, then we want to fight.  Otherwise, we want to move.
     */
    if (ce(tp->t_newpos, hero)) {
	/* Fight! (or sell) */
	if (on(*tp, CANSELL)) {
	    tp->t_action = A_SELL;
	    tp->t_no_move += movement(tp); /* takes a little time to sell */
	}
	else {
	    tp->t_action = A_ATTACK;

	    /*
	     * Try to find a weapon to wield.  Wield_weap will return a
	     * projector if weapon is a projectile (eg. bow for arrow).
	     * If weapon is NULL (the case here), it will try to find
	     * a suitable weapon.
	     *
	     * 		Add in rest of time. Fight is 
	     * 		movement() + weap_move() + FIGHTBASE
	     */
	    tp->t_using = wield_weap(NULL, tp);
	    if (tp->t_using == NULL)
		tp->t_no_move += weap_move(tp, NULL);
	    else
		tp->t_no_move += weap_move(tp, OBJPTR(tp->t_using));

	    if (on(*tp, ISHASTE))
		    tp->t_no_move += FIGHTBASE/2;
	    else if (on(*tp, ISSLOW))
		    tp->t_no_move += FIGHTBASE*2;
	    else
		    tp->t_no_move += FIGHTBASE;
	}
    }
    else {
	/* Move */
	tp->t_action = A_MOVE;

	/*
	 * Check if the creature is not next to the player.  If it
	 * is not and has held or suffocated the player, then stop it!
	 * Note that this code should more appropriately appear in
	 * the area that actually moves the monster, but for now it
	 * is okay here because the player can't move while held or
	 * suffocating.
	 */
	if (dist > 2) {
	    if (on(*tp, DIDHOLD)) {
		 turn_off(*tp, DIDHOLD);
		 turn_on(*tp, CANHOLD);
		 if (--hold_count == 0) 
		     turn_off(player, ISHELD);
	    }

	    /* If monster was suffocating, stop it */
	    if (on(*tp, DIDSUFFOCATE)) {
		turn_off(*tp, DIDSUFFOCATE);
		turn_on(*tp, CANSUFFOCATE);
		extinguish(suffocate);
		msg("You can breathe again.....Whew!");
	    }
	}
    }
}

/*
 * do_chase:
 *	Make one thing chase another.
 */

void
do_chase(struct thing *th)
{
    register struct room *orig_rer,	/* Original room of chaser */
			 *new_room;	/* new room of monster */
    char floor, rch, sch;
    coord old_pos,			/* Old position of monster */
	  ch_ret;			/* Where we want to go */

    if (on(*th, NOMOVE)) return;

    ch_ret = th->t_newpos;	/* Record our desired new position */

    /*
     * Make sure we have an open spot (no other monster's gotten in our way,
     * someone didn't just drop a scare monster there, our prey didn't just
     * get there, etc.)
     */
    if (!step_ok(th->t_newpos.y, th->t_newpos.x, FIGHTOK, th)) {
	/*
	 * Most monsters get upset now.  Guardians are all friends,
	 * and we don't want to see 50 messages in a row!
	 */
	if (th->t_stats.s_intel > 4	&&
	    off(*th, ISUNDEAD)		&&
	    off(*th, ISGUARDIAN)	&&
	    off(*th, AREMANY)		&&
	    off(*th, ISHUH)		&&
	    off(player, ISBLIND)	&&
	    cansee(unc(th->t_pos))	&&
	    !invisible(th))
	    msg("%s motions angrily.", prname(monster_name(th), TRUE));
	return;
    }
    else if (ce(th->t_newpos, hero) ||	/* Player just got in our way */
	     isalpha(mvwinch(mw, th->t_newpos.y, th->t_newpos.x))) {
	bool fightplayer = ce(th->t_newpos, hero);

	/* If we were turned or are friendly, we just have to sit here! */
	if (fightplayer && (on(*th, WASTURNED) || on(*th, ISFRIENDLY))) return;

	/* Do we want to sell something? */
	if (fightplayer && on(*th, CANSELL)) {
	    th->t_action = A_SELL;
	    th->t_no_move += movement(th); /* takes a little time to sell */
	    return;
	}

	/* Let's hit him */
	th->t_action = A_ATTACK;

	/*
	 * Try to find a weapon to wield.  Wield_weap will return a
	 * projector if weapon is a projectile (eg. bow for arrow).
	 * If weapon is NULL (the case here), it will try to find
	 * a suitable weapon.
	 */
	th->t_using = wield_weap(NULL, th);
	/*
	 * add in rest of time
	 */
	if (th->t_using == NULL)
	    th->t_no_move += weap_move(th, NULL);
	else
	    th->t_no_move += weap_move(th, OBJPTR(th->t_using));
	if (on(*th, ISHASTE))
		th->t_no_move += FIGHTBASE/2;
	else if (on(*th, ISSLOW))
		th->t_no_move += FIGHTBASE*2;
	else
		th->t_no_move += FIGHTBASE;
	return;
    }

    /*
     * Blank out the old position and record the new position --
     * the blanking must be done first in case the positions are the same.
     */
    mvwaddch(mw, th->t_pos.y, th->t_pos.x, ' ');
    mvwaddch(mw, ch_ret.y, ch_ret.x, th->t_type);

    /* Get new and old rooms of monster */
    new_room = roomin(&ch_ret);
    orig_rer = roomin(&th->t_pos);

    /* Store the critter's old position and update the current one */
    old_pos = th->t_pos;
    th->t_pos = ch_ret;
    floor = (roomin(&ch_ret) == NULL) ? PASSAGE : FLOOR;

    /* If we have a scavenger, it can pick something up */
    if (off(*th, ISGUARDIAN)) {
	register struct linked_list *n_item, *o_item;
	register int item_count = 0;
	bool want_something = FALSE;

	while ((n_item = find_obj(ch_ret.y, ch_ret.x)) != NULL) {
	    register struct object *n_obj, *o_obj;
	    bool wants_it;

	    /* Does this monster want anything? */
	    if (want_something == FALSE) {
		if (on(*th, ISSCAVENGE)  || on(*th, CARRYFOOD)   ||
		    on(*th, CARRYGOLD)   || on(*th, CARRYSCROLL) ||
		    on(*th, CARRYPOTION) || on(*th, CARRYRING)   ||
		    on(*th, CARRYSTICK)  || on(*th, CARRYMISC)   ||
		    on(*th, CARRYWEAPON) || on(*th, CARRYARMOR)	 ||
		    on(*th, CARRYDAGGER))  {
			want_something = TRUE;

			/*
			 * Blank the area.  We have to do it only before the
			 * first item in case an item gets dropped in same
			 * place.  We don't want to blank it out after it get
			 * dropped.
			 */
			mvaddch(ch_ret.y, ch_ret.x, floor);

			/* Were we specifically after something here? */
			if (ce(*th->t_dest, ch_ret)) {
			    /* If we're mean, we go after the hero */
			    if (on(*th, ISMEAN)) runto(th, &hero);

			    /* Otherwise just go back to sleep */
			    else {
				turn_off(*th, ISRUN);
				th->t_dest = NULL;
			    }
			}
		}
		else break;
	    }

	    item_count++;	/* Count the number of items */

	    /*
	     * see if he's got one of this group already
	     */
	    o_item = NULL;
	    n_obj = OBJPTR(n_item);
	    detach(lvl_obj, n_item);

	    /* See if he wants it */
	    if (n_obj->o_type == SCROLL && n_obj->o_which == S_SCARE &&
		th->t_stats.s_intel < 16)
		wants_it = FALSE; /* Most monsters don't want a scare monster */
	    else if (on(*th, ISSCAVENGE)) wants_it = TRUE;
	    else {
		wants_it = FALSE;	/* Default case */
		switch (n_obj->o_type) {
		    case FOOD:	if(on(*th, CARRYFOOD))   wants_it = TRUE;
		    when GOLD:	if(on(*th, CARRYGOLD))   wants_it = TRUE;
		    when SCROLL:if(on(*th, CARRYSCROLL)) wants_it = TRUE;
		    when POTION:if(on(*th, CARRYPOTION)) wants_it = TRUE;
		    when RING:	if(on(*th, CARRYRING))   wants_it = TRUE;
		    when STICK:	if(on(*th, CARRYSTICK))  wants_it = TRUE;
		    when MM:	if(on(*th, CARRYMISC))   wants_it = TRUE;
		    when ARMOR:	if(on(*th, CARRYARMOR))  wants_it = TRUE;
		    when WEAPON:if(on(*th, CARRYWEAPON)	||
				  (on(*th,CARRYDAGGER)&&n_obj->o_which==DAGGER))
					wants_it = TRUE;
		}
	    }
	    /*
	     * The quartermaster doesn't sell cursed stuff so he won't
	     * pick it up
	     */
	    if (on(*th, CANSELL) && (n_obj->o_flags & ISCURSED))
		wants_it = FALSE;

	    /* If he doesn't want it, throw it away */
	    if (wants_it == FALSE) {
		fall(n_item, FALSE);
		continue;
	    }

	    /* Otherwise, let's pick it up */
	    if (n_obj->o_group) {
		for(o_item = th->t_pack; o_item != NULL; o_item = next(o_item)){
		    o_obj = OBJPTR(o_item);
		    if (o_obj->o_group == n_obj->o_group) {
			o_obj->o_count += n_obj->o_count;
			o_discard(n_item);
			break;
		    }
		}
	    }
	    if (o_item == NULL) {	/* didn't find it */
	        attach(th->t_pack, n_item);
	    }
	}

	/* If there was anything here, we may have to update the screen */
	if (item_count) {
	    if (cansee(ch_ret.y, ch_ret.x))
		mvwaddch(cw, ch_ret.y, ch_ret.x, mvinch(ch_ret.y, ch_ret.x));
	    updpack(TRUE, th); /* Update the monster's encumberance, too */
	}
    }


    rch = CCHAR( mvwinch(stdscr, old_pos.y, old_pos.x) ); 
    if (th->t_oldch == floor && rch != floor && !isatrap(rch))
	mvwaddch(cw, old_pos.y, old_pos.x, rch);
    else
	mvwaddch(cw, old_pos.y, old_pos.x, th->t_oldch);
    sch = CCHAR( mvwinch(cw, ch_ret.y, ch_ret.x) ); /* What player sees */
    rch = CCHAR( mvwinch(stdscr, ch_ret.y, ch_ret.x) ); /* What's really there */

    /* If we have a tunneling monster, it may be making a tunnel */
    if (on(*th, CANTUNNEL)	&&
	(rch == SECRETDOOR || rch == WALL || rch == '|' || rch == '-')) {
	char nch;	/* The new look to the tunnel */

	if (rch == WALL) nch = PASSAGE;
	else if (levtype == MAZELEV) nch = FLOOR;
	else nch = DOOR;
	addch(nch);

	if (cansee(ch_ret.y, ch_ret.x)) sch = nch; /* Can player see this? */

	/* Does this make a new exit? */
	if (rch == '|' || rch == '-') {
	    struct linked_list *newroom;
	    coord *exit;

	    newroom = new_item(sizeof(coord));
	    exit = DOORPTR(newroom);
	    *exit = ch_ret;
	    attach(new_room->r_exit, newroom);
	}
    }

    /* Mark if the monster is inside a wall */
    if (isrock(mvinch(ch_ret.y, ch_ret.x))) turn_on(*th, ISINWALL);
    else turn_off(*th, ISINWALL);

    /* If the monster can illuminate rooms, check for a change */
    if (on(*th, HASFIRE)) {
	register struct linked_list *fire_item;

	/* Is monster entering a room? */
	if (orig_rer != new_room && new_room != NULL) {
	    fire_item = creat_item();	/* Get an item-only structure */
	    ldata(fire_item) = (char *) th;

	    attach(new_room->r_fires, fire_item);
	    new_room->r_flags |= HASFIRE;

	    if (cansee(ch_ret.y, ch_ret.x) && next(new_room->r_fires) == NULL)
		light(&hero);
	}

	/* Is monster leaving a room? */
	if (orig_rer != new_room && orig_rer != NULL) {
	    /* Find the bugger in the list and delete him */
	    for (fire_item = orig_rer->r_fires; fire_item != NULL;
		 fire_item = next(fire_item)) {
		if (THINGPTR(fire_item) == th)  {	/* Found him! */
		    detach(orig_rer->r_fires, fire_item);
		    destroy_item(fire_item);
		    if (orig_rer->r_fires == NULL) {
			orig_rer->r_flags &= ~HASFIRE;
			if (cansee(old_pos.y, old_pos.x))
			    light(&old_pos);
		    }
		    break;
		}
	    }
	}
    }

    /* If monster is entering player's room and player can see it,
     * stop the player's running.
     */
    if (new_room != orig_rer && new_room != NULL  &&
	new_room == roomin(th->t_dest) && cansee(unc(ch_ret))    &&
	(off(*th, ISINVIS)     || on(player, CANSEE)) &&
	(off(*th, ISSHADOW)    || on(player, CANSEE)) &&
	(off(*th, CANSURPRISE) || ISWEARING(R_ALERT))) {
		running = FALSE;
		if (fight_flush) md_flushinp();
    }

    th->t_oldch = sch;

    /* Let's display those creatures that we can see. */
    if (cansee(unc(ch_ret)) &&
	off(*th, ISINWALL) &&
	!invisible(th))
        mvwaddch(cw, ch_ret.y, ch_ret.x, th->t_type);

    /* Record monster's last position (if new one is different) */
    if (!ce(ch_ret, old_pos)) th->t_oldpos = old_pos;

    /* If the monster is on a trap, trap it */
    sch = CCHAR( mvinch(ch_ret.y, ch_ret.x) );
    if (isatrap(sch)) {
	if (cansee(ch_ret.y, ch_ret.x)) th->t_oldch = sch;
	be_trapped(th, &ch_ret);
    }
}


/* 
 * Get_hurl returns the weapon that the monster will "throw" if he has one 
 */

struct linked_list *
get_hurl(struct thing *tp)
{
    struct linked_list *arrow=NULL, *bolt=NULL, *rock=NULL,
	*spear = NULL, *dagger=NULL, *dart=NULL, *aklad=NULL;
    register struct linked_list *pitem;
    register struct object *obj;
    bool bow=FALSE, crossbow=FALSE, sling=FALSE;

    for (pitem=tp->t_pack; pitem; pitem=next(pitem)) {
	obj = OBJPTR(pitem);
	if (obj->o_type == WEAPON)
	    switch (obj->o_which) {
		case BOW:	bow = TRUE;
		when CROSSBOW:	crossbow = TRUE;
		when SLING:	sling = TRUE;
		when ROCK:	rock = pitem;
		when ARROW:	arrow = pitem;
		when BOLT:	bolt = pitem;
		when SPEAR:	spear = pitem;
		when DAGGER:
		    /* Don't throw the dagger if it's our last one */
		    if (obj->o_count > 1) dagger = pitem;
		when DART:	dart = pitem;
	    }
	else if (obj->o_type == RELIC &&
		 obj->o_which == AXE_AKLAD)
		    aklad = pitem;
    }
    
    /* Do we have that all-powerful Aklad Axe? */
    if (aklad) return(aklad);

    /* Use crossbow bolt if possible */
    if (crossbow && bolt) return(bolt);
    if (bow && arrow) return(arrow);
    if (spear) return(spear);