diff xrogue/chase.c @ 133:e6179860cb76

Import XRogue 8.0 from the Roguelike Restoration Project (r1490)
author John "Elwin" Edwards
date Tue, 21 Apr 2015 08:55:20 -0400
parents
children f54901b9c39b
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xrogue/chase.c	Tue Apr 21 08:55:20 2015 -0400
@@ -0,0 +1,936 @@
+/*
+    chase.c  -  Code for one object to chase another
+
+    XRogue: Expeditions into the Dungeons of Doom
+    Copyright (C) 1991 Robert Pietkivitch
+    All rights reserved.
+    
+    Based on "Advanced Rogue"
+    Copyright (C) 1984, 1985 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.
+*/
+
+#include <ctype.h>
+#include <curses.h>
+#include <limits.h>
+#include "rogue.h"
+
+/*
+ * Canblink checks if the monster can teleport (blink).  If so, it will
+ * try to blink the monster next to the player.
+ */
+
+bool
+can_blink(tp)
+register 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) {
+        unsigned 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 = 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 = 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.
+ */
+
+int
+can_shoot(er, ee, shoot_dir)
+register coord *er, *ee, *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)
+    {
+        shoot_dir->x = shoot_dir->y = 0;
+        return(-1);
+    }
+
+    /* Do we have a straight shot? */
+    if (!straight_shot(er->y, er->x, ee->y, ee->x, shoot_dir)) 
+    {
+        shoot_dir->x = shoot_dir->y = 0;
+        return(-2);
+    }
+    else 
+        return(0);
+}
+
+/*
+ * 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).
+ */
+
+chase(tp, ee, rer, ree, flee)
+register struct thing *tp;
+register coord *ee;
+register struct room *rer, *ree;
+bool flee; /* 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
+            */
+{
+    int dist, thisdist, monst_dist = INT_MAX; 
+    register coord *er = &tp->t_pos; 
+    struct thing *prey;                 /* What we are chasing */
+    coord ch_ret;                       /* Where chasing takes you */
+    unsigned 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 = INT_MIN; /* 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 : INT_MAX;
+        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 = 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 = 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, (coord *)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 = INT_MAX;
+                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 < INT_MAX)
+                    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 < INT_MAX &&
+            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 != INT_MIN &&      /* It is possible to move back */
+            ((flee && dist == 0) ||     /* No other possible moves */
+             (!flee && dist == INT_MAX))) {
+            /* 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((struct object *)NULL, tp);
+            if (tp->t_using == NULL)
+                tp->t_no_move += weap_move(tp, (struct object *)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.
+ */
+
+do_chase(th)
+register struct thing *th;
+{
+    register struct room *orig_rer,     /* Original room of chaser */
+                         *new_room;     /* new room of monster */
+    unsigned 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(*th, ISCHARMED)     &&
+            off(player, ISBLIND)    &&
+            cansee(unc(th->t_pos))  &&
+        !invisible(th) && (rnd(15) < 5)) {
+            switch (rnd(10)) {
+              case 0: case 1:
+            msg("%s lashes out at you! ",prname(monster_name(th),TRUE));
+          when 2: case 3:
+            msg("%s scrambles around. ",prname(monster_name(th), TRUE));
+                  otherwise:
+            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((struct object *)NULL, th);
+        /*
+         * add in rest of time
+         */
+        if (th->t_using == NULL)
+            th->t_no_move += weap_move(th, (struct object *)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 = 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 = mvwinch(cw, ch_ret.y, ch_ret.x); /* What player sees */
+    rch = 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==VERTWALL || rch==HORZWALL)) {
+        unsigned char nch;       /* The new look to the tunnel */
+
+        if (rch == WALL && levtype == OUTSIDE) nch = FLOOR;
+        else if (rch == WALL) nch = PASSAGE;
+        else if (levtype == MAZELEV || levtype == OUTSIDE) 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 == VERTWALL || rch == HORZWALL) {
+            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) 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 = 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(tp)
+register 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);
+    if (dagger) return(dagger);
+    if (sling && rock) return(rock);
+    if (dart) return(dart);
+    return(NULL);
+}
+
+/*
+ * runto:
+ *      Set a monster running after something
+ */
+
+runto(runner, spot)
+register struct thing *runner;
+coord *spot;
+{
+    if (on(*runner, ISSTONE))
+        return;
+
+    /* If we are chasing a new creature, forget about thrown weapons */
+    if (runner->t_dest && !ce(*runner->t_dest, *spot)) runner->t_wasshot=FALSE;
+
+    /*
+     * Start the beastie running
+     */
+    runner->t_dest = spot;
+    turn_on(*runner, ISRUN);
+    turn_off(*runner, ISDISGUISE);
+}
+
+/*
+ * straight_shot:
+ *      See if there is a straight line of sight between the two
+ *      given coordinates.  If shooting is not NULL, it is a pointer
+ *      to a structure which should be filled with the direction
+ *      to shoot (if there is a line of sight).  If shooting, monsters
+ *      get in the way.  Otherwise, they do not.
+ */
+
+bool
+straight_shot(ery, erx, eey, eex, shooting)
+register int ery, erx, eey, eex;
+register coord *shooting;
+{
+    register int dy, dx;        /* Deltas */
+    unsigned char ch;
+
+    /* Does the monster have a straight shot at prey */
+    if ((ery != eey) && (erx != eex) &&
+        (abs(ery - eey) != abs(erx - eex))) return(FALSE);
+
+    /* Get the direction to shoot */
+    if (eey > ery) dy = 1;
+    else if (eey == ery) dy = 0;
+    else dy = -1;
+
+    if (eex > erx) dx = 1;
+    else if (eex == erx) dx = 0;
+    else dx = -1;
+
+    /* Make sure we have free area all the way to the player */
+    ery += dy;
+    erx += dx;
+    while ((ery != eey) || (erx != eex)) {
+        switch (ch = winat(ery, erx)) {
+            case VERTWALL:
+            case HORZWALL:
+            case WALL:
+            case DOOR:
+            case SECRETDOOR:
+            case FOREST:
+                return(FALSE);
+            default:
+                if (shooting && isalpha(ch)) return(FALSE);
+        }
+        ery += dy;
+        erx += dx;
+    }
+
+    if (shooting) {     /* If we are shooting -- put in the directions */
+        shooting->y = dy;
+        shooting->x = dx;
+    }
+    return(TRUE);
+}
+