view urogue/fight.c @ 310:827441d05b3e

Advanced Rogue family: fix some potential buffer overflows. Some code for determining the score file location assumed that PATH_MAX would be less than 1024, which cannot be guaranteed. Advanced Rogue 5 and 7, and XRogue, have had the buffers for the file name enlarged. UltraRogue never called the functions, so the code has been deleted instead.
author John "Elwin" Edwards
date Mon, 03 May 2021 19:05:37 -0400
parents e52a8a7ad4c5
children
line wrap: on
line source

/*
    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