/*
    player.c - functions for dealing with special player abilities

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

bool pick_spell(struct spells spells[], int ability, int num_spells, int power,
                const char *prompt, const char *type);
/*
 * affect:
 *      cleric affecting undead
 */

void
affect(void)
{
    register struct linked_list *item;
    register struct thing *tp;
    register char *mname;
    bool see;
    coord new_pos;
    int lvl;

    if (!(player.t_ctype == C_CLERIC  ||
          (player.t_ctype == C_PALADIN && pstats.s_lvl > 4) ||
          cur_relic[HEIL_ANKH] != 0)) {
        msg("You cannot affect undead.");
        return;
    }

    new_pos.y = hero.y + player.t_newpos.y;
    new_pos.x = hero.x + player.t_newpos.x;

    if (cansee(new_pos.y, new_pos.x)) see = TRUE;
    else see = FALSE;

    /* Anything there? */
    if (new_pos.y < 0 || new_pos.y > lines-3 ||
        new_pos.x < 0 || new_pos.x > cols-1 ||
        mvwinch(mw, new_pos.y, new_pos.x) == ' ') {
        msg("Nothing to affect.");
        return;
    }

    if ((item = find_mons(new_pos.y, new_pos.x)) == 0) {
        debug("Affect what @ %d,%d?", new_pos.y, new_pos.x);
        return;
    }
    tp = THINGPTR(item);
    mname = monster_name(tp);

    if (on(player, ISINVIS) && off(*tp, CANSEE)) {
        msg("%s%s cannot see you", see ? "The " : "It",
            see ? mname : "");
        return;
    }

    if (off(*tp, TURNABLE) || on(*tp, WASTURNED)) 
        goto annoy;
    turn_off(*tp, TURNABLE);

    lvl = pstats.s_lvl;
    if (player.t_ctype == C_PALADIN && cur_relic[HEIL_ANKH] == 0) {
        lvl -= 4;
    }
    /* Can cleric kill it? */
    if (lvl >= 3 * tp->t_stats.s_lvl) {
        unsigned long test;      /* For overflow check */

        msg("You have destroyed %s%s.", see ? "the " : "it", see ? mname : "");
        test = pstats.s_exp + tp->t_stats.s_exp;

        /* Be sure there is no overflow before increasing experience */
        if (test > pstats.s_exp) pstats.s_exp = test;
        killed(item, FALSE, TRUE, TRUE);
        check_level();
        return;
    }

    /* Can cleric turn it? */
    if (rnd(100) + 1 >
         (100 * ((2 * tp->t_stats.s_lvl) - lvl)) / lvl) {
        unsigned long test;      /* Overflow test */

        /* Make the monster flee */
        turn_on(*tp, WASTURNED);        /* No more fleeing after this */
        turn_on(*tp, ISFLEE);
        runto(tp, &hero);

        /* Disrupt it */
        dsrpt_monster(tp, TRUE, TRUE);

        /* Let player know */
        msg("You have turned %s%s.", see ? "the " : "it", see ? mname : "");

        /* get points for turning monster -- but check overflow first */
        test = pstats.s_exp + tp->t_stats.s_exp/2;
        if (test > pstats.s_exp) pstats.s_exp = test;
        check_level();

        /* If monster was suffocating, 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);

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

        return;
    }

    /* Otherwise -- no go */
annoy:
    if (see && tp->t_stats.s_intel > 16)
        msg("%s laughs at you...", prname(mname, TRUE));
    else
        msg("You do not affect %s%s.", see ? "the " : "it", see ? mname : "");

    /* Annoy monster */
   if (off(*tp, ISFLEE)) runto(tp, &hero);
}

/* 
 * the cleric asks his deity for a spell
 */

void
pray(void)
{
    register int num_prayers, prayer_ability, which_prayer;

    which_prayer = num_prayers = prayer_ability =  0;

    if (player.t_ctype != C_CLERIC  && player.t_ctype != C_PALADIN &&
        cur_relic[HEIL_ANKH] == 0) {
            msg("You are not permitted to pray.");
            return;
    }
    if (cur_misc[WEAR_CLOAK] != NULL &&
        cur_misc[WEAR_CLOAK]->o_which == MM_R_POWERLESS) {
        msg("You can't seem to pray!");
        return;
    }

    prayer_ability = pstats.s_lvl * pstats.s_wisdom - 5;
    if (player.t_ctype != C_CLERIC)
        prayer_ability /= 2;

    if (cur_relic[HEIL_ANKH]) prayer_ability += 75;

    if (player.t_action != C_PRAY) {
        num_prayers = 0;

        /* Get the number of avilable prayers */
        if (pstats.s_wisdom > 16) 
            num_prayers += pstats.s_wisdom - 16;

        num_prayers += pstats.s_lvl;
        if (cur_relic[HEIL_ANKH])
            num_prayers += pstats.s_wisdom - 18;

        if (player.t_ctype != C_CLERIC) 
            num_prayers /= 2;

        if (num_prayers > MAXPRAYERS) 
            num_prayers = MAXPRAYERS;
        if (num_prayers < 1) {
            msg("You are not permitted to pray yet.");
            return;
        }

        /* Prompt for prayer */
        if (pick_spell( cleric_spells, 
                        prayer_ability, 
                        num_prayers, 
                        pray_time,
                        "offer",
                        "prayer"))
            player.t_action = C_PRAY;

        return;
    }

    /* We've waited our required praying time. */
    which_prayer = player.t_selection;
    player.t_selection = 0;
    player.t_action = A_NIL;

    if (cleric_spells[which_prayer].s_cost + pray_time > prayer_ability) {
        msg("Your prayer fails.");
        return;
    }

    msg("Your prayer has been granted. ");

    if (cleric_spells[which_prayer].s_type == TYP_POTION)
        quaff(          cleric_spells[which_prayer].s_which,
                        0,
                        cleric_spells[which_prayer].s_flag,
                        FALSE);
    else if (cleric_spells[which_prayer].s_type == TYP_SCROLL)
        read_scroll(    cleric_spells[which_prayer].s_which,
                        cleric_spells[which_prayer].s_flag,
                        FALSE);
    else if (cleric_spells[which_prayer].s_type == TYP_STICK) {
         if (!player_zap(cleric_spells[which_prayer].s_which,
                         cleric_spells[which_prayer].s_flag)) {
             after = FALSE;
             return;
         }
    }
    pray_time += cleric_spells[which_prayer].s_cost;
}

/*
 * the magician is going to try and cast a spell
 */

void
cast(void)
{
    register int spell_ability, which_spell, num_spells;

    if (player.t_ctype != C_MAGICIAN && player.t_ctype != C_RANGER) {
        msg("You are not permitted to cast spells.");
        return;
    }
    if (cur_misc[WEAR_CLOAK] != NULL &&
        cur_misc[WEAR_CLOAK]->o_which == MM_R_POWERLESS) {
        msg("You can't seem to cast spells!");
        return;
    }
    spell_ability = pstats.s_lvl * pstats.s_intel - 5;
    if (player.t_ctype != C_MAGICIAN)
        spell_ability /= 2;

    if (player.t_action != C_CAST) {
        /* 
         * Get the number of avilable spells 
         */
        num_spells = 0;
        if (pstats.s_intel > 16) 
            num_spells += pstats.s_intel - 16;

        num_spells += pstats.s_lvl;
        if (player.t_ctype != C_MAGICIAN) 
            num_spells /= 2;
        if (num_spells > MAXSPELLS)
            num_spells = MAXSPELLS;
        if (num_spells < 1) {
            msg("You are not allowed to cast spells yet.");
            return;
        }

    /* prompt for spell */
        if (pick_spell( magic_spells, 
                        spell_ability, 
                        num_spells, 
                        spell_power,
                        "cast",
                        "spell"))
            player.t_action = C_CAST;
        return;
    }

    /* We've waited our required casting time. */
    which_spell = player.t_selection;
    player.t_selection = 0;
    player.t_action = A_NIL;

    if ((spell_power + magic_spells[which_spell].s_cost) > spell_ability) {
        msg("Your attempt fails.");
        return;
    }

    msg("Your spell is successful. ");

    if (magic_spells[which_spell].s_type == TYP_POTION)
        quaff(  magic_spells[which_spell].s_which,
                0,
                magic_spells[which_spell].s_flag,
                FALSE);
    else if (magic_spells[which_spell].s_type == TYP_SCROLL)
        read_scroll(    magic_spells[which_spell].s_which,
                        magic_spells[which_spell].s_flag,
                        FALSE);
    else if (magic_spells[which_spell].s_type == TYP_STICK) {
         if (!player_zap(magic_spells[which_spell].s_which,
                         magic_spells[which_spell].s_flag)) {
             after = FALSE;
             return;
         }
    }
    spell_power += magic_spells[which_spell].s_cost;
}

/* 
 * the druid asks his deity for a spell
 */

void
chant(void)
{
    register int num_chants, chant_ability, which_chant;

    which_chant = num_chants = chant_ability = 0;

    if (player.t_ctype != C_DRUID && player.t_ctype != C_MONK) {
        msg("You are not permitted to chant.");
        return;
    }
    if (cur_misc[WEAR_CLOAK] != NULL &&
        cur_misc[WEAR_CLOAK]->o_which == MM_R_POWERLESS) {
        msg("You can't seem to chant!");
        return;
    }
    chant_ability = pstats.s_lvl * pstats.s_wisdom - 5;
    if (player.t_ctype != C_DRUID)
        chant_ability /= 2;

    if (player.t_action != C_CHANT) {
        num_chants = 0;

        /* Get the number of avilable chants */
        if (pstats.s_wisdom > 16) 
            num_chants += pstats.s_wisdom - 16;

        num_chants += pstats.s_lvl;

        if (player.t_ctype != C_DRUID) 
            num_chants /= 2;

        if (num_chants > MAXCHANTS) 
            num_chants = MAXCHANTS;

        if (num_chants < 1) {
            msg("You are not permitted to chant yet.");
            return;
        }

        /* Prompt for chant */
        if (pick_spell( druid_spells, 
                        chant_ability, 
                        num_chants, 
                        chant_time,
                        "sing",
                        "chant"))
            player.t_action = C_CHANT;

        return;
    }

    /* We've waited our required chanting time. */
    which_chant = player.t_selection;
    player.t_selection = 0;
    player.t_action = A_NIL;

    if (druid_spells[which_chant].s_cost + chant_time > chant_ability) {
        msg("Your chant fails.");
        return;
    }

    msg("Your chant has been granted. ");

    if (druid_spells[which_chant].s_type == TYP_POTION)
        quaff(          druid_spells[which_chant].s_which,
                        0,
                        druid_spells[which_chant].s_flag,
                        FALSE);
    else if (druid_spells[which_chant].s_type == TYP_SCROLL)
        read_scroll(    druid_spells[which_chant].s_which,
                        druid_spells[which_chant].s_flag,
                        FALSE);
    else if (druid_spells[which_chant].s_type == TYP_STICK) {
         if (!player_zap(druid_spells[which_chant].s_which,
                         druid_spells[which_chant].s_flag)) {
             after = FALSE;
             return;
         }
    }
    chant_time += druid_spells[which_chant].s_cost;
}

/* Constitution bonus */

int
const_bonus(void)   /* Hit point adjustment for changing levels */
{
    register int bonus;
    if (pstats.s_const > 9 && pstats.s_const < 18) 
        bonus = 0;
    else if (pstats.s_const >= 18 && pstats.s_const < 20) 
        bonus = 1;
    else if (pstats.s_const >= 20 && pstats.s_const < 26) 
        bonus = 2;
    else if (pstats.s_const >= 26 && pstats.s_const < 36) 
        bonus = 3;
    else if (pstats.s_const >= 36) 
        bonus = 4;
    else if (pstats.s_const > 7) 
        bonus = -1;
    else
        bonus = -2;
    switch(player.t_ctype) {
        case C_FIGHTER:         bonus = min(bonus, 11);
        when C_RANGER:          bonus = min(bonus,  9);
        when C_PALADIN:         bonus = min(bonus,  9);
        when C_MAGICIAN:        bonus = min(bonus,  8);
        when C_CLERIC:          bonus = min(bonus,  8);
        when C_THIEF:           bonus = min(bonus, 10);
        when C_ASSASSIN:        bonus = min(bonus, 10);
        when C_DRUID:           bonus = min(bonus,  8);
        when C_MONK:            bonus = min(bonus,  9);
        otherwise:              bonus = min(bonus,  7);
    }
    return(bonus);
}

/*
 * Give away slime-molds to monsters.  If monster is friendly, 
 * it will give you a "regular" food ration in return.  You have
 * to give a slime-mold to Alteran (a unique monster) in order to 
 * get the special "Card of Alteran" quest item.  There's no other
 * way to get this artifact and remain alive.
 */

void
give(struct thing *th)
{
    /*
     * Find any monster within one space of you
     */
    struct linked_list *ll;
    struct object *lb;
    register int x,y;
    register struct linked_list *mon = NULL;
    bool gotone = FALSE;

    if (levtype != POSTLEV) {  /* no monsters at trading post  */
        for (x = hero.x-1; x <= hero.x+1; x++) {
            for (y = hero.y-1; y <= hero.y+1; 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) {
                        gotone = TRUE;  /* found a monster to give away to */
                        th = THINGPTR(mon);
                    }
                }
            }
        }
    }
    if (gotone) {
        if ((ll=get_item(pack, "give away", ALL, FALSE, FALSE)) != NULL) {
            lb = OBJPTR(ll);
            mpos = 0;
            switch(lb->o_type) {
                case FOOD:
                    switch (lb->o_which) {
                        case E_SLIMEMOLD:   /* only slime-molds for now */
                if (on(*th, CANSELL)) {  /* quartermaster */
                msg("%s laughs at you. ");
                return;
                }
                if ((on(*th, ISFRIENDLY) || off(*th, ISMEAN)) &&
                off(*th, ISUNIQUE) && off(*th, CANSELL)) {
                turn_on(*th, ISRUN); /* we want him awake */
                                msg("%s accepts and promptly eats your gift of food.  --More--", prname(monster_name(th), TRUE));
                wait_for(' ');
                    del_pack(ll); /* delete slime-mold */
                          /* and add a food ration */
                create_obj(FALSE, FOOD, E_RATION);
                msg("%s gives you food in return and nods off to sleep. ", prname(monster_name(th), TRUE));
                turn_off(*th, ISRUN); /* put him to sleep */
                return;
                }
                else if (on(*th, CARRYCARD) && on(*th, ISUNIQUE)) {
                /* Now you get the Card of Alteran */
                msg("%s gives you a strange rectangular card. --More--", prname(monster_name(th), TRUE));
                wait_for(' ');
                    del_pack(ll); /* get rid of slime-mold */
                create_obj(FALSE, RELIC, ALTERAN_CARD);
                msg("%s bids you farewell. ", prname(monster_name(th), TRUE));
                killed(mon, FALSE, FALSE, FALSE);
                return;
                }
                else if (on(*th, ISUNIQUE) && off(*th, ISMEAN)) {
                /* Dragons */
                msg("%s is set free by your generosity. ", prname(monster_name(th), TRUE));
                    del_pack(ll);  /* get rid of it */
                /* just let him roam around */
                turn_on(*th, ISRUN);
                if (on(*th, ISFLEE)) turn_off(*th, ISFLEE);
                runto(th, &hero);
                th->t_action = A_NIL;
                return;
                }
                else if (on(*th, ISRUN) && off(*th, ISUNIQUE)) {
                /* if NOT sleeping and not a unique */
                switch (rnd(2)) {
                    case 0: msg("%s ignores you. ", prname(monster_name(th), TRUE));
                    when 1: {
                    msg("%s nips at your hand. ", prname(monster_name(th), TRUE));
                    if (rnd(100) < 10) { 
                                del_pack(ll); /* delete it */
                        if (off(*th, ISMEAN)) {
                        msg("The slime-mold makes %s sleepy. ", prname(monster_name(th), TRUE));
                        /* put him to sleep */
                        turn_off(*th, ISRUN);
                        return;
                        }
                        else {  
                        switch (rnd(2)) {
                            case 0: msg("%s's eyes roll back. ", prname(monster_name(th), TRUE));
                                when 1: msg("%s becomes wanderlust. ", prname(monster_name(th), TRUE));
                        }
                        /* just let him roam around */
                            turn_on(*th, ISRUN);
                        if (on(*th, ISFLEE))
                                        turn_off(*th, ISFLEE);
                        runto(th, &hero);
                        th->t_action = A_NIL;
                        return;
                        }
                    }
                    }
                }
                }
                else {
                msg("%s's mouth waters. ", prname(monster_name(th), TRUE));
                /* this wakes him up */
                if (off(*th, ISUNIQUE)) turn_on(*th, ISRUN);
                return;
                }
                otherwise:
                switch (rnd(3)) {  /* mention food (hint hint) */
                    case 0: msg("You cannot give away the %s! ", foods[lb->o_which].mi_name);
                    when 1: msg("The %s looks rancid! ", foods[lb->o_which].mi_name);
                    when 2: msg("You change your mind. ");
                }
                return;
                    }
            otherwise:
            switch (rnd(3)) {  /* do not mention other items */
                    case 0: msg("You feel foolish. ");
                    when 1: msg("You change your mind. ");
                    when 2: msg("%s ignores you. ", prname(monster_name(th), TRUE));
            }

        return;
            }
        }
    }
    else msg("Your efforts are futile. ");
    return;
}

/*
 * Frighten a monster.  Useful for the 'good' characters.
 */

void
fright(struct thing *th)
{
    /*
     * Find any monster within one space of you
     */
    register int x,y;
    register struct linked_list *mon;
    bool gotone = FALSE;

    if (levtype != POSTLEV) {  /* no monsters at trading post  */
        for (x = hero.x-1; x <= hero.x+1; x++) {
            for (y = hero.y-1; y <= hero.y+1; 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) {
                        gotone = TRUE;  /* found a monster to give away to */
                        th = THINGPTR(mon);
                    }
                }
            }
        }
    }
    if (gotone) {  /* If 'good' character or is wearing a ring of fear */
        if (player.t_ctype == C_RANGER || player.t_ctype == C_PALADIN ||
            player.t_ctype == C_MONK   || ISWEARING(R_FEAR) != 0) { 

            player.t_action = A_NIL;
            player.t_no_move = movement(&player);
            switch (player.t_ctype) {
                case C_FIGHTER:   /* loss of strength */
                    pstats.s_str--;
                    if (pstats.s_str < 3) pstats.s_str = 3;
                when C_RANGER:    /* loss of charisma */
        case C_PALADIN:
                    pstats.s_charisma--;
                    if (pstats.s_charisma < 3) pstats.s_charisma = 3;
                when C_CLERIC:    /* loss of wisdom */
        case C_DRUID:
                    pstats.s_wisdom--;
                    if (pstats.s_wisdom < 3) pstats.s_wisdom = 3;
                when C_MAGICIAN:  /* loss of wisdom intelligence */
            pstats.s_intel--;
                    if (pstats.s_intel < 3) pstats.s_intel = 3;
                when C_THIEF:     /* loss of dexterity */
                case C_ASSASSIN:
                    pstats.s_dext--;
                    if (pstats.s_dext < 3) pstats.s_dext = 3;
                when C_MONK:      /* loss of constitution */
                    pstats.s_const--;
                    if (pstats.s_const < 3) pstats.s_const = 3;
                otherwise:        /* this msg can induce great fear */
                    msg("You miss. ");
        }

        /* Cause a panic.  Good thru level 16. */
            if (level < 17) { 
        msg("You wave your arms and yell! ");
                do_panic(th->t_index);
                pstats.s_hpt -= (pstats.s_hpt/2)+1;
        if (pstats.s_hpt < 25) msg("You heart quivers... ");
                if (pstats.s_hpt < 1) {
            msg("Your heart stops!!  --More--");
            wait_for(' ');
            pstats.s_hpt = -1;
            death(D_FRIGHT);
        }
            return;
        }
        else {
        /* He can't do it after level 16 */
        switch (rnd(20)) { 
            case 0: case 2:
            msg("You stamp your foot!! ");
            when 4: case 8:
            msg("%s laughs at you! ",prname(monster_name(th),TRUE));
            when 10: case 12:
            msg("You forget what you are doing? ");
                otherwise:
                msg(nothing);
        }
            return;
        }
    }
        else {
        switch (rnd(25)) {
        case 0: case 2: case 4:
        msg("You motion angrily! ");
        when 6: case 8: case 10:
        msg("You can't frighten anything. ");
        when 12: case 14: case 16:
        msg("Your puff up your face. ");
        otherwise:
        msg(nothing);
        }
    return;
    }
    }
    else {
    msg("There is nothing to fear but fear itself. ");
    return;
    }
}

/* Routines for thieves */

/*
 * gsense: Sense gold
 */

void
gsense(void)
{
    /* Thief & assassin can do this, but fighter & ranger can later */
    if (player.t_ctype == C_THIEF     || player.t_ctype == C_ASSASSIN ||
        ((player.t_ctype == C_FIGHTER || player.t_ctype == C_RANGER)  &&
    pstats.s_lvl >= 12)) {
          read_scroll(S_GFIND, 0, FALSE);
    }
    else msg("You seem to have no gold sense.");
    return;
}

/*
 * xsense: Sense traps
 */

void
xsense(void)
{
    /* Only thief can do this, but assassin, fighter, & monk can later */
    if (player.t_ctype == C_THIEF   || ((player.t_ctype == C_ASSASSIN ||
    player.t_ctype == C_FIGHTER || player.t_ctype == C_MONK)      &&
    pstats.s_lvl >= 14)) {
        read_scroll(S_FINDTRAPS, 0, FALSE);
    }
    else msg("You seem not to be able to sense traps.");
    return;
}

/*
 * steal:
 *      Steal in direction given in delta
 */

void
steal(void)
{
    register struct linked_list *item;
    register struct thing *tp;
    register char *mname;
    coord new_pos;
    int thief_bonus = -50;
    bool isinvisible = FALSE;


    /* let the fighter steal after level 15 */
    if (player.t_ctype == C_FIGHTER && pstats.s_lvl < 15) {
        msg(nothing);
        return;
    }
    else if (player.t_ctype != C_THIEF    &&
        player.t_ctype  != C_ASSASSIN &&
            player.t_ctype  != C_FIGHTER) {
            msg("Only thieves and assassins can steal.");
            return;
    }
    if (on(player, ISBLIND)) {
        msg("You can't see anything.");
        return;
    }

    new_pos.y = hero.y + player.t_newpos.y;
    new_pos.x = hero.x + player.t_newpos.x;

    /* Anything there? */
    if (new_pos.y < 0 || new_pos.y > lines-3 ||
        new_pos.x < 0 || new_pos.x > cols-1 ||
        mvwinch(mw, new_pos.y, new_pos.x) == ' ') {
        msg("Nothing to steal from.");
        return;
    }

    if ((item = find_mons(new_pos.y, new_pos.x)) == NULL)
        debug("Steal from what @ %d,%d?", new_pos.y, new_pos.x);
    tp = THINGPTR(item);
    if (on(*tp, ISSTONE)) {
        msg ("You can't steal from stone!");
        return;
    }

    if (on(*tp, ISFLEE)) {
        msg("You can't get your hand in anywhere! ");
        return;
    }

    isinvisible = invisible(tp);
    if (isinvisible) mname = "creature";
    else mname = monster_name(tp);

    /* Can player steal something unnoticed? */
    if (player.t_ctype == C_THIEF)    thief_bonus = 9;
    if (player.t_ctype == C_ASSASSIN) thief_bonus = 6;
    if (player.t_ctype == C_FIGHTER)  thief_bonus = 3;
    if (on(*tp, ISUNIQUE)) thief_bonus -= 15;
    if (isinvisible) thief_bonus -= 20;
    if (on(*tp, ISINWALL) && off(player, CANINWALL)) thief_bonus -= 50;

    if (on(*tp, ISHELD) || tp->t_action == A_FREEZE ||
        rnd(100) <
        (thief_bonus + 2*dex_compute() + 5*pstats.s_lvl -
         5*(tp->t_stats.s_lvl - 3))) {
        register struct linked_list *s_item, *pack_ptr;
        int count = 0;
        unsigned long test;      /* Overflow check */

        s_item = NULL;  /* Start stolen goods out as nothing */

        /* Find a good item to take */
        for (pack_ptr=tp->t_pack; pack_ptr != NULL; pack_ptr=next(pack_ptr))
            if ((OBJPTR(pack_ptr))->o_type != RELIC &&
                pack_ptr != tp->t_using &&  /* Monster can't be using it */
                rnd(++count) == 0)
                s_item = pack_ptr;

        /* 
         * Find anything?
         */
        if (s_item == NULL) {
            msg("%s apparently has nothing to steal.", prname(mname, TRUE));
            return;
        }

        /* Take it from monster */
        if (tp->t_pack) detach(tp->t_pack, s_item);

        /* Recalculate the monster's encumberance */
        updpack(TRUE, tp);

        /* Give it to player */
        if (add_pack(s_item, FALSE) == FALSE) {
           (OBJPTR(s_item))->o_pos = hero;
           fall(s_item, TRUE);
        }

        /* Get points for stealing -- but first check for overflow */
        test = pstats.s_exp + tp->t_stats.s_exp/2;
        if (test > pstats.s_exp) pstats.s_exp = test;

        /*
         * Do adjustments if player went up a level
         */
        check_level();
    }

    else {
        msg("Your attempt fails.");

        /* Annoy monster (maybe) */
        if (rnd(40) >= dex_compute() + thief_bonus) {
            /*
             * If this is a charmed creature, there is a chance it
             * will become uncharmed.
             */
            if (on(*tp, ISCHARMED) && save(VS_MAGIC, tp, 0)) {
                msg("The eyes of %s turn clear.", prname(mname, FALSE));
                turn_off(*tp, ISCHARMED);
            }
            if (on(*tp, CANSELL)) {
                turn_off(*tp, CANSELL);
                tp->t_action = A_NIL;
                tp->t_movement = 0;
                if (rnd(100) < 50) /* make him steal something */
                    turn_on(*tp, STEALMAGIC);
                else
                    turn_on(*tp, STEALGOLD);
                if (!isinvisible)
                    msg("%s looks insulted.", prname(mname, TRUE));
            }
            runto(tp, &hero);
        }
    }
}

/*
 * Take charmed monsters with you via up or down commands.
 */

void
take_with(void)
{
    register struct thing *tp;
    register struct linked_list *item;
    struct linked_list *nitem;
    register int t;

    t = 0;
    for (item = mlist; item != NULL; item = nitem) {
    nitem = next(item);
    t++;
    if (t > 5) break;
    tp = THINGPTR(item);
        if (on(*tp, ISCHARMED)) {
            monsters[tp->t_index].m_normal = TRUE;
            turn_on(*tp, ISELSEWHERE);
            detach(mlist, item);
            attach(tlist, item);         /* remember him next level */
            check_residue(tp);
            continue;
        }
    }
}

/*
 * this routine lets the player pick the spell that they
 * want to cast regardless of character class
 * spells: spell list
 * ability: spell ability
 * num_spells: number of spells that can be cast
 * power: spell power
 * prompt: prompt for spell list
 * type: type of thing--> spell, prayer, chant
 */

bool
pick_spell(struct spells spells[], int ability, int num_spells, int power,
           const char *prompt, const char *type)
{
    bool                nohw = FALSE;
    register int        i;
    int                 curlen,
                        maxlen = 0,
                        dummy = 0,
                        which_spell,
                        spell_left;
    if (cur_misc[WEAR_CLOAK] != NULL &&
        cur_misc[WEAR_CLOAK]->o_which == MM_R_POWERLESS) {
        msg("You can't seem to start a %s!", type);
        return(FALSE);
    }

    /* Prompt for spells */
    msg("Which %s are you %sing? (* for list): ", type, prompt);

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

    else if (slow_invent) {
        register char c;

        nohw = TRUE;
        do {
            for (i=0; i<num_spells; i++) {
                msg("");
                mvwaddch(msgw, 0, 0, '[');
                waddch(msgw, (char) ((int) 'a' + i));
                wprintw(msgw, "] A %s of ", type);
                if (spells[i].s_type == TYP_POTION)
                    waddstr(msgw, p_magic[spells[i].s_which].mi_name);
                else if (spells[i].s_type == TYP_SCROLL)
                    waddstr(msgw, s_magic[spells[i].s_which].mi_name);
                else if (spells[i].s_type == TYP_STICK)
                    waddstr(msgw, ws_magic[spells[i].s_which].mi_name);
                waddstr(msgw, morestr);
                wclrtobot(msgw);
                clearok(msgw, FALSE);
                draw(msgw);
                do {
                    c = wgetch(cw);
                } while (c != ' ' && c != ESC);
                if (c == ESC)
                    break;
            }
            msg("");
            wmove(msgw, 0, 0);
            wprintw(msgw, "Which %s are you %sing? ", type, prompt);
            clearok(msgw, FALSE);
            draw(msgw);

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

        if (which_spell == (int) (ESC - 'a')) {
            mpos = 0;
            msg("");
            after = FALSE;
            return(FALSE);
        }
    }
    else {
        /* Now display the possible spells */
        wclear(hw);
        touchwin(hw);
        wmove(hw, 2, 0);
        wprintw(hw, "   Cost    %c%s", toupper(*type),type+1);
        mvwaddstr(hw, 3, 0,
                "-----------------------------------------------");
        maxlen = 47;    /* Maximum width of header */

        for (i=0; i<num_spells; i++) {
            sprintf(prbuf, "[%c]        %3d     A %s of ",
                        (char) ((int) 'a' + i), spells[i].s_cost, type);
            if (spells[i].s_type == TYP_POTION)
                strcat(prbuf, p_magic[spells[i].s_which].mi_name);
            else if (spells[i].s_type == TYP_SCROLL)
                strcat(prbuf, s_magic[spells[i].s_which].mi_name);
            else if (spells[i].s_type == TYP_STICK)
                strcat(prbuf, ws_magic[spells[i].s_which].mi_name);
            mvwaddstr(hw, i+4, 0, prbuf);

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

        spell_left = ability - power;
        if (spell_left < 0) {
            spell_left = 0;
            if (spell_left < -20) power = ability + 20;
        }
        sprintf(prbuf, "[Current %s power = %d]", type, spell_left);

        mvwaddstr(hw, 0, 0, prbuf);
        wprintw(hw, " Which %s are you %sing? ", type, prompt);
        getyx(hw, dummy, curlen);
        if (maxlen < curlen) maxlen = curlen;

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

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

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

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

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

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

    if (spells[which_spell].s_type == TYP_STICK && 
        need_dir(STICK, spells[which_spell].s_which)) {
            if (!get_dir(&player.t_newpos)) {
                after = FALSE;
                return(FALSE);
            }
    }
    player.t_selection = which_spell;
    player.t_no_move = (which_spell/3 + 1) * movement(&player);

    spell_left = dummy; /* hack to stop IRIX complaint about dummy */
                        /* not being used.                         */
    return(TRUE);
}

/*
 * opt_player:
 * Let the player know what's happening with himself
 */

void
opt_player(void)
{
    int i = 1;  /* initialize counters */
    int j = 2;

    wclear(hw);
    wmove(hw, 0, 0);
    wprintw(hw, "Current player effects:");
    wmove(hw, 2, 0);

        /*   Print a list of what is happening.
     *   If longer than 16 lines, make it two columns.
     *   Currently, a maximum of 32 (out of 39) "effects"
     *   can be happening all at once to a player.
     */

    /* 1 - Sense gold */
    if (player.t_ctype == C_THIEF    || player.t_ctype == C_ASSASSIN ||
        ((player.t_ctype == C_FIGHTER || player.t_ctype == C_RANGER) &&
    pstats.s_lvl >= 12)) { 
    wprintw(hw, "You can sense gold\n");
    i++;
    }
    /* 2 - Sense traps */
    if (player.t_ctype == C_THIEF   || ((player.t_ctype == C_ASSASSIN ||
        player.t_ctype == C_FIGHTER || player.t_ctype == C_MONK)      &&
    pstats.s_lvl >= 14)) {
    wprintw(hw, "You can sense traps\n");
    i++;
    }
    /* 3 - Steal */
    if (player.t_ctype == C_THIEF    || player.t_ctype == C_ASSASSIN || 
        (player.t_ctype == C_FIGHTER && pstats.s_lvl >= 15)) { 
    wprintw(hw, "You can steal\n");
    i++;
    }
    /* 4 - Cast spells */
    if (player.t_ctype == C_MAGICIAN ||
        (player.t_ctype == C_RANGER  && pstats.s_lvl > 1)) { 
    wprintw(hw, "You can cast spells\n");
    i++;
    }
    /* 5 - Make chants */
    if (player.t_ctype == C_DRUID ||
        (player.t_ctype == C_MONK && pstats.s_lvl > 1)) {
    wprintw(hw, "You can chant\n");
    i++;
    }
    /* 6 - Give prayers */
    if (cur_relic[HEIL_ANKH] != 0 || player.t_ctype == C_CLERIC ||
        (player.t_ctype == C_PALADIN && pstats.s_lvl > 1)) {
    wprintw(hw, "You can pray\n");
    i++;
    }
    /* 7 - Affect the undead */
    if (cur_relic[HEIL_ANKH] != 0 || player.t_ctype == C_CLERIC ||
        (player.t_ctype == C_PALADIN && pstats.s_lvl > 4)) { 
    wprintw(hw, "You can affect the undead\n");
    i++;
    }
    /* 8 - Cause fear */
    if (ISWEARING(R_FEAR) != 0   || ((player.t_ctype == C_RANGER ||
        player.t_ctype == C_PALADIN || player.t_ctype == C_MONK)    &&
        pstats.s_lvl > 1)) {
    wprintw(hw, "You are fearful\n");
    i++;
    }
    /* 9 - Confuse monster */
    if (on(player, CANHUH) != 0) {
    wprintw(hw, "You have multi-colored hands\n");
    i++;
    }
    /* 10 - Confused yourself */
    if (on(player, ISHUH) != 0) {
    wprintw(hw, "You are confused\n");
    i++;
    }                  /* really ISHUH or ISCLEAR */
    /* 11 - Clear thought */
    if (on(player, ISCLEAR) != 0) {
    wprintw(hw, "You are clear headed\n");
    i++;
    }
    /* 12 - Slow */
    if (on(player, ISSLOW) != 0) {
    wprintw(hw, "You are moving slow\n");
    i++;
    }                 /* really ISSLOW or ISHASTE */
    /* 13 - Haste */
    if (on(player, ISHASTE) != 0) {
    wprintw(hw, "You are moving fast\n");
    i++;
    }
    /* 14 - Flying */
    if (on(player, ISFLY) != 0) {
    wprintw(hw, "You are flying\n");
    i++;
    }
    /* 15 - Blind */
    if (on(player, ISBLIND) != 0) {
    wprintw(hw, "You are blind\n");
    i++;
    }                 /* really ISBLIND or CANSEE */
    /* 16 - Extra sight */
    if (on(player, CANSEE) != 0) {
    wprintw(hw, "You have extra sight\n");
    i++;
    }
    /* 17 - Invisibility */
    if (on(player, ISINVIS) != 0) {
    /* Okay, start a second column of effects to the screen. */
        if (i > 16) {
            mvwaddstr(hw, j, 37, "You are invisible");
        j++;
        }
    else {
        wprintw(hw, "You are invisible\n");
        i++;
    }
    }
    /* 18 - Regeneration and vampiric regen */
    if (ISWEARING(R_VAMPREGEN) != 0 || ISWEARING(R_REGEN) != 0) {
        if (i > 16) {
            mvwaddstr(hw, j, 37, "You have regenerative powers");
        j++;
        }
    else {
        wprintw(hw, "You have regenerative powers\n");
        i++;
    }
    }
    /* 19 - Phasing */
    if (on(player, CANINWALL) != 0) {
        if (i > 16) {
            mvwaddstr(hw, j, 37, "You can walk through walls");
        j++;
        }
    else {
        wprintw(hw, "You can walk through walls\n");
        i++;
    }
    }
    /* 20 - Skill (good or bad, it won't last) */
    if (find_slot(unskill) != 0) {
        if (i > 16) {
            mvwaddstr(hw, j, 37, "You feel skillful");
        j++;
        }
    else {
        wprintw(hw, "You feel skillful\n");
        i++;
    }
    }
    /* 21 - Stealthy */
    if (ISWEARING(R_STEALTH) != 0) {
        if (i > 16) {
            mvwaddstr(hw, j, 37, "You have stealth");
        j++;
        }
    else {
        wprintw(hw, "You have stealth\n");
        i++;
    }
    }
    /* 22 - Alertness */
    if (ISWEARING(R_ALERT) != 0) {  
        if (i > 16) {
            mvwaddstr(hw, j, 37, "You are awake and alert");
        j++;
        }
    else {
        wprintw(hw, "You are awake and alert\n");
        i++;
    }
    }
    /* 23 - Free action */
    if (ISWEARING(R_FREEDOM) != 0) {
        if (i > 16) {
            mvwaddstr(hw, j, 37, "You feel free");
        j++;
        }
    else {
        wprintw(hw, "You feel free\n");
        i++;
    }
    }
    /* 24 - Heroism */
    if (ISWEARING(R_HEROISM) != 0) {
        if (i > 16) {
            mvwaddstr(hw, j, 37, "You are brave");
        j++;
        }
    else {
        wprintw(hw, "You are brave\n");
        i++;
    }
    }
    /* 25 - Ice protection */
    if (on(player, NOCOLD) != 0) {
        if (i > 16) {
            mvwaddstr(hw, j, 37, "You are protected from ice");
        j++;
        }
    else {
        wprintw(hw, "You are protected from ice\n");
        i++;
    }
    }
    /* 26 - Fire protection */
    if (on(player, NOFIRE) != 0) {
        if (i > 16) {
            mvwaddstr(hw, j, 37, "You are protected from fire");
        j++;
        }
    else {
        wprintw(hw, "You are protected from fire\n");
        i++;
    }
    }
    /* 27 - Lightning protection */
    if (on(player, NOBOLT) != 0) {
        if (i > 16) {
            mvwaddstr(hw, j, 37, "You are protected from lightning");
        j++;
        }
    else {
        wprintw(hw, "You are protected from lightning\n");
        i++;
    }
    }
    /* 28 - Gas protection */
    if (on(player, NOGAS) != 0) {
        if (i > 16) {
            mvwaddstr(hw, j, 37, "You are protected from gas");
        j++;
        }
    else {
        wprintw(hw, "You are protected from gas\n");
        i++;
    }
    }
    /* 29 - Acid protection */
    if (on(player, NOACID) != 0) { 
        if (i > 16) {
            mvwaddstr(hw, j, 37, "You are protected from acid");
        j++;
        }
    else {
        wprintw(hw, "You are protected from acid\n");
        i++;
    }
    }
    /* 30 - Breath protection */
    if (cur_relic[YENDOR_AMULET] != 0) {
        if (i > 16) {
            mvwaddstr(hw, j, 37, "You are protected from monster breath");
        j++;
        }
    else {
        wprintw(hw, "You are protected from monster breath\n");
        i++;
    }             /* really only YENDOR or STONEBONES */
    }           
    /* 31 - Magic missile protection */
    if (cur_relic[STONEBONES_AMULET] != 0) { 
        if (i > 16) {
            mvwaddstr(hw, j, 37, "You are protected from magic missiles");
        j++;
        }
    else {
        wprintw(hw, "You are protected from magic missiles\n");
        i++;
    }
    }
    /* 32 - Sustain health */
    if (ISWEARING(R_HEALTH) != 0 && (off(player, HASDISEASE) &&
    off(player, HASINFEST) && off(player, DOROT))) {  
        if (i > 16) {     /* he's really healthy */
            mvwaddstr(hw, j, 37, "You are in good health");
        j++;
        }
    else {
        wprintw(hw, "You are in good health\n"); 
        i++;
    }
    }
    /* 33 - Being held */
    if (on(player, ISHELD) != 0) {
        if (i > 16) {
            mvwaddstr(hw, j, 37, "You are being held");
        j++;
        }
    else {
        wprintw(hw, "You are being held\n");
        i++;
    }
    }
    /* 34 - Stinks */
    if (on(player, HASSTINK) != 0) {
        if (i > 16) {
            mvwaddstr(hw, j, 37, "You are affronted by a bad smell");
        j++;
        }
    else {
        wprintw(hw, "You are affronted by a bad smell\n");
        i++;
    }
    }
    /* 35 - Any attribute that is down */
    if (pstats.s_intel    < max_stats.s_intel  ||
        pstats.s_str      < max_stats.s_str    ||
        pstats.s_wisdom   < max_stats.s_wisdom ||
        pstats.s_dext     < max_stats.s_dext   ||
        pstats.s_const    < max_stats.s_const  ||
        pstats.s_charisma < max_stats.s_charisma) {
        if (i > 16) {
            mvwaddstr(hw, j, 37, "You are afflicted");
        j++;
        }
    else {
        wprintw(hw, "You are afflicted\n");
        i++;
    }
    }
    /* 36 - Diseased */
    if (on(player, HASDISEASE) != 0) {  
        if (i > 16) {
            mvwaddstr(hw, j, 37, "You have a disease");
        j++;
        }
    else {
        wprintw(hw, "You have a disease\n");
        i++;
    }
    }
    /* 37 - Infested */
    if (on(player, HASINFEST) != 0) {  
        if (i > 16) {
            mvwaddstr(hw, j, 37, "You have an infestation");
        j++;
        }
    else {
        wprintw(hw, "You have an infestation\n");
        i++;
    }
    }
    /* 38 - Body rot */
    if (on(player, DOROT) != 0) {  
        if (i > 16) {
            mvwaddstr(hw, j, 37, "You have body rot");
        j++;
        }
    else {
        wprintw(hw, "You have body rot\n");
        i++;
    }
    }
    /* 39 - Dancing */
    if (on(player, ISDANCE) != 0) {  
        if (i > 16) {
            mvwaddstr(hw, j, 37, "You are a dancing fool");
        j++;
        }
    else {
        wprintw(hw, "You are a dancing fool\n");
        i++;
    }
    }
    if (i == 1) {
    wclear(hw);
        msg("No player effects. ");
    return;
    }
    else {
    if (i > 1 && i < 17) {
        j = 39;
            if (menu_overlay) {     /* Print the list. */
                wmove(hw, i+2, 0);
                wprintw(hw, spacemsg);
                over_win(cw, hw, i+3, j, i+2, 27, '\0');
        }
            else {
                wmove(hw, i+2, 0);
                wprintw(hw, spacemsg);
            draw(hw);
        }
    }
    else {
        i = 17;
            if (menu_overlay) {     /* Print the list. */
                wmove(hw, i+2, 0);
                wprintw(hw, spacemsg);
        if (j > 2) j = 78;
        else j = 39;
                over_win(cw, hw, i+3, j, i+2, 27, '\0');
        }
            else {
                wmove(hw, i+2, 0);
                wprintw(hw, spacemsg);
            draw(hw);
        }
    }
        wait_for(' ');
    wclear(hw);
    status(FALSE);
    touchwin(cw);
        return;
    }
}

