view xrogue/scrolls.c @ 264:778938a5c21d

UltraRogue: add location for character files. When using the -n option, UltraRogue will look for character files in a single location, similar to save files. The location is chosen by defining CHRDIR in getplay.c, at least until UltraRogue gets integrated with the build systems.
author John "Elwin" Edwards
date Sun, 19 Feb 2017 19:47:09 -0500
parents f54901b9c39b
children e52a8a7ad4c5
line wrap: on
line source

/*
    scrolls.c - Functions for dealing with scrolls

    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 <stdlib.h>
#include <string.h>
#include <curses.h>
#include <ctype.h>
#include "rogue.h"

/*
 * let the hero get rid of some type of monster 
 */

void
genocide(void)
{
    register struct linked_list *ip;
    register struct thing *mp;
    register struct linked_list *nip;
                             /* cannot genocide any uniques */
    register int num_monst = NUMMONST-NUMUNIQUE-NUMDINOS;
    register int which_monst;

    which_monst = makemonster(FALSE, "wipe out");
    if (which_monst <= 0 || which_monst >= num_monst) {
        msg("");
        return;
    }

    /* Remove this monster from the present level */
    for (ip = mlist; ip; ip = nip) {
        mp = THINGPTR(ip);
        nip = next(ip);
        if (mp->t_index == which_monst) {
            killed(ip, FALSE, FALSE, TRUE);
        }
    }

    /* Remove from available monsters */
    monsters[which_monst].m_normal = FALSE;
    monsters[which_monst].m_wander = FALSE;
    mpos = 0;
    msg("You have wiped out the %s.", monsters[which_monst].m_name);
}

void
read_scroll(int which, int flag, bool is_scroll)
{
    register struct object *obj = NULL, *nobj;
    register struct linked_list *item, *nitem;
    register int i,j;
    register unsigned char ch, nch;
    bool cursed, blessed;

    blessed = FALSE;
    cursed = FALSE;
    item = NULL;

    if (which < 0) {
        if (on(player, ISBLIND)) {
            msg("You can't see to read anything!");
            return;
        }
        if (on(player, ISINWALL)) {
            msg("You can't see the scroll while inside rock!");
            return;
        }

        /* This is a scroll or book. */
        if (player.t_action != C_READ) {
            int units;

            item = get_item(pack, "read", READABLE, FALSE, FALSE);

            /*
             * Make certain that it is somethings that we want to read
             */
            if (item == NULL)
                return;

            /* How long does it take to read? */
            units = usage_time(item);
            if (units < 0) return;

            player.t_using = item;      /* Remember what it is */
            player.t_no_move = units * movement(&player);
            if ((OBJPTR(item))->o_type == SCROLL) player.t_action = C_READ;
            else player.t_action = C_USE;
            return;
        }

        /* We have waited our time, let's quaff the potion */
        item = player.t_using;
        player.t_using = NULL;
        player.t_action = A_NIL;

        obj = OBJPTR(item);
        /* remove it from the pack */
        inpack--;
        detach(pack, item);

        msg("As you read the scroll, it vanishes.");
        cursed = obj->o_flags & ISCURSED;
        blessed = obj->o_flags & ISBLESSED;

        which = obj->o_which;
    }
    else {
        cursed = flag & ISCURSED;
        blessed = flag & ISBLESSED;
    }

    switch (which) {
        case S_CONFUSE: /* Scroll of monster confusion. Give him that power. */
    {
        register char *str;

        switch (rnd(5)) {
        case 0:
            str = "glow red";
        when 1:
            str = "vibrate";
        when 2:
            str = "glow blue";
        when 3:
            str = "radiate green";
        otherwise:
            str = "itch with a strange desire";
        }
            msg("Your hands begin to %s. ", str);
            turn_on(player, CANHUH);
    }
        when S_CURING:
            /*
             * A cure disease spell
             */
            if (on(player, HASINFEST) || 
                on(player, HASDISEASE)|| 
                on(player, DOROT)) {
                if (on(player, HASDISEASE)) {
                    extinguish(cure_disease);
                    cure_disease();
                }
                if (on(player, HASINFEST)) {
                    msg(terse ? "You feel yourself improving."
                              : "You begin to feel yourself improving.");
                    turn_off(player, HASINFEST);
                    infest_dam = 0;
                }
                if (on(player, DOROT)) {
                    msg("You feel your skin returning to normal.");
                    turn_off(player, DOROT);
                }
            }
            else {
                /* msg(nothing); */
                break;
            }
            if (is_scroll) s_know[S_CURING] = TRUE;
        when S_LIGHT:
            if (blue_light(blessed, cursed) && is_scroll)
                s_know[S_LIGHT] = TRUE;
        when S_HOLD:
            if (cursed) {
                /*
                 * This scroll aggravates all the monsters on the current
                 * level and sets them running towards the hero
                 */
                msg("You hear a high-pitched humming noise.");
                /* protect good charactors */
                if (player.t_ctype == C_PALADIN ||
                    player.t_ctype == C_RANGER  || player.t_ctype == C_MONK) {
                        msg("A chill runs up your spine! ");
                        aggravate(TRUE, FALSE);
                }
                else {
                    aggravate(TRUE, TRUE);
                }
            }
            else if (blessed) { /* Hold all monsters on level */
                if (mlist == NULL) msg(nothing);
                else {
                    register struct linked_list *mon;
                    register struct thing *th;

                    for (mon = mlist; mon != NULL; mon = next(mon)) {
                        th = THINGPTR(mon);
                        turn_off(*th, ISRUN);
                        turn_on(*th, ISHELD);
                        turn_off(*th, ISCHARMED);
                    }
                    if (levtype == OUTSIDE)
                        msg("A sudden peace comes over the land.. ");
                    else
                        msg("A sudden peace comes over the dungeon.. ");
                }
            }
            else {
                /*
                 * Hold monster scroll.  Stop all monsters within two spaces
                 * from chasing after the hero.
                 */
                    register int x,y;
                    register struct linked_list *mon;
                    bool gotone=FALSE;

                    for (x = hero.x-2; x <= hero.x+2; x++) {
                        for (y = hero.y-2; y <= hero.y+2; y++) {
                            if (y < 1 || x < 0 || y > lines - 3 || x > cols - 1)
                                continue;
                            if (isalpha(mvwinch(mw, y, x))) {
                                if ((mon = find_mons(y, x)) != NULL) {
                                    register struct thing *th;

                                    gotone = TRUE;
                                    th = THINGPTR(mon);
                                    turn_off(*th, ISRUN);
                                    turn_on(*th, ISHELD);
                                    turn_off(*th, ISCHARMED);
                                }
                            }
                        }
                    }
                    if (gotone) msg("A sudden peace surrounds you.");
                    else msg(nothing);
            }
        when S_SLEEP:
            /*
             * if cursed, you fall asleep
             */
            if (is_scroll) s_know[S_SLEEP] = TRUE;
            if (cursed) {
                if (ISWEARING(R_ALERT))
                    msg("You feel drowsy for a moment.");
                else {
                    msg("You fall asleep.");
                    player.t_no_move += movement(&player)*(4 + rnd(SLEEPTIME));
                    player.t_action = A_FREEZE;
                }
            }
            else {
                /*
                 * sleep monster scroll.  
                 * puts all monsters within 2 spaces asleep
                 */
                    register int x,y;
                    register struct linked_list *mon;
                    bool gotone=FALSE;

                    for (x = hero.x-2; x <= hero.x+2; x++) {
                        for (y = hero.y-2; y <= hero.y+2; y++) {
                            if (y < 1 || x < 0 || y > lines - 3 || x > cols - 1)
                                continue;
                            if (isalpha(mvwinch(mw, y, x))) {
                                if ((mon = find_mons(y, x)) != NULL) {
                                    register struct thing *th;

                                    th = THINGPTR(mon);
                                    if (on(*th, ISUNDEAD))
                                        continue;
                                    th->t_no_move += movement(th)*(SLEEPTIME+4);
                                    th->t_action = A_FREEZE;
                                    gotone = TRUE;
                                }
                            }
                        }
                    }
                    if (gotone) 
                        msg("The monster(s) around you seem to have fallen asleep!");
                    else 
                        msg(nothing);
            }
        when S_CREATE:
            /*
             * Create a monster
             * First look in a circle around him, next try his room
             * otherwise give up
             */
            creat_mons(&player, (short) 0, TRUE);
            light(&hero);
        when S_IDENT:
            /* 
             * if its blessed then identify everything in the pack
             */
            if (blessed) {
                msg("You feel more Knowledgeable!");
                idenpack();
            }
            else {
                /*
                 * Identify, let the rogue figure something out
                 */
                if (is_scroll && s_know[S_IDENT] != TRUE) {
                    msg("This scroll is an identify scroll");
                }
                whatis((struct linked_list *)NULL);
            }
            if (is_scroll) s_know[S_IDENT] = TRUE;
        when S_MAP:
            /*
             * Scroll of magic mapping.
             */
            if (blessed) {
                register int i;

                if (is_scroll && s_know[S_MAP] != TRUE)
                    s_know[S_MAP] = TRUE;
                  /* light rooms */
                for (i=0; i<MAXROOMS; i++){
                    rooms[i].r_flags &= ~ISDARK;
                }

                msg("This scroll has a very detailed map on it!  --More--");
                wait_for(' ');
                overwrite(stdscr, hw);
                overlay(stdscr, cw);    /* wizard CTRL(F) */
                overlay(mw, cw);        /* wizard CTRL(X) */
                draw(cw);
                goto map_jump;          /* skip over regular mapping routine */
            }
            rmmsg();
            overwrite(stdscr, hw);
            /*
             * Take all the things we want to keep hidden out of the window
             */
            for (i = 1; i < lines-2; i++)
                for (j = 0; j < cols; j++)
                {
                    switch (nch = ch = mvwinch(hw, i, j))
                    {
                        case SECRETDOOR:
                            nch = secretdoor (i, j);
                            break;
                        case HORZWALL:
                        case VERTWALL:
                        case DOOR:
                        case PASSAGE:
                        case ' ':
                        case STAIRS:
                            if (mvwinch(mw, i, j) != ' ')
                            {
                                register struct thing *it;

                                it = THINGPTR(find_mons(i, j));
                                if (it && it->t_oldch == ' ')
                                    it->t_oldch = nch;
                            }
                            break;
                        default:
                            nch = ' ';
                    }
                    if (nch != ch)
                        waddch(hw, nch);
                }
            /*
             * Copy in what he has discovered
             */
            overlay(cw, hw);
            /*
             * And set up for display
             */
            overwrite(hw, cw);
            draw(cw);
            if (is_scroll && s_know[S_MAP] != TRUE) {
                msg("Oh, now this scroll has a map on it.");
                s_know[S_MAP] = TRUE;
            }
            map_jump:           /* blessed map jump from above */
        when S_GFIND:
            /*
             * Scroll of gold detection
             */
            {
                int gtotal = 0;

                if (is_scroll) s_know[S_GFIND] = TRUE;
                wclear(hw);
                for (nitem = lvl_obj; nitem != NULL; nitem = next(nitem)) {
                    nobj = OBJPTR(nitem);
                    if (nobj->o_type == GOLD) {
                        gtotal += nobj->o_count;
                        mvwaddch(hw, nobj->o_pos.y, nobj->o_pos.x, GOLD);
                    }
                }
                for (nitem = mlist; nitem != NULL; nitem = next(nitem)) {
                    register struct linked_list *gitem;
                    register struct thing *th;

                    th = THINGPTR(nitem);
                    if (on(*th, NODETECT)) continue;
                    for(gitem = th->t_pack; gitem != NULL; gitem = next(gitem)){
                        nobj = OBJPTR(gitem);
                        if (nobj->o_type == GOLD) {
                            gtotal += nobj->o_count;
                            mvwaddch(hw, th->t_pos.y, th->t_pos.x, GOLD);
                        }
                    }
                }
                if (gtotal) {
                    rmmsg();
                    overlay(hw,cw);
                    draw(cw);
                    msg("You begin to feel greedy.  You sense gold!");
                    break;
                }
            }
            msg("You begin to feel a pull downward..");
        when S_TELEP:
            /*
             * Scroll of teleportation:
             * Make him disappear and reappear
             */
            if (cursed) {
                int old_max = cur_max;

                turns = (vlevel * NLEVMONS) * LEVEL;
                /* if (turns > 42000) turns = 42000; limit turns */
                debug ("vlevel = %d  turns = %d", vlevel, turns);

                level = rnd(201)+80;   /* cursed teleport range */

                new_level(NORMLEV);
                msg("You are banished to the lower regions! ");

                status(TRUE);
                mpos = 0;
                if (old_max == cur_max) { /* if he's been here make it harder */
                /* protect good charactors */
                if (player.t_ctype == C_PALADIN ||
                    player.t_ctype == C_RANGER  || player.t_ctype == C_MONK) {
                        aggravate(TRUE, FALSE);
                }
                else {
                    aggravate(TRUE, TRUE);
                }
                }
            }
            else if (blessed) {
                int     old_level, 
                        much = rnd(6) - 7;

                old_level = level;
                if (much != 0) {
                    level += much;
                    if (level < 1)
                        level = 1;
                    mpos = 0;
                    cur_max = level;
                    turns += much*LEVEL;
                    if (turns < 0)
                        turns = 0;
                    new_level(NORMLEV);         /* change levels */
                    if (level == old_level)
                        status(TRUE);
                    msg("You are whisked away to another region!");
                }
            }
            else {
                teleport();
            }
            if (is_scroll) s_know[S_TELEP] = TRUE;
        when S_SCARE:
            /*
             * A monster will refuse to step on a scare monster scroll
             * if it is dropped.  Thus reading it is a mistake and produces
             * laughter at the poor rogue's boo boo.
             */
            msg("You hear maniacal laughter in the distance.");
        when S_REMOVE:
            if (cursed) { /* curse all player's possessions */
                for (nitem = pack; nitem != NULL; nitem = next(nitem)) {
                    nobj = OBJPTR(nitem);
                    if (nobj->o_flags & ISBLESSED) 
                        nobj->o_flags &= ~ISBLESSED;
                    else 
                        nobj->o_flags |= ISCURSED;
                }
                msg("The smell of fire and brimstone fills the air!");
        /* return; leaks item, go through end of function */
            }
            else if (blessed) {
                for (nitem = pack; nitem != NULL; nitem = next(nitem)) {
                    nobj = OBJPTR(nitem);
                    nobj->o_flags &= ~ISCURSED;
                }
                msg("Your pack glistens brightly!");
                do_panic(0);         /* this startles them */
        /* return; leaks item, go through end of function */
            }
            else {
                nitem = get_item(pack, "remove the curse on",ALL,FALSE,FALSE);
                if (nitem != NULL) {
                    nobj = OBJPTR(nitem);
                    nobj->o_flags &= ~ISCURSED;
                    msg("Removed the curse from %s",inv_name(nobj,TRUE));
                }
            }
            if (is_scroll) s_know[S_REMOVE] = TRUE;
        when S_PETRIFY:
            switch (mvinch(hero.y, hero.x)) {
                case WORMHOLE:
                case TRAPDOOR:
                case DARTTRAP:
                case TELTRAP:
                case ARROWTRAP:
                case SLEEPTRAP:
                case BEARTRAP:
                    {
                        register int i;

                        /* Find the right trap */
                        for (i=0; i<ntraps && !ce(traps[i].tr_pos, hero); i++);
                        ntraps--;

                        if (!ce(traps[i].tr_pos, hero))
                            msg("What a strange trap!");
                        else {
                            while (i < ntraps) {
                                traps[i] = traps[i + 1];
                                i++;
                            }
                        }
                    }
                    goto pet_message;
                case DOOR:
                case SECRETDOOR:
                case FLOOR:
                case PASSAGE:
pet_message:        msg("The dungeon begins to rumble and shake!");
                    addch(WALL);

                    /* If the player is phased, unphase him */
                    if (on(player, CANINWALL)) {
                        extinguish(unphase);
                        turn_off(player, CANINWALL);
                        msg("The dizzy feeling leaves you.");
                    }

                    /* Mark the player as in a wall */
                    turn_on(player, ISINWALL);
                    break;
                default:
                    msg(nothing);
            }
        when S_GENOCIDE:
            msg("You have been granted the boon of genocide!  --More--");
            wait_for(' ');
            msg("");
            genocide();
            if (is_scroll) s_know[S_GENOCIDE] = TRUE;
        when S_PROTECT: {
            struct linked_list *ll;
            struct object *lb;
            bool did_it = FALSE;
            msg("You are granted the power of protection.");
            if ((ll=get_item(pack,"protect",PROTECTABLE,FALSE,FALSE)) != NULL) {
                lb = OBJPTR(ll);
                mpos = 0;
                if (cursed) {
                    switch(lb->o_type) {        /* ruin it completely */
                        case RING: if (lb->o_ac > 0) {
                                    if (is_current(lb)) {
                                        switch (lb->o_which) {
                                            case R_ADDWISDOM:
                                                pstats.s_wisdom -= lb->o_ac;
                                            when R_ADDINTEL:  
                                                pstats.s_intel -= lb->o_ac;
                                            when R_ADDSTR:
                                                pstats.s_str -= lb->o_ac;
                                            when R_ADDHIT:
                                                pstats.s_dext -= lb->o_ac;
                                        }
                                    }
                                    did_it = TRUE;
                                        lb->o_ac = 0;
                                }
                        when ARMOR: if (lb->o_ac > 10) {
                                        did_it = TRUE;
                                        lb->o_ac = 10;
                                    }
                        when STICK: if (lb->o_charges > 0) {
                                        did_it = TRUE;
                                        lb->o_charges = 0;
                                    }
                        when WEAPON:if (lb->o_hplus > 0) {
                                        did_it = TRUE;
                                        lb->o_hplus = 0;
                                    }
                                    if (lb->o_dplus > 0) {
                                        did_it = TRUE;
                                        lb->o_dplus = 0;
                                    }
                    }
                    if (lb->o_flags & ISPROT) {
                        did_it = TRUE;
                        lb->o_flags &= ~ISPROT;
                    }
                    if (lb->o_flags & ISBLESSED) {
                        did_it = TRUE;
                        lb->o_flags &= ~ISBLESSED;
                    }
                    if (did_it)
                        msg("Your %s glows red for a moment",inv_name(lb,TRUE));
                    else {
                        msg(nothing);
                        break;
                    }
                }
                else  {
                    lb->o_flags |= ISPROT;
                    msg("Protected %s.",inv_name(lb,TRUE));
                }
            }
            if (is_scroll) s_know[S_PROTECT] = TRUE;
        }
        when S_MAKEIT:
            msg("You have been endowed with the power of creation!");
            if (is_scroll) s_know[S_MAKEIT] = TRUE;
            create_obj(TRUE, 0, 0);
        when S_ALLENCH: {
            struct linked_list *ll;
            struct object *lb;
            int howmuch, flags;
            if (is_scroll && s_know[S_ALLENCH] == FALSE) {
                msg("You are granted the power of enchantment.");
                msg("You may enchant anything (weapon, ring, armor, scroll, potion)");
            }
            if ((ll = get_item(pack, "enchant", ALL, FALSE, FALSE)) != NULL) {
                lb = OBJPTR(ll);
                lb->o_flags &= ~ISCURSED;
                if (blessed) {
                    howmuch = 2;
                    flags = ISBLESSED;
                }
                else if (cursed) {
                    howmuch = -1;
                    flags = ISCURSED;
                }
                else {
                    howmuch = 1;
                    flags = ISBLESSED;
                }
                switch(lb->o_type) {
                    case RING:
                        if (lb->o_ac + howmuch > MAXENCHANT) {
                            msg("The enchantment doesn't seem to work!");
                            break;
                        }
                        lb->o_ac += howmuch;
                        if (lb==cur_ring[LEFT_1]  || lb==cur_ring[LEFT_2]  ||
                            lb==cur_ring[LEFT_3]  || lb==cur_ring[LEFT_4]  ||
                            lb==cur_ring[RIGHT_1] || lb==cur_ring[RIGHT_2] ||
                lb==cur_ring[RIGHT_3] || lb==cur_ring[RIGHT_4]) {
                            switch (lb->o_which) {
                                case R_ADDWISDOM: pstats.s_wisdom += howmuch;
                                when R_ADDINTEL:  pstats.s_intel  += howmuch;
                                when R_ADDSTR:    pstats.s_str    += howmuch;
                                when R_ADDHIT:    pstats.s_dext   += howmuch;
                            }
                        }
                        msg("Enchanted %s.",inv_name(lb,TRUE));
                    when ARMOR:
                        if ((armors[lb->o_which].a_class - lb->o_ac) +
                            howmuch > MAXENCHANT) {
                            msg("The enchantment doesn't seem to work!");
                            break;
                        }
                        else
                            lb->o_ac -= howmuch;
                        msg("Enchanted %s.",inv_name(lb,TRUE));
                    when STICK:
                        lb->o_charges += rnd(16)+10;
                        if (lb->o_charges < 0)
                            lb->o_charges = 0;
                        if (EQUAL(ws_type[lb->o_which], "staff")) {
                            if (lb->o_charges > 200) 
                                lb->o_charges = 200;
                        }
                        else {
                            if (lb->o_charges > 200)  /* make em the same */
                                lb->o_charges = 200;
                        }
                        msg("Enchanted %s.",inv_name(lb,TRUE));
                    when WEAPON:
                        if(lb->o_hplus+lb->o_dplus+howmuch > MAXENCHANT * 2){
                            msg("The enchantment doesn't seem to work!");
                            break;
                        }
                        if (rnd(100) < 50)
                            lb->o_hplus += howmuch;
                        else
                            lb->o_dplus += howmuch;
                        msg("Enchanted %s.",inv_name(lb,TRUE));
                    when MM:
                        switch (lb->o_which) {
                            case MM_BRACERS:
                                if (lb->o_ac + howmuch > MAXENCHANT) {
                                   msg("The enchantment doesn't seem to work!");
                                   break;
                                }
                                else lb->o_ac += howmuch;
                                msg("Enchanted %s.",inv_name(lb,TRUE));
                            when MM_PROTECT:
                                if (lb->o_ac + howmuch > MAXENCHANT) {
                                   msg("The enchantment doesn't seem to work!");
                                   break;
                                }
                                else lb->o_ac += howmuch;
                                msg("Enchanted %s.",inv_name(lb,TRUE));
                        }
                        lb->o_flags |= flags;
                    when POTION:
                    case SCROLL:
                    default:
                        lb->o_flags |= flags;
                    msg("Enchanted %s.",inv_name(lb,TRUE));
                }
            }
            if (is_scroll) s_know[S_ALLENCH] = TRUE;

            /* If gotten here via prayer or Ankh, dock his wisdom. */
            if (!is_scroll) {
                pstats.s_wisdom--;
        if (pstats.s_wisdom < 3) pstats.s_wisdom = 3;
                msg("You feel a drain on your system. ");
            }
        }
        when S_FINDTRAPS:
            for (i=0; i<ntraps; i++) {
                if (!(traps[i].tr_flags & ISFOUND)) {
                    traps[i].tr_flags |= ISFOUND;
                    if (cansee(traps[i].tr_pos.y, traps[i].tr_pos.x))
                        mvwaddch(cw,traps[i].tr_pos.y,traps[i].tr_pos.x,
                                 traps[i].tr_type);
                }
            }
            if (ntraps > 0) {
                msg("You sense the presence of traps.");
                if (is_scroll) s_know[S_FINDTRAPS] = TRUE;
            }
            else
                msg(nothing);

        when S_RUNES:
        {
                register struct linked_list *sitem;

                msg("The scroll explodes in a ball of fire!");
                if (on(player, NOFIRE)) {
                        msg("The fire does not seem to affect you.");
                        break;
                }
                explode(&player);
                if (pstats.s_hpt <= 0) {
            pstats.s_hpt = -1;
                    death(D_SCROLL);
        }
                for (sitem = pack; sitem != NULL; sitem = nitem) {
                    nitem = next(sitem); /* in case we delete it */
                    nobj = OBJPTR(sitem);
                    /*
                     * check for loss of all scrolls and give them
                     * a save versus fire
                     */
                    if (nobj->o_type == SCROLL && roll(1,20) < 17) {
                        msg("%s burns up!", inv_name(nobj, TRUE));
                        inpack--;
                        detach(pack, sitem);
                        o_discard(sitem);
                    }
                }
        }

        when S_CHARM:
        {
            bool spots[9];
            int x, y, spot, count, numcharmed, something, bonus;
            struct linked_list *item;
            register struct thing *tp;

            /* Initialize the places where we look around us */
            for (i=0; i<9; i++) spots[i] = FALSE;
            count = 0;  /* No spots tried yet */
            numcharmed = 0;     /* Nobody charmed yet */
            something = 0;      /* Nothing has been seen yet */
            bonus = 0;          /* no bonus yet */

            /* Now look around us randomly for a charmee */
            while (count < 9) {
                do {
                    spot = rnd(9);
                } while (spots[spot] == TRUE);

                /* We found a place */
                count++;
                spots[spot] = TRUE;
                y = hero.y - 1 + (spot / 3);
                x = hero.x - 1 + (spot % 3);

                /* Be sure to stay on the board! */
                if (x < 0 || x >= cols || (y < 1) || (y >= lines - 2))
                        continue;

                /* Is there a monster here? */
                if (!isalpha(mvwinch(mw, y, x))) continue;

                /* What kind is it? */
                item = find_mons(y, x);
                if (item == NULL) continue;

                tp = THINGPTR(item);
                if (on(*tp,ISCHARMED) || on(*tp,ISUNIQUE) || on(*tp,ISUNDEAD)) 
                    continue;

                /* Will the monster be charmed? */
                if (blessed) bonus -= 3;
                bonus -= (pstats.s_charisma - 12) / 3;
                if ((player.t_ctype==C_PALADIN || player.t_ctype==C_RANGER) &&
                    off(*tp, ISMEAN))
                        bonus -= 3;
                if (save(VS_MAGIC, tp, bonus)) continue;

                /* We got him! */
                numcharmed++;

                /* Let the player know (maybe) */
                if ((off(*tp, ISINVIS)     || on(player, CANSEE)) &&
                    (off(*tp, ISSHADOW)    || on(player, CANSEE)) &&
                    cansee(y, x)) {
                        if (on(*tp, CANSURPRISE)) {
                            turn_off(*tp, CANSURPRISE);
                            msg("What the !? ");
                        }
                        msg("The eyes of %s glaze over!",
                            prname(monster_name(tp), FALSE));
                        something++;
                }

                /* Charm him and turn off any side effects */
                turn_on(*tp, ISCHARMED);
                runto(tp, &hero);
                tp->t_action = A_NIL;

                /* If monster was suffocating us, stop it */
                if (on(*tp, DIDSUFFOCATE)) {
                    turn_off(*tp, DIDSUFFOCATE);
                    extinguish(suffocate);
                }

                /* If monster held us, stop it */
                if (on(*tp, DIDHOLD) && (--hold_count == 0))
                        turn_off(player, ISHELD);
                turn_off(*tp, DIDHOLD);

                /* If frightened of this monster, stop */
                if (on(player, ISFLEE) &&
                    player.t_dest == &tp->t_pos) turn_off(player, ISFLEE);

                if ((blessed && numcharmed >= 5) || numcharmed > 0) break;
            }

            if (something == 0) msg(nothing);
        }

        otherwise:
            msg("What a puzzling scroll!");
            if (item != NULL) o_discard(item);
            return;
    }
    look(TRUE, FALSE);  /* put the result of the scroll on the screen */
    status(FALSE);
    if (is_scroll && item && s_know[which] && s_guess[which])
    {
        free(s_guess[which]);
        s_guess[which] = NULL;
    }
    else if (is_scroll                          && 
             !s_know[which]                     && 
             item                               &&
             askme                              &&
             (obj->o_flags & ISKNOW) == 0       &&
             (obj->o_flags & ISPOST) == 0       &&
             s_guess[which] == NULL) {
        nameitem(item, FALSE);
    }
    if (item != NULL) o_discard(item);
    updpack(TRUE, &player);
}