view xrogue/misc.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 e1cd27c5464f
children
line wrap: on
line source

/*
    misc.c - routines dealing specifically with miscellaneous magic
    
    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.

    See the file LICENSE.TXT for full copyright and licensing information.
*/

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

/*
 * changeclass:
 *      Change the player's class to the specified one.
 */

void
changeclass(int newclass)
{
    if (newclass == player.t_ctype) {
        msg("You feel more skillful.");
        raise_level();
    }
    else {
        /*
         * reset his class and then use check_level to reset hit
         * points and the right level for his exp pts
         * drop exp pts by 10%
         */
        long save;

        msg("You are transformed into a %s! ", char_class[newclass].name);

        /*
         * if he becomes a thief or an assassin give him studded leather armor
         */
        if ((newclass == C_THIEF || newclass == C_ASSASSIN) &&
            cur_armor != NULL && cur_armor->o_which != STUDDED_LEATHER)
                cur_armor->o_which = STUDDED_LEATHER;
        /*
         * if he becomes a monk he can't wear any armor
     * so give him a cloak of protection
         */
        if (newclass == C_MONK && cur_armor != NULL) {
                cur_armor->o_ac = armors[cur_armor->o_which].a_class - 
                                  cur_armor->o_ac;
                cur_armor->o_type = MM;
                cur_armor->o_which = MM_PROTECT;
                cur_armor->o_flags &= ~(ISPROT | ISKNOW);
                cur_misc[WEAR_CLOAK] = cur_armor;
                cur_armor = NULL;
        }
    /*
     * otherwise give him plate armor
     */
        if ((newclass != C_THIEF ||
         newclass != C_ASSASSIN || newclass != C_MONK) &&
         cur_armor != NULL && cur_armor->o_which != PLATE_ARMOR) 
                cur_armor->o_which = PLATE_ARMOR;

        /*
         * if he used to be a spell caster of some sort, kill the fuse
         */
        if (player.t_ctype == C_MAGICIAN || player.t_ctype == C_RANGER)
                extinguish(spell_recovery);
        if (player.t_ctype == C_DRUID || player.t_ctype == C_MONK)
                extinguish(chant_recovery);
        if ((player.t_ctype == C_CLERIC || player.t_ctype == C_PALADIN) &&
             !cur_relic[HEIL_ANKH])
                extinguish(prayer_recovery);

        /*
         * if he becomes a spell caster of some kind, give him a fuse
         */
        if (newclass == C_MAGICIAN || newclass == C_RANGER)
                fuse(spell_recovery, NULL, SPELLTIME, AFTER);
        if (newclass == C_DRUID || newclass == C_MONK)
                fuse(chant_recovery, NULL, SPELLTIME, AFTER);
        if ((newclass==C_CLERIC || newclass==C_PALADIN) && !cur_misc[HEIL_ANKH])
                fuse(prayer_recovery, NULL, SPELLTIME, AFTER);
        /*
         * if he's changing from a fighter, ranger, or paladin then we
     * may have to change his sword since only these types can wield
     * the two-handed sword.
         */
        if ((player.t_ctype == C_FIGHTER ||
        player.t_ctype == C_RANGER   ||
            player.t_ctype == C_PALADIN) &&
        cur_weapon != NULL && cur_weapon->o_type == WEAPON &&
            (cur_weapon->o_which == BASWORD  ||
        cur_weapon->o_which == TWOSWORD) &&
            !(newclass == C_FIGHTER || newclass == C_RANGER ||
            newclass == C_PALADIN)  &&
        cur_weapon->o_which == TWOSWORD)
                cur_weapon->o_which = SWORD;

        /*
         * if he's changing from a thief, assassin, fighter, or monk
     * then we may have to change his sword again since only these
     * types can wield the bastard sword.
         */
        if ((player.t_ctype == C_THIEF  || player.t_ctype == C_ASSASSIN ||
            player.t_ctype == C_FIGHTER || player.t_ctype == C_MONK)    &&
        cur_weapon != NULL && cur_weapon->o_type == WEAPON &&
            (cur_weapon->o_which == BASWORD  ||
            cur_weapon->o_which == TWOSWORD) &&
            !(newclass == C_THIEF || newclass == C_ASSASSIN ||
            newclass == C_MONK)   &&
        cur_weapon->o_which == BASWORD)
                cur_weapon->o_which = SWORD;

        /*
         * if he was a thief, assassin, or monk then take out
     * the trap_look() daemon
         */
        if (player.t_ctype == C_THIEF || player.t_ctype == C_MONK ||
            player.t_ctype == C_ASSASSIN)
                kill_daemon(trap_look);

        /*
         * if he becomes a thief, assassin, or monk then add 
     * the trap_look() daemon
         */
        if (newclass == C_THIEF || newclass == C_ASSASSIN ||
        newclass == C_MONK)
                start_daemon(trap_look, NULL, AFTER);

    /* adjust stats */
        char_type = player.t_ctype = newclass;
        save = pstats.s_hpt;
        max_stats.s_hpt = pstats.s_hpt = 0;
        max_stats.s_lvl = pstats.s_lvl = 0; 
        max_stats.s_lvladj = pstats.s_lvladj = 0; 
        max_stats.s_exp = pstats.s_exp + rnd(4);
        check_level();
        if (pstats.s_hpt > save) /* don't add to current hits */
            pstats.s_hpt = save;
    }
    dsrpt_player(); /* this should disrupt whatever we were doing */
}

/*
 * Use the relic that our monster is wielding.
 */

void
m_use_relic(struct thing *monster)
{
    register struct object *obj;

    /* Make sure we really have it */
    if (monster->t_using) obj = OBJPTR(monster->t_using);
    else {
        debug("Relic not set!");
        monster->t_action = A_NIL;
        return;
    }

    /* Now let's see what we're using */
    if (obj->o_type == RELIC) switch (obj->o_which) {
        case MING_STAFF: {
            static struct object missile = {
              MISSILE, {0,0}, 0, "", "0d4 " , NULL, 0, WS_MISSILE, 100, 1
            };

            debug("Firing Ming's staff");
            sprintf(missile.o_hurldmg, "%dd4", monster->t_stats.s_lvl);
            do_motion(&missile,
                       monster->t_newpos.y, monster->t_newpos.x, monster);
            hit_monster(unc(missile.o_pos), &missile, monster);
            monster->t_artifact = monster->t_artifact * 4 / 5;
        }
        when EMORI_CLOAK:
            debug("stunning with Emori's cloak");
            do_zap(monster, obj, &monster->t_newpos, WS_PARALYZE, 0);
            obj->o_charges = 0;

        when ASMO_ROD: {
            char *name;

            switch (rnd(3)) { /* Select a function */
                case 0:    name = "lightning bolt";
                when 1:    name = "flame";
                otherwise: name = "ice";
            }
            shoot_bolt( monster, 
                        monster->t_pos, 
                        monster->t_newpos, 
                        FALSE, 
                        monster->t_index, 
                        name, 
                        roll(monster->t_stats.s_lvl,6));
            monster->t_artifact /= 2;
        }
        when BRIAN_MANDOLIN:
            /* Make sure the defendant is still around */
            if (DISTANCE(monster->t_pos.y, monster->t_pos.x,
                         hero.y, hero.x) < 25) {
                if (!save(VS_MAGIC, &player, -4) &&
                    !ISWEARING(R_ALERT)) {
                    msg("Some beautiful music enthralls you.");
                    player.t_no_move += movement(&player) * FREEZETIME;
                    player.t_action = A_FREEZE;
                    monster->t_artifact = monster->t_artifact * 2 / 3;
                }
                else {
                    msg("You wince at a sour note.");
                    monster->t_artifact /= 3;
                }
            }
        when GERYON_HORN:
            /* Make sure the defendant is still around */
            if (DISTANCE(monster->t_pos.y, monster->t_pos.x,
                         hero.y, hero.x) < 25) {
                if (!ISWEARING(R_HEROISM) &&
                    !save(VS_MAGIC, &player, -4)) {
                        turn_on(player, ISFLEE);
                        player.t_dest = &monster->t_pos;
                        msg("A shrill blast terrifies you.");
                        monster->t_artifact = monster->t_artifact * 3 / 4;
                }
                else  {
                    msg("A shrill blast sends chills up your spine! ");
                    monster->t_artifact /= 3;
                }
            }

        otherwise:
            /* Unknown RELIC! */
            debug("Unknown wielded relic %d", obj->o_which);
    }
    else debug("Declared relic is %d", obj->o_type);

    turn_off(*monster, CANSURPRISE);
    /* Reset the monsters actions */
    monster->t_action = A_NIL;
    monster->t_using = NULL;
}
 
/*
 * add something to the contents of something else
 * bag: the holder of the items
 * item: the item to put inside
 */

void
put_contents(struct object *bag, struct linked_list *item)
{
    register struct linked_list *titem;
    register struct object *tobj;

    bag->o_ac++;
    tobj = OBJPTR(item);
    for (titem = bag->contents; titem != NULL; titem = next(titem)) {
        if ((OBJPTR(titem))->o_which == tobj->o_which)
            break;
    }
    if (titem == NULL) {        /* if not a duplicate put at beginning */
        attach(bag->contents, item);
    }
    else {
        item->l_prev = titem;
        item->l_next = titem->l_next;
        if (next(titem) != NULL) 
            (titem->l_next)->l_prev = item;
        titem->l_next = item;
    }
}

/*
 * remove something from something else
 * bag: the holder of the items
 */

void
take_contents(struct object *bag, struct linked_list *item)
{

    if (bag->o_ac <= 0) {
        msg("Nothing to take out");
        return;
    }
    bag->o_ac--;
    detach(bag->contents, item);
    if (!add_pack(item, FALSE))
        put_contents(bag, item);
}


void
do_bag(struct linked_list *item)
{

    register struct linked_list *titem = NULL;
    register struct object *obj, *tobj;
    bool doit = TRUE;

    obj = OBJPTR(item);
    while (doit) {
        msg("What do you want to do? (* for a list): ");
        mpos = 0;
        switch (wgetch(cw)) {
            case EOF:
            case ESC:
                msg ("");
                doit = FALSE;
            when '1':
                inventory(obj->contents, ALL);

            when '2':
                if (obj->o_ac >= MAXCONTENTS) {
                    msg("the %s is full", m_magic[obj->o_which].mi_name);
                    break;
                }
                switch (obj->o_which) {
                case MM_BEAKER:
                    titem = get_item(pack, "put in", POTION, FALSE, FALSE);
                when MM_BOOK:
                    titem = get_item(pack, "put in", SCROLL, FALSE, FALSE);
                }
                if (titem == NULL)
                    break;
                detach(pack, titem);
                inpack--;
                put_contents(obj, titem);
            
            when '3':
                titem = get_item(obj->contents,"take out",ALL,FALSE,FALSE);
                if (titem == NULL)
                    break;
                take_contents(obj, titem);
                
            when '4': 
                switch (obj->o_which) {
                case MM_BEAKER:
                    titem = get_item(obj->contents,"quaff",ALL,FALSE,FALSE);
                    if (titem == NULL)
                        break;
                    tobj = OBJPTR(titem);
                    obj->o_ac--;
                    detach(obj->contents, titem);
                    quaff(tobj->o_which, 
                          tobj->o_kind,
                          tobj->o_flags,
                          TRUE);
                    if (p_know[tobj->o_which] && p_guess[tobj->o_which])
                    {
                        free(p_guess[tobj->o_which]);
                        p_guess[tobj->o_which] = NULL;
                    }
                    else if (!p_know[tobj->o_which]             && 
                             askme                              &&
                             (tobj->o_flags & ISKNOW) == 0      &&
                             (tobj->o_flags & ISPOST) == 0      &&
                             p_guess[tobj->o_which] == NULL) {
                        nameitem(titem, FALSE);
                    }
                    o_discard(titem);
                when MM_BOOK:   
                    if (on(player, ISBLIND)) {
                        msg("You can't see to read anything! ");
                        break;
                    }
                    titem = get_item(obj->contents,"read",ALL,FALSE,FALSE);
                    if (titem == NULL)
                        break;
                    tobj = OBJPTR(titem);
                    obj->o_ac--;
                    detach(obj->contents, titem);
                    read_scroll(tobj->o_which, 
                                tobj->o_flags & (ISCURSED|ISBLESSED),
                                TRUE);
                    if (s_know[tobj->o_which] && s_guess[tobj->o_which])
                    {
                        free(s_guess[tobj->o_which]);
                        s_guess[tobj->o_which] = NULL;
                    }
                    else if (!s_know[tobj->o_which]             && 
                             askme                              &&
                             (tobj->o_flags & ISKNOW) == 0      &&
                             (tobj->o_flags & ISPOST) == 0      &&
                             s_guess[tobj->o_which] == NULL) {
                        nameitem(titem, FALSE);
                    }
                    o_discard(titem);
                }
                doit = FALSE;

            otherwise:
                wclear(hw);
                touchwin(hw);
                mvwaddstr(hw,0,0,"The following operations are available:");
                mvwaddstr(hw,2,0,"[1]\tInventory\n");
                wprintw(hw,"[2]\tPut something in the %s\n",
                        m_magic[obj->o_which].mi_name);
                wprintw(hw,"[3]\tTake something out of the %s\n",
                        m_magic[obj->o_which].mi_name);
                switch(obj->o_which) {
                    case MM_BEAKER: waddstr(hw,"[4]\tQuaff a potion\n");
                    when MM_BOOK:   waddstr(hw,"[4]\tRead a scroll\n");
                }
        /* this is confusing! <press space to continue> */
                /* waddstr(hw,"[ESC]\tLeave this menu\n"); */
                mvwaddstr(hw, lines-1, 0, spacemsg);
                draw(hw);
                wait_for (' ');
                restscr(cw);
        }
    }
}

void
do_panic(int who)
{
    /* who: kind of monster to panic (all if who is 0) */
    register int x,y;
    register struct linked_list *mon, *item;
    register struct thing *th;

    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) {
                    th = THINGPTR(mon);

                    /* Is this the right kind of monster to panic? */
                    if (who && th->t_index != who) continue;

                    if ((who && th->t_stats.s_intel < 14) || 
                        (!on(*th, ISUNDEAD) && !save(VS_MAGIC, th, 0) &&
              off(*th, WASTURNED))) {
                          msg("%s %s.", prname(monster_name(th), TRUE),
                            terse ? "panics" : "turns to run in panic");

                        turn_on(*th, ISFLEE);
                        turn_on(*th, WASTURNED);
                        turn_off(*th, CANSURPRISE);

                        /* Disrupt what it was doing */
                        dsrpt_monster(th, TRUE, TRUE);

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

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

                        /*
                         * if he has something he might drop it
                         */
                        if ((item = th->t_pack) != NULL         && 
                            (OBJPTR(item))->o_type != RELIC     && 
                            rnd(100) < 67) {
                                detach(th->t_pack, item);
                                fall(item, FALSE);
                        }

                        /* It is okay to turn tail */
                        th->t_oldpos = th->t_pos;
                    }
                    runto(th, &hero);
                }
            }
        }
    }
}

/*
 * print miscellaneous magic bonuses
 */

int
misc_name(char *str, struct object *obj)
{
    char buf1[LINELEN];

    *str = 0;
    buf1[0] = 0;

    if (!(obj->o_flags & ISKNOW))
    {
        strcat(str,m_magic[obj->o_which].mi_name);
        return(0);
    }

    switch (obj->o_which) 
    {
        case MM_BRACERS:
        case MM_PROTECT:
            strcat(str, num(obj->o_ac, 0));
            strcat(str, " ");
    }
    switch (obj->o_which) {
    case MM_CRYSTAL:
            if (obj->o_flags & ISBLESSED)
                strcat(str, "glowing ");
    }
    switch (obj->o_which) {
        case MM_G_OGRE:
        case MM_G_DEXTERITY:
        case MM_JEWEL:
        case MM_STRANGLE:
        case MM_R_POWERLESS:
        case MM_DANCE:
            if (obj->o_flags & ISCURSED)
                strcat(str, "cursed ");
        when MM_CRYSTAL:
            if (obj->o_flags & ISCURSED)
                strcat(str, "opaque ");
    }
    strcat(str, m_magic[obj->o_which].mi_name);

    switch (obj->o_which) 
    {
        case MM_JUG:
            if (obj->o_ac == JUG_EMPTY)
                strcat(buf1, " [empty]");
            else if (p_know[obj->o_ac])
                sprintf(buf1, " [containing a potion of %s (%s)]",
                        p_magic[obj->o_ac].mi_name,
                        p_colors[obj->o_ac]);
            else sprintf(buf1, " [containing a%s %s liquid]", 
                        vowelstr(p_colors[obj->o_ac]),
                        p_colors[obj->o_ac]);
        when MM_BEAKER:         
        case MM_BOOK: {
            sprintf(buf1, " [containing %d]", obj->o_ac);
        }
        when MM_OPEN:
        case MM_HUNGER:
            sprintf(buf1, " [%d ring%s]", obj->o_charges, 
                          obj->o_charges == 1 ? "" : "s");
        when MM_DRUMS:
            sprintf(buf1, " [%d beat%s]", obj->o_charges, 
                          obj->o_charges == 1 ? "" : "s");
        when MM_DISAPPEAR:
        case MM_CHOKE:
            sprintf(buf1, " [%d pinch%s]", obj->o_charges, 
                          obj->o_charges == 1 ? "" : "es");
        when MM_KEOGHTOM:
            sprintf(buf1, " [%d application%s]", obj->o_charges, 
                          obj->o_charges == 1 ? "" : "s");
        when MM_SKILLS:
            sprintf(buf1, " [%s]", char_class[obj->o_ac].name);
    }
    strcat(str, buf1);
    
    return(0);
}

void
use_emori(void)
{
    char selection;     /* Cloak function */
    int state = 0;      /* Menu state */

    msg("What do you want to do? (* for a list): ");
    do {
        selection = wgetch(cw);
        switch (selection) {
            case '*':
              if (state != 1) {
                wclear(hw);
                touchwin(hw);
                mvwaddstr(hw, 2, 0,  "[1] Fly\n[2] Stop flying\n");
                waddstr(hw,          "[3] Turn invisible\n[4] Turn Visible\n");
                mvwaddstr(hw, 0, 0, "What do you want to do? ");
                draw(hw);
                state = 1;      /* Now in prompt window */
              }
              break;

            case ESC:
                if (state == 1) {
                    restscr(cw);
                }
                msg("");

                after = FALSE;
                return;

            when '1':
            case '2':
            case '3':
            case '4':
                if (state == 1) {       /* In prompt window */
                    restscr(cw);
                }

                msg("");

                state = 2;      /* Finished */
                break;

            default:
                if (state == 1) {       /* In the prompt window */
                    mvwaddstr(hw, 0, 0,
                                "Please enter a selection between 1 and 4:  ");
                    draw(hw);
                }
                else {  /* Normal window */
                    mpos = 0;
                    msg("Please enter a selection between 1 and 4:  ");
                }
        }
    } while (state != 2);

    /* We now must have a selection between 1 and 4 */
    switch (selection) {
        case '1':       /* Fly */
            if (on(player, ISFLY)) {
                extinguish(land);       /* Extinguish in case of potion */
                msg("%slready flying.", terse ? "A" : "You are a");
            }
            else {
                msg("You feel lighter than air!");
                turn_on(player, ISFLY);
            }
        when '2':       /* Stop flying */
            if (off(player, ISFLY))
                msg("%sot flying.", terse ? "N" : "You are n");
            else {
                if (find_slot(land))
                    msg("%sot flying by the cloak.",
                        terse ? "N" : "You are n");
                else land();
            }
        when '3':       /* Turn invisible */
            if (off(player, ISINVIS)) {
                turn_on(player, ISINVIS);
                msg("You have a tingling feeling all over your body. ");
                PLAYER = IPLAYER;
                light(&hero);
            }
            else {
                extinguish(appear);     /* Extinguish in case of potion */
                extinguish(dust_appear);/* dust of disappearance        */
                msg("%slready invisible.", terse ? "A" : "You are a");
            }
        when '4':       /* Turn visible */
            if (off(player, ISINVIS))
                msg("%sot invisible.", terse ? "N" : "You are n");
            else {
                if (find_slot(appear) || find_slot(dust_appear))
                    msg("%sot invisible by the cloak.",
                        terse ? "N" : "You are n");
                else appear();
            }
    }
}

/*
 * try to write a scroll with the quill of Nagrom
 */

void
use_quill(struct object *obj)
{
    struct linked_list  *item;
    register int        i,
                        scroll_ability;
    int                 which_scroll,
                        curlen,
                        maxlen = 0,
                        dummy = 0;
    bool                nohw = FALSE;

    i = which_scroll = 0;
    scroll_ability = obj->o_charges;

    /* Prompt for scrolls */
    msg("Which scroll are you writing? (* for list): ");

    which_scroll = (int) (wgetch(cw) - 'a');
    msg("");    /* Get rid of the prompt */
    if (which_scroll == (int) ESC - (int) 'a') {
        after = FALSE;
        return;
    }
    if (which_scroll >= 0 && which_scroll < MAXQUILL) nohw = TRUE;

    else if (slow_invent) {
        register char c;

        nohw = TRUE;
        do {
            for (i=0; i<MAXQUILL; i++) {
                msg("");
                mvwaddch(msgw, 0, 0, '[');
                waddch(msgw, (char) ((int) 'a' + i));
                waddstr(msgw, "] A scroll of ");
                waddstr(msgw, s_magic[quill_scrolls[i].s_which].mi_name);
                waddstr(msgw, morestr);
                clearok(msgw, FALSE);
                draw(msgw);
                do {
                    c = wgetch(cw);
                } while (c != ' ' && c != ESC);
                if (c == ESC)
                    break;
            }
            msg("");
            mvwaddstr(msgw, 0, 0, "Which scroll are you writing? ");
            clearok(msgw, FALSE);
            draw(msgw);

            which_scroll = (int) (wgetch(cw) - 'a');
        } while (which_scroll != (int) (ESC - 'a') &&
                 (which_scroll < 0 || which_scroll >= MAXQUILL));

        if (which_scroll == (int) (ESC - 'a')) {
            mpos = 0;
            msg("");
            after = FALSE;
            return;
        }
    }
    else {
        /* Now display the possible scrolls */
        wclear(hw);
        touchwin(hw);
        mvwaddstr(hw, 2, 0, "   Cost            Scroll");
        mvwaddstr(hw, 3, 0,
                "-----------------------------------------------");
        maxlen = 47;    /* Maximum width of header */

        for (i=0; i<MAXQUILL; i++) {
            wmove(hw, i+4, 0);
            sprintf(prbuf, "[%c]        %3d     A scroll of %s",
                    (char) ((int) 'a' + i),
                    quill_scrolls[i].s_cost,
                    s_magic[quill_scrolls[i].s_which].mi_name);
            waddstr(hw, prbuf);

            /* Get the length of the line */
            getyx(hw, dummy, curlen);
            if (maxlen < curlen) maxlen = curlen;
        }

        sprintf(prbuf, "[Current scroll power = %d]", scroll_ability);
        mvwaddstr(hw, 0, 0, prbuf);
        waddstr(hw, " Which scroll are you writing? ");
        getyx(hw, dummy, curlen);
        if (maxlen < curlen) maxlen = curlen;

        /* Should we overlay? */
        if (menu_overlay && MAXQUILL + 3 < lines - 3) {
            over_win(cw, hw, MAXQUILL + 5, maxlen + 3, 0, curlen, '\0');
        }
        else draw(hw);
    }

    if (!nohw) {
        which_scroll = (int) (wgetch(cw) - 'a');
        while (which_scroll < 0 || which_scroll >= MAXQUILL) {
            if (which_scroll == (int) ESC - (int) 'a') {
                after = FALSE;

                /* Restore the screen */
                if (MAXQUILL + 3 < lines / 2) {
                    clearok(cw, FALSE);
                    touchwin(cw);
                }
                else restscr(cw);
                return;
            }
            wmove(hw, 0, 0);
            wclrtoeol(hw);
            waddstr(hw, "Please enter one of the listed scrolls. ");
            getyx(hw, dummy, curlen);
            if (maxlen < curlen) maxlen = curlen;

            /* Should we overlay? */
            if (menu_overlay && MAXQUILL + 3 < lines - 3) {
                over_win(cw, hw, MAXQUILL + 5, maxlen + 3,
                            0, curlen, '\0');
            }
            else draw(hw);

            which_scroll = (int) (wgetch(cw) - 'a');
        }
    }

    /* Now restore the screen if we have to */
    if (!nohw) {
        if (MAXQUILL + 3 < lines / 2) {
            touchwin(cw);
            clearok(cw, FALSE);
        }
        else restscr(cw);
    }

    /* We've waited our required time. */
    player.t_using = NULL;
    player.t_action = A_NIL;

    if (quill_scrolls[which_scroll].s_cost > scroll_ability) {
        msg("Your attempt fails.");
        return;
    }

    obj->o_charges -= quill_scrolls[which_scroll].s_cost;
    item = spec_item(SCROLL, quill_scrolls[which_scroll].s_which, 0, 0);
    if (add_pack(item, FALSE) == FALSE) {
        (OBJPTR(item))->o_pos = hero;
        fall(item, TRUE);
    }

    which_scroll = dummy; /* Hack to stop IRIX complaint about dummy not */
                          /* being used */
}

/*
 * Use something
 */

void
use_mm(int which)
{
    register struct object *obj = NULL;
    register struct linked_list *item = NULL;
    bool is_mm;

    is_mm = FALSE;

    if (which < 0) {    /* A real miscellaneous magic item  */
        /* This is miscellaneous magic.  It takes 3 movement periods to use */
        if (player.t_action != C_USE) {
            int units;  /* Number of movement units for the item */

            item = get_item(pack, "use", USEABLE, FALSE, FALSE);

            /*
             * Make certain that it is a micellaneous magic item
             */
            if (item == NULL)
                return;

            units = usage_time(item);
            if (units < 0) return;

            player.t_using = item;      /* Remember what it is */
            player.t_action = C_USE;    /* We are quaffing */
            player.t_no_move = units * movement(&player);
            return;
        }

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

        is_mm = TRUE;

        obj = OBJPTR(item);
        which = obj->o_which;
    }

    if (obj->o_type == POTION) {                /* A potion */
        is_mm = FALSE;
        inpack--;
        detach (pack, item);
        switch (obj->o_which) {
            case P_POISON:
                if (cur_weapon) {
                    if (cur_weapon->o_type == RELIC) {
                        msg("The poison burns off %s", 
                            inv_name(cur_weapon,FALSE));
                    }
                    else {
                        cur_weapon->o_flags |= ISPOISON;
                        msg("Your weapon has %s gooey stuff on it",
                            p_colors[cur_weapon->o_which]);
                    }
                }
                else 
                    msg("The poison pours on the floor and disappears!");
        }
        o_discard(item);
    }
    else if (obj->o_type == RELIC) {            /* An artifact */
        is_mm = FALSE;
        switch (obj->o_which) {
            case EMORI_CLOAK:
                use_emori();
            when QUILL_NAGROM:
                use_quill(obj);
            when BRIAN_MANDOLIN:
                /* Put monsters around us to sleep */
                read_scroll(S_HOLD, 0, FALSE);
            when GERYON_HORN:
                /* Chase close monsters away */
                msg("The horn blasts a shrill tone.");
                do_panic(0);
            when EYE_VECNA:
                msg("The pain slowly subsides.. ");
            when HEIL_ANKH:
                msg("Your hand grows very warm. ");
            when YENDOR_AMULET:
                msg("Your chest glows! ");
                do_panic(findmindex("frost giant"));
            when STONEBONES_AMULET:
                msg("Your chest glows! ");
                do_panic(findmindex("storm giant"));
            when SURTUR_RING:
                do_panic(findmindex("fire giant"));
            when ALTERAN_CARD:  /* the card allows you to teleport anywhere */
        do_teleport();
        }
    }
    else switch (which) {               /* Miscellaneous Magic */
        /*
         * the jug of alchemy manufactures potions when you drink
         * the potion it will make another after a while
         */
        case MM_JUG:
            if (obj->o_ac == JUG_EMPTY) {
                msg("The jug is empty");
                break;
            }
            quaff (obj->o_ac, 0, 0, FALSE);
            obj->o_ac = JUG_EMPTY;
            fuse (alchemy, obj, ALCHEMYTIME, AFTER);
            if (!(obj->o_flags & ISKNOW))
                whatis(item);

        /*
         * the beaker of plentiful potions is used to hold potions
         * the book of infinite spells is used to hold scrolls
         */
        when MM_BEAKER:
        case MM_BOOK:
            do_bag(item);

        /*
         * the chime of opening opens up secret doors
         */
        when MM_OPEN:
        {
            register struct linked_list *exit;
            register struct room *rp;
            register coord *cp;

            if (obj->o_charges <= 0) {
                msg("The chime is cracked!");
                break;
            }
            obj->o_charges--;
            msg("chime... chime... hime... ime... me... e...");
            if ((rp = roomin(&hero)) == NULL) {
                search(FALSE, TRUE); /* Non-failing search for door */
                break;
            }
            for (exit = rp->r_exit; exit != NULL; exit = next(exit)) {
                cp = DOORPTR(exit);
                if (winat(cp->y, cp->x) == SECRETDOOR) {
                    mvaddch (cp->y, cp->x, DOOR);
                    if (cansee (cp->y, cp->x))
                        mvwaddch(cw, cp->y, cp->x, DOOR);
                }
            }
        }

        /*
         * the chime of hunger just makes the hero hungry
         */
        when MM_HUNGER:
            if (obj->o_charges <= 0) {
                msg("The chime is spent. ");
                break;
            }
            obj->o_charges--;
            if (food_left >= MORETIME + 5) {
                food_left = MORETIME + 5;
                msg("A strange sensation comes over you.. ");
                msg(terse? "Getting hungry" : "You are starting to get hungry");
                hungry_state = F_HUNGRY;
            }
            if (player.t_ctype == C_PALADIN ||
                player.t_ctype == C_RANGER  || player.t_ctype == C_MONK) {
                    msg("You feel a chilling sensation!");
                    aggravate(TRUE, FALSE);
            }
            else {
                aggravate(TRUE, TRUE);
            }

        /*
         * the drums of panic make all creatures within two squares run
         * from the hero in panic unless they save or they are mindless
         * undead
         */
        when MM_DRUMS:
            if (obj->o_charges <= 0) {
                msg("The drum is broken.");
                break;
            }
            obj->o_charges--;
            do_panic(0);
        return;
        /*
         * dust of disappearance makes the player invisible for a while
         */
        when MM_DISAPPEAR:
            m_know[MM_DISAPPEAR] = TRUE;
            if (obj->o_charges <= 0) {
                msg("No more dust!");
                break;
            }
            obj->o_charges--;
            if (terse) msg("You sneeze! ");
            else msg("Ahh.. Ahh... Choo!! ");
            if (!find_slot(dust_appear)) {
                turn_on(player, ISINVIS);
                fuse(dust_appear, NULL, DUSTTIME, AFTER);
                PLAYER = IPLAYER;
                light(&hero);
            }
            else lengthen(dust_appear, DUSTTIME);

        /*
         * dust of choking and sneezing can kill the hero if he misses
         * the save
         */
        when MM_CHOKE:
            m_know[MM_CHOKE] = TRUE;
            if (obj->o_charges <= 0) {
                msg("No more dust!");
                break;
            }
            obj->o_charges--;
            if (terse) msg("You snort! ");
            else msg("aaAAACHOOOooo. Cough. Cough. Sneeze. Sneeze.");
            if (!cur_relic[SURTUR_RING] && !save(VS_POISON, &player, 0)) {
                msg ("You choke to death!!!  --More--");
                wait_for(' ');
                pstats.s_hpt = -1;  /* in case he hangs up the phone! */
                death(D_CHOKE);
            }
            else {
                msg("You begin to cough and choke uncontrollably! ");
                if (find_slot(unchoke))
                    lengthen(unchoke, DUSTTIME);
                else
                    fuse(unchoke, NULL, DUSTTIME, AFTER);
                turn_on(player, ISHUH);
                turn_on(player, ISBLIND);
                light(&hero);
            }
                
        when MM_KEOGHTOM:
            /*
             * this is a very powerful healing ointment
             * but it takes a while to put on...
             */
            obj->o_charges--;
            if (on(player, HASDISEASE)) {
                extinguish(cure_disease);
                cure_disease();
                msg(terse ? "You feel yourself improving."
                          : "You begin to feel yourself improving again.");
            }
            if (on(player, HASINFEST)) {
                turn_off(player, HASINFEST);
                infest_dam = 0;
                msg(terse ? "You feel yourself improving."
                          : "You begin to feel yourself improving again.");
            }
            if (on(player, DOROT)) {
                msg("You feel your skin returning to normal.");
                turn_off(player, DOROT);
            }
            pstats.s_hpt += roll(pstats.s_lvl, 6);
            if (pstats.s_hpt > max_stats.s_hpt)
                pstats.s_hpt = max_stats.s_hpt;
            sight();
            msg("You begin to feel much better.");
                
        /*
         * The book has a character class associated with it.
         * if your class matches that of the book, it will raise your 
         * level by one. If your class does not match the one of the book, 
         * it change your class to that of book.
         * Note that it takes a while to read.
         */
        when MM_SKILLS:
            detach (pack, item);
            inpack--;
            changeclass(obj->o_ac);
    when MM_CRYSTAL:
    {
        register char *str;

            detach (pack, item);
            inpack--;
            if (obj->o_flags & ISCURSED) {
            if (is_mm && !m_know[MM_CRYSTAL])
                str = "rock in a curious sort of way";
        else
            str = "crystal briefly";
        msg("You rub the %s and yell out in agony! ", str);
          /* curse his pack */
        read_scroll(S_REMOVE, obj->o_flags & ISCURSED, FALSE);
          /* aggravate monsters */
        read_scroll(S_HOLD, obj->o_flags & ISCURSED, FALSE);
                player.t_no_move += (2 * movement(&player) * FREEZETIME);
                player.t_action = A_FREEZE;
          /* loss of 1/4 total hit points */
        pstats.s_hpt -= ((max_stats.s_hpt / 4));
                max_stats.s_hpt -= rnd(3)+3;
        if (pstats.s_hpt > max_stats.s_hpt) 
                pstats.s_hpt = max_stats.s_hpt;
                if ((pstats.s_hpt < 1) || (max_stats.s_hpt < 1)) {
            pstats.s_hpt = -1;
            msg("The crystal has absorbed you...  --More--");
            wait_for(' ');
                    death(D_CRYSTAL);
        }
        }
        else {  /* if normal, give him a bonus */
            if (is_mm && !m_know[MM_CRYSTAL])
                    str = "flashes brightly";
        else
            str = "vibrates softly";
        msg("You rub the crystal and it %s...  ", str);
          /* cure him */
        read_scroll(S_CURING, 0, FALSE);
          /* give him weird hands */
                turn_on(player, CANHUH);
        msg("Your fingertips turn blue.  ");
          /* add intelligence */
                if (player.t_ctype == C_MAGICIAN) {
                max_stats.s_intel += 1;
                pstats.s_intel += 1;
        }
          /* add strength */
                if (player.t_ctype == C_FIGHTER) {
            max_stats.s_str += 1;
                pstats.s_str += 1;
        }
          /* add wisdom */
                if (player.t_ctype == C_CLERIC || player.t_ctype == C_PALADIN) {
                    max_stats.s_wisdom += 1;
                    pstats.s_wisdom += 1;
        }
          /* add dexterity */
                if (player.t_ctype == C_THIEF || player.t_ctype == C_ASSASSIN) {
                    max_stats.s_dext += 1;
                    pstats.s_dext += 1;
        }
              /* add constitution */
                if (player.t_ctype == C_MONK) {
                    max_stats.s_const += 1;
                    pstats.s_const += 1;
        }
          /* add charisma */
                if (player.t_ctype == C_RANGER || player.t_ctype == C_PALADIN) {
                max_stats.s_charisma += 1;
                pstats.s_charisma += 1;
        }
        }
            if (obj->o_flags & ISBLESSED) {  /* if blessed */
            if (is_mm && !m_know[MM_CRYSTAL])
            msg("The crystal disappears from your hands.  ");
        else
            msg("Your hands absorb the medicine crystal.  ");
            /* set hit points to at least 50 */
        if (max_stats.s_hpt < 50) {
            max_stats.s_hpt = 50;
                    pstats.s_hpt = max_stats.s_hpt;
        }
        else {  /* or just add 10% */
            max_stats.s_hpt += (max_stats.s_hpt / 10);
                    pstats.s_hpt = max_stats.s_hpt;
        }
            /* heck, really make it memorable */
        read_scroll(S_REMOVE, obj->o_flags & ISBLESSED, FALSE);
        }
    }
        otherwise:
            msg("What a strange magic item you have!");
    }
    status(FALSE);
    if (is_mm && m_know[which] && m_guess[which]) {
        free(m_guess[which]);
        m_guess[which] = NULL;
    }
    else if (is_mm && !m_know[which] && askme &&
             (obj->o_flags & ISKNOW) == 0 &&
             m_guess[which] == NULL) {
        nameitem(item, FALSE);
    }
    if (item != NULL && (which == MM_SKILLS || which == MM_CRYSTAL))
        o_discard(item);
    updpack(TRUE, &player);
}

/*
 * usage_time:
 *      Return how long it takes to use an item.  For now we only give time
 *      for MM, RELIC, SCROLL, and POTION items.
 */

int
usage_time(struct linked_list *item)
{
    register struct object *obj;
    register int units = -1;

    obj = OBJPTR(item);
    switch (obj->o_type) {
        case SCROLL:    units = 4;
        when POTION:    units = 3;
        when RELIC:                     /* An artifact */
            switch (obj->o_which) {
                case BRIAN_MANDOLIN:
                case GERYON_HORN:       units = 4;
                when QUILL_NAGROM:
                case EMORI_CLOAK:
                case HEIL_ANKH:         units = 3;
                when YENDOR_AMULET:
                case STONEBONES_AMULET: units = 2;
                when EYE_VECNA:         units = 6;
                    /* The eye will do nothing other than give a headache */
                    pstats.s_hpt -= rnd(25)+1;
                    msg("You feel a sharp pain shoot through your forehead!");
                    if (pstats.s_hpt < 1) {
               pstats.s_hpt = -1;
                       msg ("The pain is too much for you to bear!  --More--");
                       wait_for(' ');
                       death(D_RELIC);
                    }
                when SURTUR_RING:
                    units = 3;
                    msg("Your nose tickles a bit.");
        when ALTERAN_CARD:
            units = 2;
            msg("You gaze intently at the card... ");
            }
        when MM:
            switch (obj->o_which) {     /* Miscellaneous Magic */
                case MM_JUG:
                    if (obj->o_ac == JUG_EMPTY) {
                        msg("The jug is empty");
                        return (-1);
                    }
                    units = 2;
                when MM_BEAKER:
                case MM_BOOK:
                    /* This is a strange case because it can go forever */
                    units = 1;
                case MM_CHOKE: /* Dust */
                when MM_HUNGER: /* Chimes */
                    units = 3;
                when MM_OPEN:
                case MM_DRUMS:
                case MM_DISAPPEAR:
                    units = 4;
                when MM_KEOGHTOM:
            /* Ointment */
                    if (obj->o_charges <= 0) {
                        msg("The jar is empty!");
                        return (-1);
                    }
                    units = 5;
                when MM_SKILLS:
                    /* A whole book! */
                    units = 15;
                when MM_CRYSTAL:
            /* Enhance player's quest */
            units = 5;
                otherwise:
                    /* What is it? */
                    units = -1;
            }
        otherwise:      units = -1;
    }

    return (units);
}