/*
    fight.c - All the fighting gets done here
 
    UltraRogue: The Ultimate Adventure in the Dungeons of Doom
    Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong
    All rights reserved.

    Based on "Advanced Rogue"
    Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka
    All rights reserved.

    Based on "Rogue: Exploring the Dungeons of Doom"
    Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
    All rights reserved.

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

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

/*
 * This are the beginning experience levels for all players all further
 * experience levels are computed by multiplying by 2
 */

static long e_levels[10] =
{
    143L,           /* Fighter     */
    182L,           /* Paladin     */
    169L,           /* Ranger      */
    127L,           /* Cleric      */
    154L,           /* Druid       */
    185L,           /* Magician    */
    169L,           /* Illusionist */
    112L,           /* Thief       */
    126L,           /* Assasin     */
    319L            /* Ninja       */
};

static struct matrix att_mat[11] =
{
    /* Base, Max_lvl, Factor, Offset, Range */

    {  10,     17,      2,      1,      2     },  /* fi */
    {  10,     17,      2,      1,      2     },  /* pa */
    {  10,     17,      2,      1,      2     },  /* ra */
    {  10,     19,      2,      1,      3     },  /* cl */
    {  10,     19,      2,      1,      3     },  /* dr */
    {   9,     21,      2,      1,      5     },  /* mu */
    {   9,     21,      2,      1,      5     },  /* il */
    {  10,     21,      2,      1,      4     },  /* th */
    {  10,     21,      2,      1,      4     },  /* as */
    {  10,     21,      2,      1,      4     },  /* nj */
    {   7,     25,      1,      0,      2     }   /* mn */
};

void
do_fight(coord dir, int tothedeath)
{
    int x,y;

    x = dir.x;
    y = dir.y;

    if (!tothedeath && pstats.s_hpt < max_stats.s_hpt / 3)
    {
        msg("That's not wise.");

        after = fighting = FALSE;
        return;
    }

    if (isalpha(CCHAR(winat(hero.y + y, hero.x + x))))
    {
        after = fighting = TRUE;
        do_move(y, x);
    }
    else
    {
        if (fighting == FALSE)
            msg("Nothing there.");

        after = fighting = FALSE;
    }

    return;
}

/*
    fight()
        The player attacks the monster.
*/

int
fight(coord *mp, struct object *weap, int thrown)
{
    struct thing    *tp;
    struct linked_list  *item;
    int    did_hit = TRUE;
    char    *mname;

    /* Find the monster we want to fight */

    if ((item = find_mons(mp->y, mp->x)) == NULL)
    {
        debug("Fight what @ %d,%d", mp->y, mp->x);
        return 0;
    }

    tp = THINGPTR(item);

    mname = (on(player, ISBLIND)) ? "it" : monsters[tp->t_index].m_name;

    /* Since we are fighting, things are not quiet so no healing takes place */

    player.t_rest_hpt = player.t_rest_pow = 0;
    tp->t_rest_hpt = tp->t_rest_pow = 0;

    /*  Let him know it was really a mimic (if it was one). */

    if (off(player, ISBLIND))
    {
        if (on(*tp, ISDISGUISE) && (tp->t_type != tp->t_disguise))
        {
            msg("Wait! That's a %s!", mname);
            turn_off(*tp, ISDISGUISE);
            did_hit = thrown;
        }

        if (on(*tp, CANSURPRISE))
        {
            turn_off(*tp, CANSURPRISE);
            if ((player.t_ctype == C_RANGER && rnd(6) != 0) ||
                (player.t_ctype == C_NINJA && rnd(pstats.s_lvl / 2)
                 != 0))
                msg("You notice a %s trying to hide!", mname);
            else
            {
                msg("Wait! There's a %s!", mname);
                did_hit = thrown;
            }
        }
    }

    /* Protection from Normal Missiles */

    if (thrown && on(*tp, HASMSHIELD))
    {
        msg("The %s slows as it approaches %s.",
            weaps[weap->o_which].w_name, mname);

        did_hit = FALSE;
    }

    if (did_hit)
    {
        did_hit = FALSE;

        if (!can_blink(tp) &&
            (off(*tp, MAGICHIT) || (weap != NULL &&
                   (weap->o_hplus > 0 || weap->o_dplus > 0))) &&
            (off(*tp, BMAGICHIT) || (weap != NULL &&
                   (weap->o_hplus > 2 || weap->o_dplus > 2))) &&
            roll_em(&player, tp, weap, thrown, cur_weapon))
        {
            did_hit = TRUE;
            tp->t_wasshot = TRUE;

            if (thrown)
            {
                if (weap != NULL && weap->o_type == WEAPON
                    && weap->o_which == GRENADE)
                {
                    hearmsg("BOOOM!");
                    aggravate();
                }

                thunk(weap, mname);
            }
            else
                hit(mname);

            /* hitting a friendly monster is curtains */

            if (on(*tp, ISFRIENDLY))
            {
                turn_off(*tp, ISFRIENDLY);
                turn_on(*tp, ISMEAN);
            }

            /* Charmed monsters become uncharmed */

            if (on(*tp, ISCHARMED))
            {
                turn_off(*tp, ISCHARMED);
                turn_on(*tp, ISMEAN);
            }

            /*
             * If the player hit a rust monster, he better have a
             * + weapon
            */

            if (on(*tp, CANRUST))
            {
                if (!thrown && (weap != NULL) &&
                    (weap->o_flags & ISMETAL) &&
                    !(weap->o_flags & ISPROT) &&
                    !(weap->o_flags & ISSILVER) &&
                (weap->o_hplus < 1) && (weap->o_dplus < 1))
                {
                    if (rnd(100) < 50)
                        weap->o_hplus--;
                    else
                        weap->o_dplus--;

                    msg("Your %s weakens!", weaps[weap->o_which].w_name);
                }
                else if (!thrown && weap != NULL && (weap->o_flags & ISMETAL))
                    msg("The rust vanishes from your %s!",
                        weaps[weap->o_which].w_name);
            }

            /* flammable monsters die from burning weapons */

            if (thrown && on(*tp, CANBBURN) &&
                (weap->o_flags & CANBURN) &&
                !save_throw(VS_WAND, tp))
            {
                msg("The %s vanishes in a ball of flame.",
                    monsters[tp->t_index].m_name);

                tp->t_stats.s_hpt = 0;
            }

            /* spores explode and infest hero  */

            if (on(*tp, CANSPORE))
            {
                msg("The %s explodes in a cloud of dust.",
                    monsters[tp->t_index].m_name);

                if (is_wearing(R_HEALTH) ||
                    player.t_ctype == C_PALADIN ||
                    (player.t_ctype == C_NINJA && pstats.s_lvl
                     > 6) ||
                    thrown && rnd(50) > 0 ||
                    rnd(20) > 0)
                {
                    msg("The dust makes it hard to breath.");
                }
                else
                {
                    msg("You have contracted a parasitic infestation!");

                    infest_dam++;
                    turn_on(player, HASINFEST);
                }

                tp->t_stats.s_hpt = 0;
            }

            /* fireproof monsters laugh at you when burning weapon hits */

            if (thrown && on(*tp, NOFIRE) && (weap->o_flags & CANBURN))
                msg("The %s laughs as the %s bounces.",
                    monsters[tp->t_index].m_name,
                    weaps[weap->o_which].w_name);

            /* sharp weapons have no effect on NOSHARP monsters */

            if (on(*tp, NOSHARP) && (weap != NULL) &&
                (weap->o_flags & ISSHARP))
            {
                msg("The %s has no effect on the %s!",
                    weaps[weap->o_which].w_name,
                    monsters[tp->t_index].m_name);

                fighting = FALSE;
            }

            /* metal weapons pass through NOMETAL monsters */

            if (on(*tp, NOMETAL) && (weap != NULL) &&
                (weap->o_flags & ISMETAL))
            {
                msg("The %s passes through the %s!",
                    weaps[weap->o_which].w_name,
                    monsters[tp->t_index].m_name);

                fighting = FALSE;
            }

            /*
             * If the player hit something that shrieks, wake the
             * dungeon
             */

            if (on(*tp, CANSHRIEK))
            {
                turn_off(*tp, CANSHRIEK);

                if (on(player, CANHEAR))
                {
                    msg("You are stunned by the %s's shriek.", mname);
                    no_command += 4 + rnd(8);
                }
                else if (off(player, ISDEAF))
                    msg("The %s emits a piercing shriek.", mname);
                else
                    msg("The %s seems to be trying to make some noise.", mname);

                aggravate();

                if (rnd(wizard ? 3 : 39) == 0 && cur_armor
                    != NULL
                    && cur_armor->o_which == CRYSTAL_ARMOR)
                {
                    struct linked_list  *itm;
                    struct object   *obj;

                    for (itm = pack; itm != NULL; itm = next(itm))
                    {
                        obj = OBJPTR(itm);

                        if (obj == cur_armor)
                            break;
                    }

                    if (itm == NULL)
                        debug("Can't find crystalline armor being worn.");
                    else
                    {
                        msg("Your armor shatters from the shriek.");
                        cur_armor = NULL;
                        del_pack(itm);
                    }
                }
            }

            /*
             * If the player hit something that can surprise, it
             * can't now
             */

            if (on(*tp, CANSURPRISE))
                turn_off(*tp, CANSURPRISE);

            /*
             * If the player hit something that can summon, it
             * will try to
             */

            summon_help(tp, NOFORCE);

            /* Can the player confuse? */

            if (on(player, CANHUH) && !thrown)
            {
                seemsg("Your hands stop glowing red!");
                seemsg("The %s appears confused.", mname);
                turn_on(*tp, ISHUH);
                turn_off(player, CANHUH);
            }

            /* Merchants just disappear if hit */

            /*
             * increases prices and curses objects from now on
             * though
             */

            if (on(*tp, CANSELL))
            {
                msg("The %s disappears with his wares with a BOOM and a flash.",
                     mname);
                killed(NULL, item, NOMESSAGE, NOPOINTS);
                aggravate();
                luck++;
            }
            else if (tp->t_stats.s_hpt <= 0)
                killed(&player, item, MESSAGE, POINTS);

            /*
             * If the monster is fairly intelligent and about to
             * die, it may turn tail and run.
             */

            else if ((tp->t_stats.s_hpt < max(10, tp->maxstats.s_hpt / 10)) &&
                 (rnd(25) < tp->t_stats.s_intel))
            {
                turn_on(*tp, ISFLEE);

                /* If monster was suffocating, stop it */

                if (on(*tp, DIDSUFFOCATE))
                {
                    turn_off(*tp, DIDSUFFOCATE);
                    extinguish_fuse(FUSE_SUFFOCATE);
                }

                /* If monster held us, stop it */

                if (on(*tp, DIDHOLD) && (--hold_count == 0))
                    turn_off(player, ISHELD);

                turn_off(*tp, DIDHOLD);

                if (on(*tp, CANTELEPORT))
                {
                    int rm;

                    /*
                     * Erase the monster from the old
                     * position
                     */

                    if (isalpha(mvwinch(cw, tp->t_pos.y, tp->t_pos.x)))
                        mvwaddch(cw, tp->t_pos.y, tp->t_pos.x, tp->t_oldch);

                    mvwaddch(mw, tp->t_pos.y, tp->t_pos.x, ' ');

                    /* Get a new position */

                    do
                    {
                        rm = rnd_room();
                        rnd_pos(&rooms[rm], &tp->t_pos);
                    }
                    while (winat(tp->t_pos.y, tp->t_pos.x) != FLOOR);

                    /* Put it there */

                    mvwaddch(mw, tp->t_pos.y, tp->t_pos.x, tp->t_type);
                    tp->t_oldch = CCHAR( mvwinch(cw, tp->t_pos.y, tp->t_pos.x) );
                    seemsg("The %s seems to have disappeared!", mname);
                }
            }
        }
        else if (thrown)
            bounce(weap, mname);
        else
            miss(mname);
    }

    if (curr_mons)
        chase_it(mp,&player);   /* after so that backstabbing can happen */

    count = 0;

    return(did_hit);
}

/*
    attack()
        The monster attacks the player
*/

int
attack(struct thing *mp, struct object *weapon, int thrown)
{
    char    *mname;
    int    did_hit = FALSE;

    /* If the monster is in a wall, it cannot attack */

    if (on(*mp, ISINWALL))
        return (FALSE);

    /* If two monsters start to gang up on our hero, stop fight mode */

    if (fighting)
    {
        if (beast == NULL)
            beast = mp;
        else if (beast != mp)
            fighting = FALSE;
    }

    /*
       Since this is an attack, stop running and any healing that was
       going on at the time.
    */

    running = FALSE;
    player.t_rest_hpt = player.t_rest_pow = 0;
    mp->t_rest_hpt = mp->t_rest_pow = 0;

    if (on(*mp, ISDISGUISE) && off(player, ISBLIND))
        turn_off(*mp, ISDISGUISE);
    mname = on(player, ISBLIND) ? "the monster" :
        monsters[mp->t_index].m_name;

    if (roll_em(mp, &player, weapon, thrown, wield_weap(weapon, mp)) &&
        (!thrown || off(player, HASMSHIELD)))
    {
        did_hit = TRUE;

        m_thunk(weapon, mname);

        if (pstats.s_hpt <= 0)
        {
            death(mp->t_index); /* Bye bye life ... */
            return TRUE;
        }

        /* surprising monsters appear after they shoot at you */

        if (thrown && on(*mp, CANSURPRISE))
            turn_off(*mp, CANSURPRISE);
        else if (!thrown)
        {

            /*
               If a vampire hits, it may take half your hit
               points
            */

            if ( on(*mp, CANSUCK) && !save(VS_MAGIC) )
            {
                if (pstats.s_hpt == 1)
                {
                    death(mp->t_index);
                    return TRUE;
                }
                else
                {
                    pstats.s_hpt /= 2;
                    msg("You feel your life force being drawn from you.");
                }
            }

            /*
               strong monsters can shatter or gong crystalline
               armor
            */

            if (cur_armor != NULL && cur_armor->o_which == CRYSTAL_ARMOR)
            {
                if (rnd(mp->t_stats.s_str + (cur_armor->o_ac / 2)) > 20)
                {
                    struct linked_list  *item;
                    struct object   *obj;

                    for (item = pack; item != NULL; item = next(item))
                    {
                        obj = OBJPTR(item);

                        if (obj == cur_armor)
                            break;
                    }

                    if (item == NULL)
                        debug("Can't find crystalline armor being worn.");
                    else
                    {
                        msg("Your armor is shattered by the blow.");
                        cur_armor = NULL;
                        del_pack(item);
                    }
                }
                else if (rnd(mp->t_stats.s_str) > 15)
                {
                    msg("Your armor rings from the blow.");
                    aggravate();
                }
            }

            /* Stinking monsters reduce the player's strength */

            if (on(*mp, CANSTINK))
            {
                turn_off(*mp, CANSTINK);

                if (player.t_ctype != C_PALADIN
                    && !(player.t_ctype == C_NINJA && pstats.s_lvl > 12)
                    && !save(VS_POISON))
                {
                    if (on(player, CANSCENT))
                    {
                        msg("You pass out from the stench of the %s.", mname);
                        no_command += 4 + rnd(8);
                    }
                    else if (off(player, ISUNSMELL))
                        msg("The stench of the %s sickens you.", mname);

                    if (on(player, HASSTINK))
                        lengthen_fuse(FUSE_UNSTINK, STINKTIME);
                    else
                    {
                        turn_on(player, HASSTINK);
                        light_fuse(FUSE_UNSTINK, 0, STINKTIME, AFTER);
                    }
                }
            }

            /* chilling monster reduces strength permanently */

            if (on(*mp, CANCHILL) &&
                (cur_armor == NULL || cur_armor->o_which != CRYSTAL_ARMOR))
            {
                msg("You cringe at the %s's chilling touch.", mname);

                if (!is_wearing(R_SUSABILITY))
                {
                    chg_str(-1, FALSE, TRUE);

                    if (lost_str == 0)
                        light_fuse(FUSE_RES_STRENGTH, 0, CHILLTIME, AFTER);
                    else
                        lengthen_fuse(FUSE_RES_STRENGTH, CHILLTIME);
                }
            }

            /* itching monsters reduce dexterity (temporarily) */

            if (on(*mp, CANITCH) && player.t_ctype != C_PALADIN
                && !(player.t_ctype == C_NINJA && pstats.s_lvl > 12)
                && !save(VS_POISON))
            {
                msg("The claws of the %s scratch you!", mname);

                if (is_wearing(R_SUSABILITY))
                    msg("The scratch has no effect.");
                else
                {
                    msg("You feel a burning itch.");
                    turn_on(player, HASITCH);
                    chg_dext(-1, FALSE, TRUE);
                    light_fuse(FUSE_UNITCH, 0, roll(4, 6), AFTER);
                }
            }

            /* a hugging monster may SQUEEEEEEEZE */

            if (on(*mp, CANHUG) &&
                (cur_armor == NULL || cur_armor->o_which != CRYSTAL_ARMOR))
            {
                if (roll(1, 20) >= 18 || roll(1, 20) >= 18)
                {
                    msg("The %s squeezes you against itself.", mname);

                    if ((pstats.s_hpt -= roll(2, 8)) <= 0)
                    {
                        death(mp->t_index);
                        return TRUE;
                    }
                }
            }

            /* a trampling monster may step on the player */

            if (on(*mp, CANTRAMPLE))
            {
                if (roll(1, 20) >= 16 || roll(1, 20) >= 16)
                {
                    msg("The %s steps on you.", mname);

                    if ((pstats.s_hpt -= roll(3, mp->t_stats.s_lvl)) <= 0)
                    {
                        death(mp->t_index);
                        return TRUE;
                    }
                }
            }

            /* a disease-carrying monster may transmit the disease */

            if (on(*mp, CANDISEASE) &&
                (rnd(pstats.s_const) < mp->t_stats.s_lvl) &&
                off(player, HASDISEASE))
            {

                if (is_wearing(R_HEALTH)
                    || (player.t_ctype == C_PALADIN)
                    || (player.t_ctype == C_NINJA &&
                    pstats.s_lvl > 6))
                    msg("The wound heals quickly.");
                else
                {
                    turn_on(player, HASDISEASE);
                    light_fuse(FUSE_CURE_DISEASE,0,roll(4,4) * SICKTIME, AFTER);
                    msg("You have contracted a disease!");
                }
            }

            /* a rust monster will weaken your armor */

           if (on(*mp, CANRUST))
           {
                if (cur_armor != NULL &&
                    cur_armor->o_which != SOFT_LEATHER &&
                    cur_armor->o_which != HEAVY_LEATHER &&
                    cur_armor->o_which != CUIRBOLILLI &&
                    cur_armor->o_which != PADDED_ARMOR &&
                    cur_armor->o_which != CRYSTAL_ARMOR &&
                    cur_armor->o_which != MITHRIL &&
                    !(cur_armor->o_flags & ISPROT) &&
                    cur_armor->o_ac < pstats.s_arm + 1)
                {
                    msg("Your armor weakens!");
                    cur_armor->o_ac++;
                }
                else if (cur_armor != NULL &&
                     (cur_armor->o_flags & ISPROT) &&
                    cur_armor->o_which != SOFT_LEATHER &&
                     cur_armor->o_which != HEAVY_LEATHER &&
                     cur_armor->o_which != CUIRBOLILLI &&
                    cur_armor->o_which != PADDED_ARMOR &&
                     cur_armor->o_which != CRYSTAL_ARMOR &&
                     cur_armor->o_which != MITHRIL)
                    msg("The rust vanishes instantly!");
            }

            /* If a surprising monster hit you, you can see it now */

            if (on(*mp, CANSURPRISE))
                turn_off(*mp, CANSURPRISE);

            /* an infesting monster will give you a parasite or rot */

            if (on(*mp, CANINFEST) && rnd(pstats.s_const) < mp->t_stats.s_lvl)
            {
                if (is_wearing(R_HEALTH) || (player.t_ctype == C_PALADIN)
                    || (player.t_ctype == C_NINJA && pstats.s_lvl > 6))
                    msg("The wound quickly heals.");
                else
                {
                    turn_off(*mp, CANINFEST);
                    msg("You have contracted a parasitic infestation!");
                    infest_dam++;
                    turn_on(player, HASINFEST);
                }
            }

            /* Some monsters have poisonous bites */

            if (on(*mp, CANPOISON) && !save(VS_POISON))
            {
                if (is_wearing(R_SUSABILITY) || (player.t_ctype == C_PALADIN)
                    || (player.t_ctype == C_NINJA && pstats.s_lvl > 12))
                    msg("The sting has no effect on you!");
                else
                {
                    chg_str(-1, FALSE, FALSE);
                    msg("You feel a sting in your arm and now feel weaker.");
                }
            }

            /* a hideous monster may cause fear by touching */

            if (on(*mp, TOUCHFEAR))
            {
                turn_off(*mp, TOUCHFEAR);

                if (!save(VS_WAND)&&!(on(player,ISFLEE)&&(player.t_chasee==mp)))
                {
                    if (off(player, SUPERHERO)
                         && (player.t_ctype != C_PALADIN))
                    {
                        turn_on(player, ISFLEE);
                        player.t_ischasing = FALSE;
                        player.t_chasee = mp;
                        msg("The %s's touch terrifies you.", mname);
                    }
                    else
                        msg("The %s's touch feels cold and clammy.", mname);
                }
            }

            /* some monsters will suffocate our hero */

            if (on(*mp, CANSUFFOCATE) && (rnd(100) < 15) &&
                (find_slot(FUSE_SUFFOCATE, FUSE) == NULL))
            {
                turn_on(*mp, DIDSUFFOCATE);
                msg("The %s is beginning to suffocate you.",
                    mname);
                light_fuse(FUSE_SUFFOCATE, 0, roll(4, 2), AFTER);
            }

            /* don't look now, you will get turned to stone */

            if (on(*mp, TOUCHSTONE))
            {
                turn_off(*mp, TOUCHSTONE);

                if (on(player, CANINWALL))
                    msg("The %s's touch has no effect.", mname);
                else
                {
                    if (!save(VS_PETRIFICATION) && rnd(100) < 3)
                    {
                        msg("Your body begins to solidify.");
                        msg("You are turned to stone !!! --More--");
                        wait_for(' ');
                        death(D_PETRIFY);
                        return TRUE;
                    }
                    else
                    {
                        msg("The %s's touch stiffens your limbs.", mname);
                        no_command = rnd(STONETIME) + 2;
                    }
                }
            }

            /* Undead might drain energy levels */

            if ((on(*mp, CANDRAIN) || on(*mp, DOUBLEDRAIN)) && rnd(100) < 15)
            {
                if (is_carrying(TR_AMULET))
                   msg("The Amulet protects you from the %s's negative energy!",
                       mname);
                else
                {
                    lower_level(mp->t_index);

                    if (on(*mp, DOUBLEDRAIN))
                        lower_level(mp->t_index);
                }

                turn_on(*mp, DIDDRAIN);
            }

            /* permanently drain a wisdom point */

            if (on(*mp, DRAINWISDOM) && rnd(100) < 15)
            {
                int ring_str;   /* Value of ring strengths */

                /* Undo any ring changes */

                ring_str = ring_value(R_ADDWISDOM) +
                    (on(player, POWERWISDOM) ? 10 : 0);

                pstats.s_wisdom -= ring_str;
				
                msg("You feel slightly less wise now.");
				
                pstats.s_wisdom = max(pstats.s_wisdom - 1, 3);
                max_stats.s_wisdom = pstats.s_wisdom;

                /* Now put back the ring changes */

                pstats.s_wisdom += ring_str;

            }

            /* permanently drain a intelligence point */

            if (on(*mp, DRAINBRAIN) && rnd(100) < 15)
            {
                int ring_str;   /* Value of ring strengths */

                /* Undo any ring changes */

                ring_str = ring_value(R_ADDINTEL) +
                    (on(player, POWERINTEL) ? 10 : 0);

                pstats.s_intel -= ring_str;

                msg("You feel slightly less intelligent now.");
                pstats.s_intel = max(pstats.s_intel - 1, 3);
                max_stats.s_intel = pstats.s_intel;

                /* Now put back the ring changes */

                pstats.s_intel += ring_str;
            }

            /* Violet fungi and others hold the hero */

            if (on(*mp, CANHOLD) && off(*mp, DIDHOLD)
                && !is_wearing(R_FREEDOM))
            {
                turn_on(player, ISHELD);
                turn_on(*mp, DIDHOLD);
                hold_count++;
            }

            /* suckers will suck blood and run away */

            if (on(*mp, CANDRAW))
            {
                turn_off(*mp, CANDRAW);
                turn_on(*mp, ISFLEE);
                msg("The %s sates itself with your blood.", mname);

                if ((pstats.s_hpt -= 12) <= 0)
                {
                    death(mp->t_index);
                    return TRUE;
                }
            }

            /* el stinkos will force a reduction in strength */

            if (on(*mp, CANSMELL))
            {
                turn_off(*mp, CANSMELL);

                if (save(VS_MAGIC) || is_wearing(R_SUSABILITY))
                    msg("You smell an unpleasant odor.");
                else
                {
                    int   odor_str = -(rnd(6) + 1);

                    if (on(player, CANSCENT))
                    {
                        msg("You pass out from a foul odor.");
                        no_command += 4 + rnd(8);
                    }
                    else if (off(player, ISUNSMELL))
                        msg("You are overcome by a foul odor.");

                    if (lost_str == 0)
                    {
                        chg_str(odor_str, FALSE, TRUE);
                        light_fuse(FUSE_RES_STRENGTH, 0, SMELLTIME, AFTER);
                    }
                    else
                        lengthen_fuse(FUSE_RES_STRENGTH, SMELLTIME);
                }
            }

            /* Paralyzation */

            if (on(*mp, CANPARALYZE))
            {
                turn_off(*mp, CANPARALYZE);

                if (!save(VS_PARALYZATION) && no_command == 0)
                {
                    if (on(player, CANINWALL))
                        msg("The %s's touch has no effect.", mname);
                    else
                    {
                        msg("The %s's touch paralyzes you.", mname);
                        no_command = FREEZETIME;
                    }
                }
            }

            /* Rotting */

            if (on(*mp, CANROT))
            {
                turn_off(*mp, CANROT);
                turn_on(*mp, DOROT);
            }

            /* some monsters steal gold */

            if (on(*mp, STEALGOLD))
            {
                long    lastpurse;
                struct linked_list  *item;
                struct object   *obj;

                lastpurse = purse;
                purse = (purse > GOLDCALC) ? purse - GOLDCALC : 0L;

                if (!save(VS_MAGIC))
                    purse = (purse > (4*GOLDCALC)) ? purse-(4*GOLDCALC) : 0L;

                if (purse != lastpurse)
                {
                    msg("Your purse feels lighter.");

                    /* Give the gold to the thief */

                    for (item = mp->t_pack; item != NULL; item = next(item))
                    {
                        obj = OBJPTR(item);

                        if (obj->o_type == GOLD)
                        {
                            obj->o_count += lastpurse - purse;
                            break;
                        }
                    }

                    /* Did we do it? */

                    if (item == NULL)  /* Then make some */
                    {
                        item = new_item(sizeof *obj);
                        obj = OBJPTR(item);
                        obj->o_type = GOLD;
                        obj->o_count = lastpurse - purse;
                        obj->o_hplus = obj->o_dplus = 0;
                        obj->o_damage = obj->o_hurldmg = "0d0";
                        obj->o_ac = 11;
                        obj->o_group = 0;
                        obj->o_flags = 0;
                        obj->o_mark[0] = '\0';
                        obj->o_pos = mp->t_pos;

                        attach(mp->t_pack, item);
                    }
                }

                if (rnd(2))
                    turn_on(*mp, ISFLEE);

                turn_on(*mp, ISINVIS);
            }

            /* other monsters steal magic */

            if (on(*mp, STEALMAGIC))
            {
                struct linked_list  *list, *stealit;
                struct object   *obj;
                int worth = 0;

                stealit = NULL;

                for (list = pack; list != NULL; list = next(list))
                {
                    obj = OBJPTR(list);

                    if (rnd(33) == 0) /* some stuff degrades */
                    {
                        if (obj->o_flags & ISBLESSED)
                            obj->o_flags &= ~ISBLESSED;
                        else
                            obj->o_flags |= ISCURSED;

                        msg("You feel nimble fingers reach into you pack.");
                    }

                    if ((obj != cur_armor &&
                         obj != cur_weapon &&
                         obj != cur_ring[LEFT_1] &&
                         obj != cur_ring[LEFT_2] &&
                         obj != cur_ring[LEFT_3] &&
                         obj != cur_ring[LEFT_4] &&
                         obj != cur_ring[LEFT_5] &&
                         obj != cur_ring[RIGHT_1] &&
                         obj != cur_ring[RIGHT_2] &&
                         obj != cur_ring[RIGHT_3] &&
                         obj != cur_ring[RIGHT_4] &&
                         obj != cur_ring[RIGHT_5] &&
                         !(obj->o_flags & ISPROT) &&
                         is_magic(obj)
                         || level > 45)
                        && get_worth(obj) > worth)
                    {
                        stealit = list;
                        worth = get_worth(obj);
                    }
                }

                if (stealit != NULL)
                {
                    struct object   *newobj;

                    newobj = OBJPTR(stealit);

                    if (newobj->o_count > 1 && newobj->o_group == 0)
                    {
                        int oc;
                        struct linked_list  *nitem;
                        struct object   *op;

                        oc = --(newobj->o_count);
                        newobj->o_count = 1;
                        nitem = new_item(sizeof *newobj);
                        op = OBJPTR(nitem);
                        *op = *newobj;

                       msg("The %s stole %s!",mname,inv_name(newobj,LOWERCASE));
                        newobj->o_count = oc;
                        attach(mp->t_pack, nitem);
                    }
                    else
                    {
                       msg("The %s stole %s!",mname,inv_name(newobj,LOWERCASE));
                        newobj->o_flags &= ~ISCURSED;
                        dropcheck(newobj);
                        rem_pack(newobj);
                        attach(mp->t_pack, stealit);

                        if (newobj->o_type == ARTIFACT)
                            has_artifact &= ~(1 << newobj->o_which);
                    }

                    if (newobj->o_flags & ISOWNED)
                    {
                        turn_on(*mp, NOMOVE);
                        msg("The %s is transfixed by your ownership spell.",
                            mname);
                    }

                    if (rnd(2))
                        turn_on(*mp, ISFLEE);

                    turn_on(*mp, ISINVIS);
                    updpack();
                }
            }
        }
    }
    else /* missed */
    {
        /* If the thing was trying to surprise, no good */

        if (on(*mp, CANSURPRISE))
            turn_off(*mp, CANSURPRISE);

        m_bounce(weapon, mname);
    }

    count = 0;

    status(FALSE);

    return(did_hit);
}


/*
    mon_mon_attack()
        A monster attacks another monster
*/

int
mon_mon_attack(struct thing *attacker, struct linked_list *mon, struct object *weapon, int thrown)
{
    struct thing *attackee = THINGPTR(mon);
    int           did_hit  = FALSE;
    int           ee_visible = cansee(attackee->t_pos.y, attackee->t_pos.x);
    int           er_visible = cansee(attacker->t_pos.y, attacker->t_pos.x);
    char         *mname1   = monsters[attacker->t_index].m_name;
    char         *mname2   = monsters[attackee->t_index].m_name;

    /* Similar monsters don't hit each other */

    if (attacker->t_index == attackee->t_index)
    {
        if (attacker == THINGPTR(fam_ptr) && er_visible)
            msg("Master, I cannot hit one of my brethren.");
        if (!thrown && rnd(100) - attacker->t_stats.s_charisma + luck < 0) 
		{
            if (er_visible)
                msg("Your %s has made a new ally.", mname1);
				
            turn_on(*attackee, ISCHARMED);
		}

        return(FALSE);
    }

    /* stop running and any healing */

    attackee->t_rest_hpt = attackee->t_rest_pow = 0;
    attacker->t_rest_hpt = attacker->t_rest_pow = 0;

    if (roll_em(attacker, attackee, weapon, thrown,
        wield_weap(weapon, attacker)))
    {
        did_hit = TRUE;

        if (ee_visible && on(*attackee, CANSURPRISE))
            turn_off(*attackee, CANSURPRISE);

        if (ee_visible && er_visible && weapon != NULL)
            msg("The %s's %s hits the %s.", mname1,
                weaps[weapon->o_which].w_name, mname2);
        else if (ee_visible && er_visible)
            msg("The %s hits the %s.", mname1, mname2);

        if (attackee->t_stats.s_hpt <= 0)
        {
            killed(attacker, mon, MESSAGE,
                 on(*attacker, ISFAMILIAR) ? POINTS : NOPOINTS);
            return(TRUE);
        }
    } 
    else  /* missed */
	{
	    did_hit = FALSE;
		
		if (ee_visible && er_visible && weapon != NULL)
            msg("The %s's %s misses the %s.", mname1,
                weaps[weapon->o_which].w_name, mname2);
        else if (ee_visible && er_visible)
            msg("The %s misses the %s.", mname1, mname2);
	}
	
	if (er_visible && !ee_visible)
        msg("The %s struggles with something.",mname1);

    if (off(*attackee, ISMEAN) && off(*attackee, ISFAMILIAR))
        turn_on(*attackee, ISRUN);

    count = 0;

    status(FALSE);

    return(did_hit);
}


/*
    swing()
        returns true if the swing hits
*/

int
swing(int class, int at_lvl, int op_arm, int wplus)
{
    int res = rnd(20) + 1;
    int need;

    need = att_mat[class].base -
        att_mat[class].factor *
        ((min(at_lvl, att_mat[class].max_lvl) -
          att_mat[class].offset) / att_mat[class].range) +
        (10 - op_arm);

    if (need > 20 && need <= 25)
        need = 20;

    return(res + wplus >= need);
}

/*
    init_exp()
        set up initial experience level change threshold
*/

void
init_exp(void)
{
    max_stats.s_exp = e_levels[player.t_ctype];
}

/*
    next_exp_level()
        Do the next level arithmetic Returns number of levels to jump
*/

int
next_exp_level(int print_message)
{
    int level_jump = 0;

    while (pstats.s_exp >= max_stats.s_exp)
    {
        pstats.s_exp -= max_stats.s_exp;    /* excess experience points */
        level_jump++;

        if (max_stats.s_exp < 0x3fffffffL)  /* 2^30 - 1 */
            max_stats.s_exp *= 2L;  /* twice as many for next */
    }

    if (print_message)
        msg("You need %d more points to attain the %stitle of %s.",
            max_stats.s_exp - pstats.s_exp,
            (pstats.s_lvl > 14 ? "next " : ""),
            cnames[player.t_ctype][min(pstats.s_lvl, 14)]);

    return(level_jump);
}

/*
    check_level()
        Check to see if the guy has gone up a level.
*/

void
check_level(void)
{
    int num_jumped, j, add;
    int nsides;

    if ((num_jumped = next_exp_level(NOMESSAGE)) <= 0)
        return;

    pstats.s_lvl += num_jumped; /* new experience level */

    switch (player.t_ctype)
    {
        case C_MAGICIAN:
        case C_ILLUSION: nsides = 4;
                         break;
        case C_THIEF:
        case C_ASSASIN:
        case C_NINJA:
        case C_MONSTER:
        default:         nsides = 6;
                         break;
        case C_CLERIC:
        case C_DRUID:    nsides = 8;
                         break;

        case C_FIGHTER:
        case C_PALADIN:
        case C_RANGER:
                         nsides = 12;
                         break;
    }

    /* Take care of multi-level jumps */

    for (add = 0, j = 0; j < num_jumped; j++)
    {
        int increase = roll(1, nsides) + const_bonus();

        add += max(1, increase);
    }

    max_stats.s_hpt += add;
    pstats.s_hpt += add;

    msg("Welcome, %s, to level %d.",
        cnames[player.t_ctype][min(pstats.s_lvl - 1, 14)], pstats.s_lvl);

    next_exp_level(MESSAGE);

    /* Now add new spell points and learn new spells */

    nsides = 16 - nsides;

    for (add = 0, j = 0; j < num_jumped; j++)
    {
        int increase = roll(1, nsides) + int_wis_bonus();

        add += max(1, increase);
    }

    max_stats.s_power += add;
    pstats.s_power += add;

    learn_new_spells();

    /* Create a more powerful familiar (if player has one) */

    if (on(player, HASFAMILIAR) && on(player, CANSUMMON))
        summon_monster((short) 0, FAMILIAR, NOMESSAGE);
}

/*
    roll_em()
        Roll several attacks
*/

int
roll_em(struct thing *att_er, struct thing *def_er, struct object *weap, int thrown, struct object *my_weapon)
{
    struct stats *att = &att_er->t_stats;
    struct stats *def = &def_er->t_stats;
    int ndice, nsides, nplus, def_arm;
    char *cp;
    int prop_hplus = 0, prop_dplus = 0;
    int is_player = (att_er == &player);
    int did_hit = FALSE;

    if (weap == NULL)
        cp = att->s_dmg;
    else if (!thrown)
        cp = weap->o_damage;
    else if ((weap->o_flags & ISMISL) && my_weapon != NULL &&
         my_weapon->o_which == weap->o_launch)
    {
        cp = weap->o_hurldmg;
        prop_hplus = my_weapon->o_hplus;
        prop_dplus = my_weapon->o_dplus;
    }
    else
        cp = (weap->o_flags & ISMISL ? weap->o_damage :
            weap->o_hurldmg);

    for (;;)
    {
        int damage;
        int hplus = prop_hplus + (weap == NULL ? 0 : weap->o_hplus);
        int dplus = prop_dplus + (weap == NULL ? 0 : weap->o_dplus);

        /* Is attacker weak? */

        if (on(*att_er, HASSTINK))
            hplus -= 2;

        if (is_player)
        {
            hplus += hitweight();   /* adjust for encumberence */
            dplus += hung_dam();    /* adjust damage for hungry player */
            dplus += ring_value(R_ADDDAM);
        }

        ndice = atoi(cp);

        if (cp == NULL || (cp = strchr(cp, 'd')) == NULL)
            break;

        nsides = atoi(++cp);

        if (cp != NULL && (cp = strchr(cp, '+')) != NULL)
            nplus = atoi(++cp);
        else
            nplus = 0;

        if (def == &pstats)
        {
            if (on(*att_er, NOMETAL) && cur_armor != NULL &&
                (cur_armor->o_which == RING_MAIL ||
                 cur_armor->o_which == SCALE_MAIL ||
                 cur_armor->o_which == CHAIN_MAIL ||
                 cur_armor->o_which == SPLINT_MAIL ||
                 cur_armor->o_which == BANDED_MAIL ||
                 cur_armor->o_which == GOOD_CHAIN ||
                 cur_armor->o_which == PLATE_MAIL ||
                 cur_armor->o_which == PLATE_ARMOR))
                def_arm = def->s_arm;
            else if (cur_armor != NULL)
                def_arm = cur_armor->o_ac - 10 + pstats.s_arm;
            else
                def_arm = def->s_arm;
            def_arm -= ring_value(R_PROTECT);
        }
        else
            def_arm = def->s_arm;

        if ((weap != NULL && weap->o_type == WEAPON &&
             (weap->o_flags & ISSILVER) &&
            !save_throw(VS_MAGIC, def_er)) ||
            swing(att_er->t_ctype, att->s_lvl,
            def_arm - dext_prot(def->s_dext),
           hplus + str_plus(att->s_str) + dext_plus(att->s_dext)))
        {
            damage = roll(ndice, nsides) + dplus + nplus +
                add_dam(att->s_str);

            /* Rangers do +1/lvl vs. ISLARGE */

            if (att_er->t_ctype == C_RANGER && on(*def_er, ISLARGE))
                damage += pstats.s_lvl;

            /* Ninja do +1 per lvl/2 */

            if (att_er->t_ctype == C_NINJA)
                damage += pstats.s_lvl / 2;

            /* Check for half damage monsters */

            if (on(*def_er, HALFDAMAGE) && (weap != NULL) &&
                !((weap->o_flags & CANBURN) &&
                on(*def_er, CANBBURN)))
                damage /= 2;

            /* undead get twice damage from silver weapons */

            if (on(*def_er, ISUNDEAD) &&
                (weap != NULL) && (weap->o_flags & ISSILVER))
                damage *= 2;

            /* Check for fireproof monsters */

            if (on(*def_er, NOFIRE) && (weap != NULL) &&
                (weap->o_flags & CANBURN))
                damage = 0;

            /* Check for metal proof monsters */

            if (on(*def_er, NOMETAL) && (weap != NULL) &&
                (weap->o_flags & ISMETAL))
                damage = 0;

            /* Check for monsters that ignore sharp weapons  */

            if (on(*def_er, NOSHARP) && (weap != NULL) &&
                (weap->o_flags & ISSHARP))
                damage = 0;

            /* Check for poisoned weapons */

            if ((weap != NULL) && (weap->o_flags & ISPOISON)
                && off(*def_er, ISUNDEAD)
                && !save_throw(VS_POISON, def_er))
                damage = max(damage, (def->s_hpt / 2) + 5);

            /* Check for no-damage and division */

            if (on(*def_er, BLOWDIVIDE) && rnd(3) == 0 &&
                !((weap != NULL) && (weap->o_flags & CANBURN)))
            {
                damage = 0;
                creat_mons(def_er, def_er->t_index, NOMESSAGE);
            }

            damage = max(0, damage);

            /*
             * sleeping monsters are backstabbed by certain
             * player classes, but only when they can see
             */

            if (is_player && !thrown && damage > 0 &&
                (off(*def_er, ISRUN) || def_er->t_no_move > 0) &&
                (player.t_ctype == C_THIEF ||
                 player.t_ctype == C_NINJA ||
                 player.t_ctype == C_ASSASIN) &&
				 off(player,ISBLIND)
                && (wield_ok(&player, my_weapon, NOMESSAGE))
                && (wear_ok(&player, cur_armor, NOMESSAGE)))
            {
                damage *= (pstats.s_lvl / 4 + 2);

                msg("You backstabbed the %s %d times!",
                    monsters[def_er->t_index].m_name,
                    (pstats.s_lvl / 4) + 2);

                if (player.t_ctype == C_NINJA ||
                    player.t_ctype == C_ASSASIN)
                    pstats.s_exp += def_er->t_stats.s_exp
                        / 2;
            }

            def->s_hpt -= damage;   /* Do the damage */

            debug("Hit %s for %d (%d) ",
                  monsters[def_er->t_index].m_name, damage,
                  def_er->t_stats.s_hpt);

            if (is_player && is_wearing(R_VREGEN))
            {
                damage = (ring_value(R_VREGEN) * damage) / 3;
                pstats.s_hpt = min(max_stats.s_hpt,
                    pstats.s_hpt + damage);
            }

            /* stun monsters when taking more than 1/3 their max hpts */

            if (is_player && !thrown && !did_hit &&
                (player.t_ctype == C_FIGHTER) &&
                (damage > def_er->maxstats.s_hpt / 3) )
            {
                if (def->s_hpt > 0)
                {
                    msg("The %s has been stunned!",
                      monsters[def_er->t_index].m_name);
                    def_er->t_no_move += rnd(4) + 1;
                }
                pstats.s_exp += def_er->t_stats.s_exp / 4;
            }

            did_hit = TRUE;
        }

        if (cp == NULL || (cp = strchr(cp, '/')) == NULL)
            break;

        cp++;
    }

    return(did_hit);
}

/*
    prname()
        Figure out the monsters name
*/

const char *
prname(char *who)
{
    if (on(player, ISBLIND))
        return(monstern);
    else
        return(who);
}

/*
    hit()
        Print a message to indicate a succesful hit
*/

void
hit(char *ee)
{
    char    *s;

    if (fighting)
        return;

    switch (rnd(15))
    {
        default:  s = "hit";                            break;
        case 1:   s = "score an excellent hit on";      break;
        case 2:   s = "injure";                         break;
        case 3:   s = "swing and hit";                  break;
        case 4:   s = "damage";                         break;
        case 5:   s = "barely nick";                    break;
        case 6:   s = "scratch";                        break;
        case 7:   s = "gouge a chunk out of";           break;
        case 8:   s = "severely wound";                 break;
        case 9:   s = "counted coup on";                break;
        case 10:  s = "drew blood from";                break;
        case 11:  s = "nearly decapitate";              break;
        case 12:  s = "deal a wacking great blow to";   break;
    }

    msg("You %s the %s.", s, prname(ee));
}

/*
    miss()
        Print a message to indicate a poor swing
*/

void
miss(char *ee)
{
    char *s;

    if (fighting)
        return;

    switch (rnd(10))
    {
        default:  s = "miss";                          break;
        case 1:   s = "swing and miss";                break;
        case 2:   s = "barely miss";                   break;
        case 3:   s = "don't hit";                     break;
        case 4:   s = "wildly windmill around";        break;
        case 5:   s = "almost fumble while missing";   break;
    }

    msg("You %s the %s.", s, prname(ee));
}

/*
    save_throw()
        See if a creature save against something
*/

int
save_throw(int which, struct thing *tp)
{
    int need;
    int ring_bonus = 0;
    int armor_bonus = 0;
    int class_bonus = 0;

    if (tp == &player)
    {
        if (player.t_ctype == C_PALADIN)
            class_bonus = 2;

        ring_bonus = ring_value(R_PROTECT);

        if (cur_armor != NULL && (which == VS_WAND ||
            which == VS_MAGIC))
        {
            if (cur_armor->o_which == MITHRIL)
                armor_bonus += 5;
            armor_bonus += (armors[cur_armor->o_which].a_class
                    - cur_armor->o_ac);
        }
    }

    need = 14 + which - tp->t_stats.s_lvl / 2 - ring_bonus -
        armor_bonus - class_bonus;

    /* Roll of 1 always fails; 20 always saves */

    if (need < 1)
        need = 1;
    else if (need > 20)
        need = 20;

    return(roll(1, 20) >= need);
}

/*
    save()
        See if he saves against various nasty things
*/

int
save(int which)
{
    return save_throw(which, &player);
}

/*
    dext_plus()
       compute to-hit bonus for dexterity
*/

int
dext_plus(int dexterity)
{
    return ((dexterity - 10) / 3);
}

/*
 * dext_prot: compute armor class bonus for dexterity
 */

int
dext_prot(int dexterity)
{
    return ((dexterity - 9) / 2);
}

/*
    str_plus()
       compute bonus/penalties for strength on the "to hit" roll
*/

static const int strtohit[] =
{
    0, 0, 0, -3, -2, -2, -1, -1,
    0, 0, 0, 0, 0, 0, 0, 0, 0,
    1, 1, 3, 3, 4, 4, 5, 6, 7
};

int
str_plus(int str)
{
    int ret_val = str;

    if (str < 3)
        ret_val = 3;
    else if (str > 25)
        ret_val = 25;

    return(strtohit[ret_val]);
}

/*
    add_dam()
        compute additional damage done for exceptionally high or low strength
*/

static const int str_damage[] =
{
    0, 0, 0, -1, -1, -1, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 1,
    1, 2, 7, 8, 9, 10, 11, 12, 14
};

int
add_dam(int str)
{
    int ret_val = str;

    if (str < 3)
        ret_val = 3;
    else if (str > 25)
        ret_val = 25;

    return(str_damage[ret_val]);
}

/*
    hung_dam()
        Calculate damage depending on players hungry state
*/

int
hung_dam(void)
{
    int howmuch = 0;

    switch (hungry_state)
    {
        case F_OK:
        case F_HUNGRY: howmuch =  0; break;
        case F_WEAK:   howmuch = -1; break;
        case F_FAINT:  howmuch = -2; break;
    }

    return(howmuch);
}

/*
    raise_level()
        The guy just magically went up a level.
*/

void
raise_level(void)
{
    pstats.s_exp = max_stats.s_exp;
    check_level();
}

/*
    thunk()
        A missile hits a monster
*/

void
thunk(struct object *weap, char *mname)
{
    if (fighting)
        return;

    if (weap->o_type == WEAPON)
        msg("The %s hits the %s.", weaps[weap->o_which].w_name, prname(mname));
    else
        msg("You hit the %s.", prname(mname));
}

/*
    m_thunk()
        A missile from a monster hits the player
*/

void
m_thunk(struct object *weap, char *mname)
{
    if (fighting)
        return;

    if (weap != NULL && weap->o_type == WEAPON)
        msg("The %s's %s hits you.",prname(mname),weaps[weap->o_which].w_name);
    else
        msg("The %s hits you.", prname(mname));
}

/*
    bounce()
        A missile misses a monster
*/

void
bounce(struct object *weap, char *mname)
{
    if (fighting)
        return;

    if (weap->o_type == WEAPON)
        msg("The %s misses the %s.",weaps[weap->o_which].w_name,prname(mname));
    else
        msg("You missed the %s.", prname(mname));
}

/*
    m_bounce()
        A missile from a monster misses the player
*/

void
m_bounce(struct object *weap, char *mname)
{
    if (fighting)
        return;

    if (weap != NULL && weap->o_type == WEAPON)
        msg("The %s's %s misses you.", prname(mname),
                                       weaps[weap->o_which].w_name);
    else
        msg("The %s misses you.", prname(mname));
}

/*
    remove_monster()
        remove a monster from the screen
*/

void
remove_monster(coord *mp, struct linked_list *item)
{
    struct thing *tp = THINGPTR(item);
    char          ch = tp->t_oldch;

    mvwaddch(mw, mp->y, mp->x, ' ');

    if (ch < 33 || ch == ' ')
        ch = CCHAR( mvwinch(stdscr, mp->y, mp->x) );

    if (cansee(mp->y, mp->x))
        mvwaddch(cw, mp->y, mp->x, ch);

    detach(mlist, item);
    discard(item);
}

/*
    is_magic()
        Returns true if an object radiates magic
*/

int
is_magic(struct object *obj)
{
    switch (obj->o_type)
    {
        case ARMOR:
            return(obj->o_ac != armors[obj->o_which].a_class);

        case WEAPON:
            return(obj->o_hplus != 0 || obj->o_dplus != 0);

        case POTION:
        case SCROLL:
        case STICK:
        case RING:
        case ARTIFACT:
            return(TRUE);
    }

    return(FALSE);
}

/*
    killed()
        Called to put a monster to death
*/

void
killed(struct thing *killer, struct linked_list *item, int print_message,
       int give_points)
{
    struct linked_list *pitem, *nitem;
    struct thing       *tp        = THINGPTR(item);
    int                 visible   = cansee(tp->t_pos.y, tp->t_pos.x);
    int                 is_player = (killer == (&player));

    if (item == curr_mons)
        curr_mons = NULL;
    else if (item == next_mons)
        next_mons = next(next_mons);

    if (on(*tp, WASSUMMONED))
    {
        extinguish_fuse(FUSE_UNSUMMON);
        turn_off(player, HASSUMMONED);
    }

    if (print_message && visible)
    {
        if (is_player)
            addmsg("You have defeated ");
        else
            addmsg("The %s has defeated ",
                monsters[killer->t_index].m_name);

        if (on(player, ISBLIND))
            msg("it.");
        else
            msg("the %s.", monsters[tp->t_index].m_name);
    }
    debug("Removing %s", monsters[tp->t_index].m_name);
    if (killer != NULL && item == fam_ptr)  /* The player's familiar died */
    {
        turn_off(player, HASFAMILIAR);
        fam_ptr = NULL;
        msg("An incredible wave of sadness sweeps over you.");
    }

    check_residue(tp);

    if (is_player)
    {
        fighting = FALSE;

        if (on(*tp, ISFRIENDLY))
        {
            msg("You feel a slight chill run up and down your spine.");
            luck++;
        }
    }

    if (give_points)
    {
        if (killer != NULL)
        {
            killer->t_stats.s_exp += tp->t_stats.s_exp;

            if (on(*killer, ISFAMILIAR))
                pstats.s_exp += tp->t_stats.s_exp;
        }

        if (is_player)
        {
            switch (player.t_ctype)
            {
                case C_CLERIC:
                case C_PALADIN:
                    if (on(*tp, ISUNDEAD) || on(*tp, ISUNIQUE))
                    {
                        pstats.s_exp += tp->t_stats.s_exp / 2;
                        msg("You are to be commended for smiting the ungodly.");
                    }
                    break;

                case C_DRUID:
                case C_RANGER:
                    if (on(*tp, ISLARGE))
                    {
                        pstats.s_exp += tp->t_stats.s_exp / 2;
                        msg("Congratulations on smiting a dangerous monster.");
                    }
                    break;

                case C_MAGICIAN:
                case C_ILLUSION:
                    if (on(*tp, DRAINBRAIN))
                    {
                        pstats.s_exp += tp->t_stats.s_exp / 2;
                        msg("Congratulations on smiting a dangerous monster.");
                    }

            }
        }
        check_level();
    }

    /* Empty the monsters pack */

    for (pitem = tp->t_pack; pitem != NULL; pitem = nitem)
    {
        struct object *obj = OBJPTR(pitem);

        nitem = next(pitem);

        obj->o_pos = tp->t_pos;
        detach(tp->t_pack, pitem);

        if (killer == NULL)
            discard(pitem);
        else
            fall(killer, pitem, FALSE, FALSE);
    }

    remove_monster(&tp->t_pos, item);
}


/*
    wield_weap()
        Returns a pointer to the weapon the monster is wielding corresponding           to the given thrown weapon
*/

struct object *
wield_weap(struct object *weapon, struct thing *mp)
{
    int look_for;
    struct linked_list *pitem;

    if (weapon == NULL)
        return (NULL);

    switch (weapon->o_which)
    {
        case BOLT:
            look_for = CROSSBOW;
            break;

        case ARROW:
            look_for = BOW;
            break;

        case SILVERARROW:
        case FLAMEARROW:
            look_for = BOW;
            break;

        case ROCK:
        case BULLET:
            look_for = SLING;
            break;

        default:
            return(NULL);
    }

    for (pitem = mp->t_pack; pitem; pitem = next(pitem))
        if ((OBJPTR(pitem))->o_which == look_for)
            return(OBJPTR(pitem));

    return (NULL);
}

/*
    summon_help()
        Summon - see whether to summon help Returns TRUE if help comes, FALSE
        otherwise
*/

void
summon_help(struct thing *mons, int force)
{
    char *helpname;
    int   which, i;
    char *mname = monsters[mons->t_index].m_name;

    /* Try to summon if less than 1/3 max hit points */

    if (on(*mons, CANSUMMON) &&
      (force == FORCE ||
      (mons->t_stats.s_hpt < mons->maxstats.s_hpt / 3) &&
      (rnd(40 * 10) < (mons->t_stats.s_lvl * mons->t_stats.s_intel))))
    {
        turn_off(*mons, CANSUMMON);
        msg("The %s summons its attendants!", mname);
        helpname = monsters[mons->t_index].m_typesum;

        for (which = 1; which < nummonst; which++)
        {
            if (strcmp(helpname, monsters[which].m_name) == 0)
                break;
        }

        if (which >= nummonst)
        {
            debug("Couldn't find summoned one.");
            return;
        }

        /* summoned monster was genocided */

        if (!monsters[which].m_normal)
        {
            msg("The %s becomes very annoyed at you!", mname);

            if (on(*mons, ISSLOW))
                turn_off(*mons, ISSLOW);
            else
                turn_on(*mons, ISHASTE);

            return;
        }
        else
            for (i = 0; i < monsters[mons->t_index].m_numsum; i++)
            {
                struct linked_list  *ip;
                struct thing    *tp;

                if ((ip = creat_mons(mons, which, NOMESSAGE)) != NULL)
                {
                    tp = THINGPTR(ip);
                    turn_off(*tp, ISFRIENDLY);
                }
            }
    }

    return;
}

/*
    maxdamage()
        return the max damage a weapon can do
*/

int
maxdamage(char *cp)
{
    int ndice, nsides, nplus;

    ndice = atoi(cp);

    if (cp == NULL || (cp = strchr(cp, 'd')) == NULL)
        return(0);

    nsides = atoi(++cp);

    if (cp != NULL && (cp = strchr(cp, '+')) != NULL)
        nplus = atoi(++cp);
    else
        nplus = 0;

    return(ndice * nsides + nplus);
}
