view arogue7/scrolls.c @ 227:696277507a2e

Rogue V4, V5: disable a cheat granting permanent monster detection. In these two games, a potion of monster detection turns on the player's SEEMONST flag. A fuse is set to call turn_see() to turn the flag back off. But the save and restore functions do not recognize turn_see() and fail to set the fuse up again. When restoring, Rogue V4 merely sets the fuse's function to NULL and leaves it burning. When it goes off, a segfault results. Rogue V5 clears all the fuse's fields, and the player retains the ability to see all monsters on the level. The save and restore code can now handle the fuse. The function used is a new wrapper, turn_see_off(), which should lead to less problems with daemons being multiple incompatible types. Also, Rogue V4 and Super-Rogue now properly clear unrecognized daemon and fuse slots when restoring a saved game.
author John "Elwin" Edwards
date Sat, 05 Mar 2016 12:10:20 -0500
parents f9ef86cf22b2
children e52a8a7ad4c5
line wrap: on
line source

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

/*
 * Read a scroll and let it happen
 *
 */

#include "curses.h"
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "rogue.h"

/*
 * let the hero get rid of some type of monster (but not a UNIQUE!)
 */
void
genocide(void)
{
    register struct linked_list *ip;
    register struct thing *mp;
    register struct linked_list *nip;
    register int num_monst = NUMMONST-NUMUNIQUE-1, /* cannot genocide uniques */
		 pres_monst=1, 
		 num_lines=2*(lines-3);
    register int which_monst;

    which_monst = makemonster(FALSE, "Genocide", "wipe out");
    if (which_monst <= 0) {
	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 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) != 0;
	blessed = (obj->o_flags & ISBLESSED) != 0;

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


    switch (which) {
	case S_CONFUSE:
	    /*
	     * Scroll of monster confusion.  Give him that power.
	     */
	    msg("Your hands begin to glow red");
	    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 again.");
		    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
		 */
		aggravate(TRUE, TRUE);
		msg("You hear a high pitched humming noise.");
	    }
	    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);
		    }
		    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);
				}
			    }
			}
		    }
		    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(NULL);
	    }
	    if (is_scroll) s_know[S_IDENT] = TRUE;
	when S_MAP:
	    /*
	     * Scroll of magic mapping.
	     */
	    waddstr(msgw, morestr);
	    clearok(msgw, FALSE);
	    draw(msgw);
	    wait_for(' ');
	    msg("");
	    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 = CCHAR( mvwinch(hw, i, j) ))
		    {
			case SECRETDOOR:
			    nch = secretdoor (i, j);
			    break;
			case '-':
			case '|':
			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;
	    }
	when S_GFIND:
	    /*
	     * Scroll of gold detection
	     */
	    {
		int gtotal = 0;

		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) {
		    if (is_scroll) s_know[S_GFIND] = TRUE;
		    waddstr(msgw, morestr);
		    clearok(msgw, FALSE);
		    draw(msgw);
		    wait_for(' ');
		    msg("");
		    overlay(hw, cw);
		    draw(cw);
		    msg("You begin to feel greedy and 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 * 3) * LEVEL;
		level = nfloors;
		new_level(NORMLEV);
		status(TRUE);
		mpos = 0;
		msg("You are banished to the lower regions.");
		if (old_max == cur_max) /* if he's been here, make it harder */
		    aggravate(TRUE, TRUE);
	    }
	    else if (blessed) {
		int	old_level, 
			much = rnd(4) - 4;

		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.");
	    }
	    else if (blessed) {
		for (nitem = pack; nitem != NULL; nitem = next(nitem)) {
		    nobj = OBJPTR(nitem);
		    nobj->o_flags &= ~ISCURSED;
		}
		msg("Your pack glistens brightly");
	    }
	    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 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("Your 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 += (howmuch * 10) + rnd(5);
			if (lb->o_charges < 0)
			    lb->o_charges = 0;
			if (EQUAL(ws_type[lb->o_which], "staff")) {
			    if (lb->o_charges > 100) 
				lb->o_charges = 100;
			}
			else {
			    if (lb->o_charges > 50)
				lb->o_charges = 50;
			}
			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/2) {
				   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 (!is_scroll) {
		pstats.s_const--;
		max_stats.s_const--;
		if (pstats.s_const <= 0)
		    death(D_CONSTITUTION);
		msg("You feel less healthy now");
	    }
	}
	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)
			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) < 19) {
			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 - 13) / 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("Woah!");
			}
			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 >= 2) || numcharmed > 0) break;
	    }

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

	otherwise:
	    msg("What a puzzling scroll!");
	    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);
}