diff urogue/fight.c @ 256:c495a4f288c6

Import UltraRogue from the Roguelike Restoration Project (r1490)
author John "Elwin" Edwards
date Tue, 31 Jan 2017 19:56:04 -0500
parents
children 317166b49d8a
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/urogue/fight.c	Tue Jan 31 19:56:04 2017 -0500
@@ -0,0 +1,2175 @@
+/*
+    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);
+}