view xrogue/actions.c @ 314:2f0eb38da609

Advanced Rogue 7: fix a crash when casting magic missile. do_zap() dereferenced a struct object pointer while ignoring the result of a NULL check. XRogue had the same problem, though triggering it was unlikely, since XRogue does not include a magic missile spell. Reported by John Harris of @Play.
author John "Elwin" Edwards
date Thu, 21 Oct 2021 21:00:15 -0400
parents e52a8a7ad4c5
children
line wrap: on
line source

/*
    actions.c  -  functions for dealing with monster actions
   
    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"

int mf_count = 0;       /* move_free counter - see actions.c(m_act()) */
int mf_jmpcnt = 0;      /* move_free counter for # of jumps           */

void m_breathe(struct thing *tp);
void m_select(struct thing *th, bool flee);
void m_sonic(struct thing *tp);
void m_spell(struct thing *tp);
void m_summon(struct thing *tp);
bool m_use_it(struct thing *tp, bool flee, struct room *rer, struct room *ree);
bool m_use_pack(struct thing *monster, coord *defend_pos, int dist, 
                coord *shoot_dir);

/* 
 * Did we disrupt a spell? 
 */
void
dsrpt_monster(struct thing *tp, bool always, bool see_him)
{
    switch (tp->t_action) {
    case A_SUMMON:
    case A_MISSILE:
    case A_SLOW:
        tp->t_action = A_NIL;   /* Just make the old fellow start over again */
        tp->t_no_move = movement(tp);
        tp->t_using = NULL; /* Just to be on the safe side */
        turn_on(*tp, WASDISRUPTED);
        if (see_him)
            msg("%s's spell has been disrupted.",prname(monster_name(tp),TRUE));
        /*
         * maybe choose something else to do next time since player
         * is disrupting us
         */
        tp->t_summon *= 2;
        tp->t_cast /= 2;
        return;
    }

    /* We may want to disrupt other actions, too */
    if (always) {
        tp->t_action = A_NIL; /* Just make the old fellow start over again */
        tp->t_no_move = movement(tp);
        tp->t_using = NULL;/* Just to be on the safe side */
    }
}

void
dsrpt_player(void)
{
    int which, action;
    struct linked_list *item;
    struct object *obj;
    
    action = player.t_action;
    which = player.t_selection;

    switch (action) {
    case C_CAST: /* Did we disrupt a spell? */
    case C_PRAY:
    case C_CHANT:
    {
        msg("Your %s was disrupted!", action == C_CAST ? "spell" : "prayer");

        /* Charge him 1/4 anyway */
        if (action == C_CAST)
            spell_power += magic_spells[which].s_cost / 4;
        else if (action == C_PRAY)
            pray_time += cleric_spells[which].s_cost / 4;
        else if (action == C_CHANT)
            chant_time += druid_spells[which].s_cost / 4;
    }
    when C_COUNT: /* counting of gold? */
    {
        if (purse > 0) {
            msg("Your gold goes flying everywhere!");
            do {
                item = spec_item(GOLD, 0, 0, 0);
                obj = OBJPTR(item);
                obj->o_count = min(purse, rnd(20)+1);
                purse -= obj->o_count;
                obj->o_pos = hero;
                fall(item, FALSE);
            } while (purse > 0 && rnd(25) != 1);
        }
    }
    when C_EAT:
        msg("Ack!  You gag on your food for a moment. ");
        del_pack(player.t_using);
        
    when A_PICKUP:
        msg("You drop what you are picking up! ");

    when C_SEARCH:      /* searching for traps and secret doors... */
        msg("Ouch!  You decide to stop searching. ");
        count = 0;      /* don't search again */

    when C_SETTRAP:
        msg("Ouch!  You can't set a trap right now. ");

    when A_NIL:
    default:
        return;
    }
    player.t_no_move = movement(&player); /* disoriented for a while */
    player.t_action = A_NIL;
    player.t_selection = 0;
}

/*
 * m_act:
 *      If the critter isn't doing anything, choose an action for it.
 *      Otherwise, let it perform its chosen action.
 */

void
m_act(struct thing *tp)
{
    struct object *obj;
    bool flee;          /* Are we scared? */

    /* What are we planning to do? */
    switch (tp->t_action) {
        default:
            /* An unknown action! */
            msg("Unknown monster action (%d)", tp->t_action);

            /* Fall through */

        case A_NIL:
            /* If the monster is fairly intelligent and about to die, it
             * may turn tail and run.  But if we are a FRIENDLY creature
             * in the hero's service, don't run.
             */
            if (off(*tp, ISFLEE)                                        &&
                tp->t_stats.s_hpt < tp->maxstats.s_hpt                  &&
                tp->t_stats.s_hpt < max(10, tp->maxstats.s_hpt/6)       &&
                (off(*tp, ISFRIENDLY) || tp->t_dest != &hero)           &&
                rnd(25) < tp->t_stats.s_intel) {
                    turn_on(*tp, ISFLEE);

                    /* It is okay to turn tail */
                    tp->t_oldpos = tp->t_pos;
                }

            /* Should the monster run away? */
            flee = on(*tp, ISFLEE) ||
                ((tp->t_dest == &hero) && on(player, ISINWALL) &&
                 off(*tp, CANINWALL));

            m_select(tp, flee); /* Select an action */
            return;

        when A_ATTACK:
            /* 
             * We're trying to attack the player or monster at t_newpos 
             * if the prey moved, do nothing
             */
            obj = tp->t_using ? OBJPTR(tp->t_using) : NULL;
            if (ce(tp->t_newpos, hero)) {
                attack(tp, obj, FALSE);
            }
            else if (mvwinch(mw, tp->t_newpos.y, tp->t_newpos.x) &&
                     step_ok(tp->t_newpos.y, tp->t_newpos.x, FIGHTOK, tp)) {
                skirmish(tp, &tp->t_newpos, obj, FALSE);
            }

        when A_SELL:
                /* Is the quartermaster still next to us? */
            if (ce(tp->t_newpos, hero)) sell(tp);

                /* The quartermaster moved away */
            else if (off(player, ISBLIND) && cansee(unc(tp->t_pos)) &&
                (off(*tp, ISINVIS)     || on(player, CANSEE)) &&
                (off(*tp, ISSHADOW)    || on(player, CANSEE)) &&
                (off(*tp, CANSURPRISE) || ISWEARING(R_ALERT)) &&
        (rnd(12) < 4))
                msg("%s grunts with frustration",prname(monster_name(tp),TRUE));

        when A_MOVE:
            /* Let's try to move */
            do_chase(tp);

            /* If t_no_move > 0, we found that we have to fight! */
            if (tp->t_no_move > 0) return;

        when A_BREATHE:
            /* Breathe on the critter */
            m_breathe(tp);

        when A_SLOW:
            /* make him move slower */
            add_slow();
            turn_off(*tp, CANSLOW);

        when A_MISSILE:
            /* Start up a magic missile spell */
            m_spell(tp);

        when A_SONIC:
            /* Let out a sonic blast! */
            m_sonic(tp);

        when A_THROW:
            /* We're throwing something (like an arrow) */
            missile(tp->t_newpos.y, tp->t_newpos.x, tp->t_using, tp);

        when A_SUMMON:
            /* We're summoning help */
            m_summon(tp);

        when A_USERELIC:
            /* Use our relic */
            m_use_relic(tp);

        when A_USEWAND:
            /* use the wand we have */
            m_use_wand(tp);
    }

    /* Can we in fact move?  (we might have solidified in solid rock) */
    if (!step_ok(hero.y, hero.x, NOMONST, &player)) {

         if (move_free > 1) goto jump_over;   /* avoid messages */
         if (mf_count > 2)  goto jump_over;   /* limit messages */

         if (pstats.s_hpt < 1) {
         pstats.s_hpt = -1;
             msg("You have merged into the surroundings!  --More--");
             wait_for(' ');
             death(D_PETRIFY);
         }
         else {
             mf_count += 1;  /* count number of times we are here */
             pstats.s_hpt -= rnd(2)+1;
             if (pstats.s_hpt < 1) {
          pstats.s_hpt = -1;
                  msg("You have merged into the surroundings!  --More--");
                  wait_for(' ');
                  death(D_PETRIFY);
             }
         }
         switch (rnd(51)) {
             case 0: msg("Arrrggghhhhh!! ");
             when 5: msg("You can't move! "); 
             when 10: msg("You motion angrily! "); 
             when 15: msg("You feel so weird! ");
             when 20: msg("If only you could phase. ");
             when 25: msg("The rock maggots are closing in! ");
             when 30: msg("You wrench and wrench and wrench... ");
             when 35: msg("You wish you could teleport out of here! ");
             when 40: msg("Your feel your life force ebbing away... ");
             when 45: msg("You partially regain your senses. ");
             when 50: msg("The rock maggots have found you!!! "); 
             otherwise: pstats.s_hpt -= rnd(4)+1;
         }
         if (pstats.s_hpt < 1) {
          pstats.s_hpt = -1;
              msg("You lose the urge to live...  --More--");
              wait_for(' ');
              death(D_PETRIFY);
     }
        jump_over:
        mf_jmpcnt++;          /* count this jump */
        if (mf_jmpcnt > 9) {  /* take a few turns, then reset it */
            mf_jmpcnt = 0;
            mf_count  = 0;
        }
    }

    /* No action now */
    tp->t_action = A_NIL;
    tp->t_using = NULL;
}

/*
 * m_breathe:
 *      Breathe in the chosen direction.
 */

void
m_breathe(struct thing *tp)
{
    register int damage;
    register char *breath = NULL;

    damage = tp->t_stats.s_hpt;
    turn_off(*tp, CANSURPRISE);

    /* Will it breathe at random */
    if (on(*tp, CANBRANDOM)) {
        /* Turn off random breath */
        turn_off(*tp, CANBRANDOM);

        /* Select type of breath */
        switch (rnd(10)) {
            case 0: breath = "acid";
                    turn_on(*tp, NOACID);
            when 1: breath = "flame";
                    turn_on(*tp, NOFIRE);
            when 2: breath = "lightning bolt";
                    turn_on(*tp, NOBOLT);
            when 3: breath = "chlorine gas";
                    turn_on(*tp, NOGAS);
            when 4: breath = "ice";
                    turn_on(*tp, NOCOLD);
            when 5: breath = "nerve gas";
                    turn_on(*tp, NOPARALYZE);
            when 6: breath = "sleeping gas";
                    turn_on(*tp, NOSLEEP);
            when 7: breath = "slow gas";
                    turn_on(*tp, NOSLOW);
            when 8: breath = "confusion gas";
                    turn_on(*tp, ISCLEAR);
            when 9: breath = "fear gas";
                    turn_on(*tp, NOFEAR);
        }
    }

    /* Or can it breathe acid? */
    else if (on(*tp, CANBACID)) {
        turn_off(*tp, CANBACID);
        breath = "acid";
    }

    /* Or can it breathe fire */
    else if (on(*tp, CANBFIRE)) {
        turn_off(*tp, CANBFIRE);
        breath = "flame";
    }

    /* Or can it breathe electricity? */
    else if (on(*tp, CANBBOLT)) {
        turn_off(*tp, CANBBOLT);
        breath = "lightning bolt";
    }

    /* Or can it breathe gas? */
    else if (on(*tp, CANBGAS)) {
        turn_off(*tp, CANBGAS);
        breath = "chlorine gas";
    }

    /* Or can it breathe ice? */
    else if (on(*tp, CANBICE)) {
        turn_off(*tp, CANBICE);
        breath = "ice";
    }

    else if (on(*tp, CANBPGAS)) {
        turn_off(*tp, CANBPGAS);
        breath = "nerve gas";
    }

    /* can it breathe sleeping gas */
    else if (on(*tp, CANBSGAS)) {
        turn_off(*tp, CANBSGAS);
        breath = "sleeping gas";
    }

    /* can it breathe slow gas */
    else if (on(*tp, CANBSLGAS)) {
        turn_off(*tp, CANBSLGAS);
        breath = "slow gas";
    }

    /* can it breathe confusion gas */
    else if (on(*tp, CANBCGAS)) {
        turn_off(*tp, CANBCGAS);
        breath = "confusion gas";
    }

    /* can it breathe fear gas */
    else {
        turn_off(*tp, CANBFGAS);
        breath = "fear gas";
    }

    /* Now breathe */
    shoot_bolt(tp, tp->t_pos, tp->t_newpos, FALSE, 
                    tp->t_index, breath, damage);

    running = FALSE;
    if (fight_flush) flushinp();
}

/*
 * m_select:
 *      Select an action for the monster.
 * flee: True if running away or player is inaccessible in wall
 */

void
m_select(struct thing *th, bool flee)
{
    register struct room *rer, *ree;    /* room of chaser, room of chasee */
    int dist = INT_MIN;
    int mindist = INT_MAX, maxdist = INT_MIN;
    bool rundoor;                       /* TRUE means run to a door */
    char sch;
    coord *last_door=0,                 /* Door we just came from */
           this;                        /* Temporary destination for chaser */

    rer = roomin(&th->t_pos);   /* Find room of chaser */
    ree = roomin(th->t_dest);   /* Find room of chasee */

    /* First see if we want to use an ability or weapon */
    if (m_use_it(th, flee, rer, ree)) return;

    /*
     * We don't count monsters on doors as inside rooms here because when
     * a monster is in a room and the player is not in that room, the
     * monster looks for the best door out.  If we counted doors as part
     * of the room, the monster would already be on the best door out;
     * so he would never move.
     */
    sch = mvwinch(stdscr, th->t_pos.y, th->t_pos.x) & A_CHARTEXT;
    if (sch == DOOR || sch == SECRETDOOR || sch == PASSAGE) {
        rer = NULL;
    }
    this = *th->t_dest;

    /*
     * If we are in a room heading for the player and the player is not
     * in the room with us, we run to the "best" door.
     * If we are in a room fleeing from the player, then we run to the
     * "best" door if he IS in the same room.
     *
     * Note:  We don't bother with doors in mazes or if we can walk
     * through walls.
     */
    if (rer != NULL && levtype != MAZELEV && off(*th, CANINWALL)) {
        if (flee) rundoor = (rer == ree);
        else rundoor = (rer != ree);
    }
    else rundoor = FALSE;

    if (rundoor) {
        register struct linked_list *exitptr;   /* For looping through exits */
        coord *exit,                            /* A particular door */
              *entrance;                        /* Place just inside doorway */
        int exity, exitx;                       /* Door's coordinates */
        char dch='\0';                          /* Door character */

        if ((th->t_doorgoal.x != -1) && (th->t_doorgoal.y != -1))
            dch = mvwinch(stdscr, th->t_doorgoal.y, th->t_doorgoal.x) & 
                    A_CHARTEXT;
            
        /* Do we have a valid goal? */
        if ((dch == PASSAGE || dch == DOOR) &&  /* A real door */
            (!flee || !ce(th->t_doorgoal, *th->t_dest))) { /* Prey should not
                                                             * be at door if
                                                             * we are running
                                                             * away
                                                             */
            /* Make sure the player is not in the doorway, either */
            entrance = doorway(rer, &th->t_doorgoal);
            if (!flee || entrance == NULL || !ce(*entrance, *th->t_dest)) {
                this = th->t_doorgoal;
                dist = 0;       /* Indicate that we have our door */
            }
        }

        /* Go through all the doors */
        else for (exitptr = rer->r_exit; exitptr; exitptr = next(exitptr)) {
            exit = DOORPTR(exitptr);
            exity = exit->y;
            exitx = exit->x;

            /* Make sure it is a real door */
            dch = mvwinch(stdscr, exity, exitx) & A_CHARTEXT;
            if (dch == PASSAGE || dch == DOOR) {
                /* Don't count a door if we are fleeing from someone and
                 * he is standing on it.  Also, don't count it if he is
                 * standing in the doorway.
                 */
                if (flee) {
                    if (ce(*exit, *th->t_dest)) continue;

                    entrance = doorway(rer, exit);
                    if (entrance != NULL && ce(*entrance, *th->t_dest))
                        continue;
                }
                
                /* Were we just on this door? */
                if (ce(*exit, th->t_oldpos)) last_door = exit;

                else {
                    dist = DISTANCE(th->t_dest->y, th->t_dest->x, exity, exitx);

                    /* If fleeing, we want to maximize distance from door to
                     * what we flee, and minimize distance from door to us.
                     */
                    if (flee)
                       dist -= DISTANCE(th->t_pos.y, th->t_pos.x, exity, exitx);

                    /* Maximize distance if fleeing, otherwise minimize it */
                    if ((flee && (dist > maxdist)) ||
                        (!flee && (dist < mindist))) {
                        th->t_doorgoal = *exit;  /* Use this door */
                        this = *exit;
                        mindist = maxdist = dist;
                    }
                }
            }
        }

        /* Could we not find a door? */
        if (dist == INT_MIN) {
            /* If we were on a door, go ahead and use it */
            if (last_door) {
                th->t_doorgoal = *last_door;
                this = th->t_oldpos;
                dist = 0;       /* Indicate that we found a door */
            }
            else th->t_doorgoal.x = th->t_doorgoal.y = -1; /* No more door goal */
        }

        /* Indicate that we do not want to flee from the door */
        if (dist != INT_MIN) flee = FALSE;
    }
    else th->t_doorgoal.x = th->t_doorgoal.y = -1;    /* Not going to any door */

    /* Now select someplace to go and start the action */
    chase(th, &this, rer, ree, flee);
}

/*
 * m_sonic:
 *      The monster is sounding a sonic blast.
 */

void
m_sonic(struct thing *tp)
{
    register int damage;
    struct object blast =
    {
        MISSILE, {0, 0}, 0, "", "150" , NULL, 0, 0, 0, 0
    };

    turn_off(*tp, CANSONIC);
    turn_off(*tp, CANSURPRISE);
    do_motion(&blast, tp->t_newpos.y, tp->t_newpos.x, tp);
    damage = rnd(61)+40;
    if (save(VS_BREATH, &player, -3))
        damage /= 2;
    msg ("%s's ultra-sonic blast hits you", prname(monster_name(tp), TRUE));
    if ((pstats.s_hpt -= damage) <= 0) {
    pstats.s_hpt = -1;
        death(tp->t_index);
    }
    running = FALSE;
    if (fight_flush) flushinp();
    dsrpt_player();
}

/*
 * m_spell:
 *      The monster casts a spell.  Currently this is limited to
 *      magic missile.
 */
void
m_spell(struct thing *tp)
{
    struct object missile =
    {
        MISSILE, {0, 0}, 0, "", "0d4 " , NULL, 0, WS_MISSILE, 100, 1
    };

    sprintf(missile.o_hurldmg, "%dd4", tp->t_stats.s_lvl);
    do_motion(&missile, tp->t_newpos.y, tp->t_newpos.x, tp);
    hit_monster(unc(missile.o_pos), &missile, tp);
    turn_off(*tp, CANMISSILE);
    turn_off(*tp, CANSURPRISE);

    running = FALSE;
    if (fight_flush) flushinp();
}

/*
 * m_summon:
 *      Summon aid.
 */

void
m_summon(struct thing *tp)
{
    register char *helpname, *mname;
    int fail, numsum;
    register int which, i;

    /* Let's make sure our prey is still here */
    if (!cansee(unc(tp->t_pos)) || fallpos(&hero, FALSE, 2) == NULL) return;

    /*
     * Non-uniques can only summon once.  Uniques get fewer
     * creatures with each successive summoning. Also, the
     * probability of summoning goes down
     */
    if (off(*tp, ISUNIQUE))
            turn_off(*tp, CANSUMMON);

    turn_off(*tp, CANSURPRISE);
    mname = monster_name(tp);
    helpname = monsters[tp->t_index].m_typesum;
    which = findmindex(helpname);

    if ((off(*tp, ISINVIS)     || on(player, CANSEE)) &&
        (off(*tp, ISSHADOW)    || on(player, CANSEE)) &&
        (off(*tp, CANSURPRISE) || ISWEARING(R_ALERT))) {
        if (monsters[which].m_normal == FALSE) { /* genocided? */
            msg("%s appears dismayed", prname(mname, TRUE));
            monsters[tp->t_index].m_numsum = 0;
        }
        else {
            msg("%s summons %ss for help", prname(mname, TRUE), helpname);
        }
    }
    else {
        if (monsters[which].m_normal == FALSE) /* genocided? */
            monsters[tp->t_index].m_numsum = 0;
        else {
            msg("%ss seem to appear from nowhere!", helpname);
        }
    }
    numsum = monsters[tp->t_index].m_numsum;
    if (numsum && on(*tp, ISUNIQUE)) {   /* UNIQUEs summon less each time */
        monsters[tp->t_index].m_numsum--; 
        tp->t_summon *= 2; /* cut probability in half */
    }

    /*
     * try to make all the creatures around player but remember
     * if unsuccessful
     */
    for (i=0, fail=0; i<numsum; i++) {
         if (!creat_mons(&player, which, FALSE))
             fail++;    /* remember the failures */
    }

    /*
     * try once again to make the buggers
     */
    for (i=0; i<fail; i++)
         creat_mons(tp, which, FALSE);
    
    /* Now let the poor fellow see all the trouble */
    light(&hero);
    turn_on(*tp, HASSUMMONED);
}

/*
 * m_use_it:
 *      See if the monster (tp) has anything useful it can do
 *      (ie. an ability or a weapon) other than just move.
 */

bool
m_use_it(struct thing *tp, bool flee, struct room *rer, struct room *ree)
{
    int dist;
    register coord *ee = tp->t_dest, *er = &tp->t_pos; 
    coord *shoot_dir = NULL;
    coord straight_dir;
    int   straight_shot = FALSE;
    struct thing *prey;
    bool dest_player;   /* Are we after the player? */

    /*
     * If we are fleeing, there's a chance, depending on our
     * intelligence, that we'll just run in terror.
     */
    if (flee && rnd(25) >= tp->t_stats.s_intel) return(FALSE);

    /*
     * Make sure that we have a living destination, and record whether
     * it is the player.
     */
    if (ee != NULL) {
        if (ce(*ee, hero)) {
            dest_player = TRUE;
            prey = &player;
        }
        else {
            struct linked_list *item;

            dest_player = FALSE;

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

    /*
     * If we are friendly to the hero, we don't do anything.
     */
    if (on(*tp, ISFRIENDLY) && dest_player) return(FALSE);

    /*
     * Also, for now, if our prey is in a wall, we won't do
     * anything.  The prey must be in the same room as we are OR
     * we must have a straight shot at him.  Note that
     * shoot_dir must get set before rer is checked so
     * that we get a valid value.
     */
 
    if (can_shoot(er, ee, &straight_dir) == 0)
        shoot_dir = &straight_dir;
    else
        shoot_dir = NULL;

    if (on(*prey, ISINWALL) ||
        ( (shoot_dir == NULL) && (rer == NULL || rer != ree)))
        return(FALSE);

    /*
     * If we can't see the prey then forget it
     */
    if (on(*prey, ISINVIS) && off(*tp, CANSEE))
        return(FALSE);

    /* How far are we from our prey? */
    dist = DISTANCE(er->y, er->x, ee->y, ee->x);

    /* 
     * Shall we summon aid so we don't have to get our hands dirty? 
     * For now, we will only summon aid against the player.
     * We'll wait until he's within 2 dots of a missile length.
     */
    if (on(*tp, CANSUMMON) && dest_player                       &&
        dist < (BOLT_LENGTH+2)*(BOLT_LENGTH+2)                  &&
        rnd(tp->t_summon) < tp->t_stats.s_lvl                   &&
        monsters[tp->t_index].m_numsum > 0                      &&
        fallpos(&hero, FALSE, 2) != NULL) {
        tp->t_action = A_SUMMON;        /* We're going to summon help */
        tp->t_no_move = movement(tp); /* It takes time! */
        return(TRUE);
    }

    /*
     * If the creature can cast a slow spell and if the prey is within
     * 2 dots of a missile fire, then see whether we will cast it.
     * if next to player, lessen chance because we don't like being
     * disrupted
     */
    if (on(*tp, CANSLOW) && dest_player                 && 
        dist < (BOLT_LENGTH+5)*(BOLT_LENGTH+5)          &&
        rnd(100) < (dist > 3 ? tp->t_cast : tp->t_cast/2)) {
            tp->t_action = A_SLOW;              /* We're going to slow him */
            tp->t_no_move = 3 * movement(tp);   /* Takes time! */
            debug("casting slow spell!");
            return(TRUE);
    }

    /*
     * If we have a special magic item, we might use it.  We will restrict
     * this options to uniques with relics and creatures with wands for now.  
     * Also check for the quartermaster. Don't want him shooting wands....
     */
    if ((on(*tp, ISUNIQUE) || on(*tp, CARRYSTICK)) && 
        off(*tp, CANSELL) && dest_player           &&
        m_use_pack(tp, ee, dist, shoot_dir)) {
            return(TRUE);
    }

    /* From now on, we must have a direct shot at the prey */
    if (!straight_shot) return(FALSE);

    /* We may use a sonic blast if we can, only on the player */
    if (on(*tp, CANSONIC)               && 
        dest_player                     &&
        (dist < BOLT_LENGTH*2)          &&
        (rnd(100) < tp->t_breathe)) {