view xrogue/effects.c @ 152:0c775afe0072

arogue7, xrogue: fix command repetition. In both games, command() loops and calls the action function twice for each command: once to schedule it by setting player.t_action and player.t_no_move, and again, to actually do it, once player.t_no_move has been reduced to 0. Each loop decremented count, so repeated commands were only repeated half the number of times the player typed. count is now decremented only when ch has been set to the repeated command.
author John "Elwin" Edwards
date Thu, 28 May 2015 11:08:03 -0400
parents ce0cf824c192
children f54901b9c39b
line wrap: on
line source

/*
    effects.c  -  functions for dealing with appllying effects to monsters

    XRogue: Expeditions into the Dungeons of Doom
    Copyright (C) 1991 Robert Pietkivitch
    All rights reserved.
    
    Based on "Advanced Rogue"
    Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka and AT&T
    All rights reserved.

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

/*
 * effect:
 *      Check for effects of one thing hitting another thing.  Return
 *      the reason code if the defender is killed.  Otherwise return 0.
 */

effect(att, def, weap, thrown, see_att, see_def)
register struct thing *att, *def;
struct object *weap;
bool thrown;
register bool see_att, see_def;
{
    register bool att_player, def_player;
    char attname[LINELEN+1], defname[LINELEN+1];

    /* See if the attacker or defender is the player */
    att_player = (att == &player);
    def_player = (def == &player);

    /*
     * If the player could see the attacker or defender, they can't
     * surprise anymore (don't bother checking if they could).
     */
    if (see_att) turn_off(*att, CANSURPRISE);
    if (see_def) turn_off(*def, CANSURPRISE);

    /* What are the attacker and defender names? */
    if (att_player) strcpy(attname, "you");
    else {
        if (see_att) strcpy(attname, monster_name(att));
        else strcpy(attname, "something");
    }

    if (def_player) strcpy(defname, "you");
    else {
        if (see_def) strcpy(defname, monster_name(def));
        else strcpy(defname, "something");
    }

    /*
     * See what happens to the attacker first.  We can skip this
     * whole section, however, if the defender is the player.
     * Nothing happens (yet) to anyone just for hitting the player.
     */
    if (!def_player) {
        if (!thrown) {  /* Some things require a direct hit. */
            /*
             * If the attacker hits a rusting monster, The weapon
             * may be damaged
             */
            if (on(*def, CANRUST)       && weap                         &&
                weap->o_type != RELIC   && (weap->o_flags & ISMETAL)    &&
                !(weap->o_flags & ISPROT)) {
                    if ((weap->o_hplus < 1 && weap->o_dplus < 1) ||
                        roll(1,20) < weap->o_hplus+weap->o_dplus+10) {
                            if (rnd(100) < 50) weap->o_hplus--;
                            else               weap->o_dplus--;
                            if (att_player)
                                msg(terse ? "Your %s weakens!"
                                          : "Your %s gets weaker!",
                                    weaps[weap->o_which].w_name);
                    }
            }
        }
                
        /* If the attacker hit something that shrieks, wake the dungeon */
        if (on(*def, CANSHRIEK)) {
            if (see_def)
                msg("%s emits an ear piercing shriek! ", prname(defname, TRUE));
            else
                msg("You hear an ear piercing shriek!");

            /* Friendly charactors should be immune */
            if (player.t_ctype == C_PALADIN ||
                player.t_ctype == C_RANGER  || player.t_ctype == C_MONK)
                    aggravate(TRUE, FALSE);
            else
                aggravate(TRUE, TRUE);
        }

        /*
         * does the creature explode when hit?
         */
        if (on(*def, CANEXPLODE)) {
            if (see_def) msg("%s explodes!", prname(defname, TRUE));
            else msg("You hear a tremendous explosion!");
            explode(def);
            if (pstats.s_hpt < 1) {
        pstats.s_hpt = -1;
                death(def->t_index);
        }
        }
    }

    /*
     * Now let's see what happens to the defender.  Start out with
     * the things that everyone can do.  Then exit if the attacker
     * is the player.
     */
    if (!thrown) {
        /* 
         * Can the player confuse? 
         */
        if (on(*att, CANHUH) && att_player) {
            msg("Your hands return to normal. ");
            if (off(*def, ISCLEAR) && 
               (off(*def, ISUNIQUE) || !save(VS_MAGIC, def, 0))) {
                if (see_def) msg("%s appears confused!", prname(defname, TRUE));
                turn_on(*def, ISHUH);
            }
            turn_off(*att, CANHUH);
        }

        /* Return now if the attacker is the player. */
        if (att_player) return(0);

        /*
         * Some monsters may take half your hit points
         */
        if (on(*att, CANSUCK) && !save(VS_MAGIC, def, 0)) {
            if (def->t_stats.s_hpt == 1) return(att->t_index); /* Killed! */
            else {
                def->t_stats.s_hpt /= 2;
                if (def_player)
                    msg("Your life force is being drained out of you.");
            }
        }

        /*
         * If a hugging monster hits, it may SQUEEEEEEEZE.
         */
        if (on(*att, CANHUG)) {
            if (roll(1,20) >= 18 || roll(1,20) >= 18) {
                if (def_player)
                    msg("%s squeezes itself nastily against you!",
                                prname(attname, TRUE));
                else if (see_att)
                    msg("%s squeezes real hard!", prname(attname, TRUE));

                if ((def->t_stats.s_hpt -= roll(2,8)) <= 0)
                    return(att->t_index);
            }
        }

        /*
         * Some monsters have poisonous bites.
         */
        if (on(*att, CANPOISON) && !save(VS_POISON, def, 0)) {
            if (def_player) {
                if (ISWEARING(R_SUSABILITY))
                    msg(terse ? "Sting has no effect."
                              : "A sting momentarily weakens your arm.");
                else {
                    chg_str(-1);
                    msg(terse ? "A sting has weakened you." :
                    "You get stung in the arm!  You feel weaker. ");
                }
            }
            else {
                /* Subtract a strength point and see if it kills it */
                if (--def->t_stats.s_str <= 0) return(D_STRENGTH);
            }
        }

        /*
         * Turning to stone:
         */
        if (on(*att, TOUCHSTONE)) {
            if (def_player) turn_off(*att, TOUCHSTONE);
            if (on(*def, CANINWALL)) {
                if (def_player)
                    msg("%s's touch has no effect.", prname(attname, TRUE));
            }
            else {
                if (!save(VS_PETRIFICATION, def, 0) && rnd(100) < 10) {
                    if (def_player) {
                        msg("Your body begins to solidify.. ");
                        msg("You are transformed into stone!! --More--");
                        wait_for(' ');
                        return(D_PETRIFY);
                    }
                    else {
                        /* The monster got stoned! */
                        turn_on(*def, ISSTONE);
                        turn_off(*def, ISRUN);
                        turn_off(*def, ISINVIS);
                        turn_off(*def, ISDISGUISE);
                        if (def->t_stats.s_intel > 15)
                            msg("%s staggers.. ", prname(defname, TRUE));
                        else if (see_def)
                            msg("%s turns to stone! ", prname(defname, TRUE));
                        else if (cansee(unc(def->t_pos)))
                            msg("A statue appears out of nowhere! ");
                    }
                }
                else if (def->t_action != A_FREEZE) {
                    if (def_player)
                        msg("%s's touch stiffens your limbs.",
                                        prname(attname, TRUE));
                    else if (see_def)
                        msg("%s appears to freeze over.", prname(defname, TRUE));

                    def->t_no_move += movement(def) * STONETIME;
                    def->t_action = A_FREEZE;
                }
            }
        }

        /*
         * Wraiths might drain energy levels
         */
        if ((on(*att, CANDRAIN) || on(*att, DOUBLEDRAIN)) && 
            !save(VS_POISON, def, 3-(att->t_stats.s_lvl/5))) {
            if (def_player) {
                lower_level(att->t_index);
                if (on(*att, DOUBLEDRAIN)) lower_level(att->t_index);
                turn_on(*att, DIDDRAIN);  
            }
            else {
                def->t_stats.s_hpt -= roll(1, 8);
                def->t_stats.s_lvl--;
                if (on(*att, DOUBLEDRAIN)) {
                    def->t_stats.s_hpt -= roll(1, 8);
                    def->t_stats.s_lvl--;
                }
                if (see_def)
                    msg("%s appears less skillful.", prname(defname, TRUE));

                /* Did it kill it? */
                if (def->t_stats.s_hpt <= 0 ||
                    def->t_stats.s_lvl <= 0)
                    return(att->t_index);
            }
        }

        /*
         * Paralyzation:
         */
        if (on(*att, CANPARALYZE) && def->t_action != A_FREEZE) {
            if (def_player) turn_off(*att, CANPARALYZE);
            if (!save(VS_PARALYZATION, def, 0)) {
                if (on(*def, CANINWALL)) {
                    if (def_player)
                        msg("%s's touch has no effect.", prname(attname, TRUE));
                }
                else {
                    if (def_player)
                        msg("%s's touch paralyzes you.", prname(attname, TRUE));
                    else if (see_def)
                        msg("%s appears to freeze over!", prname(defname, TRUE));

                    def->t_no_move += movement(def) * FREEZETIME;
                    def->t_action = A_FREEZE;
                }
            }
        }

        /*
         * Painful wounds make the defendant faint
         */
         if (on(*att, CANPAIN) && def->t_action != A_FREEZE) {
            if (def_player) turn_off(*att, CANPAIN);
            if (!ISWEARING(R_ALERT) && !save(VS_POISON, def, 0)) {
                    if (def_player)
                        msg("You faint from the painful wound!");
                    else if (see_def)
                        msg("%s appears to faint!", prname(defname, TRUE));

                    def->t_no_move += movement(def) * PAINTIME;
                    def->t_action = A_FREEZE;
            }
        }

        /*
         * Some things currently affect only the player.  Let's make
         * a check here so we don't have to check for each thing.
         */
        if (def_player) {
        /*
         * Stinking monsters make the defender weaker (to hit).  For now
         * this will only affect the player.  We may later add the HASSTINK
         * effect to monsters, too.
         */
            if (on(*att, CANSTINK)) {
                turn_off(*att, CANSTINK);
                if (!save(VS_POISON, def, 0)) {
                    msg("The stench of %s sickens you.  Blech!",
                                prname(attname, FALSE));
                    if (on(player, HASSTINK)) lengthen(unstink, STINKTIME);
                    else {
                        turn_on(player, HASSTINK);
                        fuse(unstink, (VOID *)NULL, STINKTIME, AFTER);
                    }
                }
            }

            /*
             * Chilling monster reduces strength each time.  This only
             * affects the player for now because of its temporary nature.
             */
            if (on(*att, CANCHILL)) {
                if (!ISWEARING(R_SUSABILITY) && !save(VS_POISON, def, 0)) {
                    msg("You cringe at %s's chilling touch.",
                                prname(attname, FALSE));
                    chg_str(-1);
                    if (lost_str++ == 0)
                        fuse(res_strength, (VOID *)NULL, CHILLTIME, AFTER);
                    else lengthen(res_strength, CHILLTIME);
                }
            }

            /*
             * Itching monsters reduce dexterity (temporarily).  This only
             * affects the player for now because of its temporary nature.
             */
            if (on(*att, CANITCH) && !save(VS_POISON, def, 0)) {
                msg("The claws of %s scratch you!", prname(attname, FALSE));
                if (ISWEARING(R_SUSABILITY)) {
                    msg("The scratch has no effect.");
                }
                else {
                    (*add_abil[A_DEXTERITY])(-1);
                }
            }

            /*
             * If a disease-carrying monster hits, there is a chance the
             * defender will catch the disease.  This only applies to the
             * player for now because of the temporary nature. Don't affect
             * the Ranger or Paladin.
             */
            if (on(*att, CANDISEASE) &&
                (rnd(def->t_stats.s_const) < att->t_stats.s_lvl) &&
                off(*def, HASDISEASE)) {
                    if (ISWEARING(R_HEALTH)             ||
                        player.t_ctype == C_PALADIN     ||
                        player.t_ctype == C_RANGER) {
                            msg("The wound heals quickly.");
                    }
                    else {
                        turn_on(*def, HASDISEASE);
                        fuse(cure_disease, (VOID *)NULL, roll(HEALTIME,SICKTIME), AFTER);
                        msg(terse ? "You have been diseased!"
                            : "You have contracted an annoying disease!");
                    }
            }

            /*
             * If a rusting monster hits, you lose armor.  This only applies to
             * the player because monsters don't wear armor (for now).
             */
            if (on(*att, CANRUST)) { 
                if (cur_armor != NULL                           &&
                    cur_armor->o_which != LEATHER               &&
                    cur_armor->o_which != STUDDED_LEATHER       &&
                    cur_armor->o_which != PADDED_ARMOR          &&
                    !(cur_armor->o_flags & ISPROT)              &&
                    cur_armor->o_ac < def->t_stats.s_arm+1) {
                        msg(terse ? "Your armor weakens."
                            : "Your armor becomes weaker.");
                        cur_armor->o_ac++;
                }
                if (cur_misc[WEAR_BRACERS] != NULL              &&
                    cur_misc[WEAR_BRACERS]->o_ac > 0            &&
                    !(cur_misc[WEAR_BRACERS]->o_flags & ISPROT)) {
                        cur_misc[WEAR_BRACERS]->o_ac--;
                        if (cur_misc[WEAR_BRACERS]->o_ac == 0) {
                            register struct linked_list *item;

                            for (item=pack; item!=NULL; item=next(item)) {
                                if (OBJPTR(item) == cur_misc[WEAR_BRACERS]) {
                                    detach(pack, item);
                                    o_discard(item);
                                    break;
                                }
                            }
                            msg ("Your bracers crumble apart!");
                            cur_misc[WEAR_BRACERS] = NULL;
                            inpack--;
                        }
                        else {
                            msg("Your bracers weaken!");
                        }
                }
            }

            /*
             * If can dissolve and hero has leather type armor.  This
             * also only applies to the player for now because of the
             * armor.
             */
            if (on(*att, CANDISSOLVE) && cur_armor != NULL &&
                (cur_armor->o_which == LEATHER            ||
                 cur_armor->o_which == STUDDED_LEATHER    ||
                 cur_armor->o_which == PADDED_ARMOR)      &&
                !(cur_armor->o_flags & ISPROT) &&
                cur_armor->o_ac < def->t_stats.s_arm+1) {
                msg(terse ? "Your armor dissolves!"
                    : "Your armor appears to have dissolved!");
                cur_armor->o_ac++;
            }

            /*
             * If an infesting monster hits you, you get a parasite or rot.
             * This will only affect the player until we figure out how to
             * make it affect monsters.  Don't affect the Monk.
             */
            if (on(*att, CANINFEST) &&
                rnd(def->t_stats.s_const) < att->t_stats.s_lvl) {
                if (ISWEARING(R_HEALTH) || player.t_ctype == C_MONK) {
                        msg("The wound heals quickly.");
                }
                else {
                    turn_off(*att, CANINFEST);
                    msg(terse ? "You have been infested."
                        : "You have contracted a parasitic infestation!");
                    infest_dam++;
                    turn_on(*def, HASINFEST);
                }
            }

            /*
             * Does it take wisdom away?  This currently affects only
             * the player because of its temporary nature.
             */
            if (on(*att, TAKEWISDOM)            && 
                !save(VS_MAGIC, def, 0) &&
                !ISWEARING(R_SUSABILITY)) {
                        (*add_abil[A_WISDOM])(-1);
            }

            /*
             * Does it take intelligence away?  This currently affects
             * only the player because of its temporary nature.
             */
            if (on(*att, TAKEINTEL)             && 
                !save(VS_MAGIC, &player, 0)     &&
                !ISWEARING(R_SUSABILITY)) {
                        (*add_abil[A_INTELLIGENCE])(-1);
            }

            /*
             * Cause fear by touching.  This currently affects only
             * the player until we figure out how we want it to
             * affect monsters.
             */
            if (on(*att, TOUCHFEAR)) {
                turn_off(*att, TOUCHFEAR);
                if (!ISWEARING(R_HEROISM)       &&
                    !save(VS_WAND, def, 0)      &&
                    !(on(*def, ISFLEE) && (def->t_dest == &att->t_pos))) {
                        turn_on(*def, ISFLEE);
                        def->t_dest = &att->t_pos;
                        msg("%s's touch terrifies you!", prname(attname, TRUE));

                        /* It is okay to turn tail */
                        if (!def_player) def->t_oldpos = def->t_pos;
                }
            }

            /*
             * Make the hero dance (as in otto's irresistable dance)
             * This should be fairly easy to do to monsters, but
             * we'll restrict it to players until we decide what to
             * do about the temporary nature.
             */
            if (on(*att, CANDANCE)              && 
                !on(*def, ISDANCE)              &&
                def->t_action != A_FREEZE       &&
                !save(VS_MAGIC, def, -4)) {
                    turn_off(*att, CANDANCE);
                    turn_on(*def, ISDANCE);
                    msg("You begin to dance uncontrollably!");
                    fuse(undance, (VOID *)NULL, roll(2,4), AFTER);
            }

            /*
             * Suffocating our hero.  Monsters don't get suffocated.
             * That's too hard for now.
             */
            if (on(*att, CANSUFFOCATE)          && 
                !ISWEARING(R_FREEDOM)           && 
                rnd(100) < 30                   &&
                (find_slot(suffocate) == 0)) {
                turn_on(*att, DIDSUFFOCATE);
                msg("%s is beginning to suffocate you!", prname(attname, TRUE));
                fuse(suffocate, (VOID *)NULL, roll(9,3), AFTER);
            }

            /*
             * some creatures stops the poor guy from moving.
             * How can we do this to a monster?
             */
            if (on(*att,CANHOLD) && off(*att,DIDHOLD) && !ISWEARING(R_FREEDOM)){
                turn_on(*def, ISHELD);
                turn_on(*att, DIDHOLD);
                hold_count++;
            }

            /*
             * Sucker will suck blood and run.  This
             * should be easy to have happen to a monster,
             * but we have to decide how to handle the fleeing.
             */
            if (on(*att, CANDRAW)) {
                turn_off(*att, CANDRAW);
                turn_on(*att, ISFLEE);
                msg("%s sates itself with your blood!", prname(attname, TRUE));
                if ((def->t_stats.s_hpt -= 12) <= 0) return(att->t_index);

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

            /*
             * Bad smell will force a reduction in strength.
             * This will happen only to the player because of
             * the temporary nature.
             */
            if (on(*att, CANSMELL)) {
                turn_off(*att, CANSMELL);
                if (save(VS_MAGIC, def, 0) || ISWEARING(R_SUSABILITY)) {
                    if (terse)
                        msg("Pheww!");
                    else
                        msg("You smell an unpleasant odor.  Phew!");
                    }

                else {
                    int odor_str = -(rnd(6)+1);

                    msg("You are overcome by a foul odor!");
                    if (lost_str == 0) {
                        chg_str(odor_str);
                        fuse(res_strength, (VOID *)NULL, SMELLTIME, AFTER);
                        lost_str -= odor_str;
                    }
                    else lengthen(res_strength, SMELLTIME);
                }
            }

            /*
             * The monsters touch slows the defendant down.
             */
             if (on(*att, TOUCHSLOW)) {
                turn_off(*att, TOUCHSLOW);
                if (!save(VS_PARALYZATION, def, 0)) 
                        add_slow();
            }

            /*
             * Rotting only affects the player.  Don't affect the Monk,
             * Paladin, or Ranger.
             */
            if (on(*att, CANROT)) {
                if (!ISWEARING(R_HEALTH)        && 
                    player.t_ctype != C_MONK    &&
                    player.t_ctype != C_RANGER  &&
                    player.t_ctype != C_PALADIN &&
                    !save(VS_POISON, def, 0)    && 
                    off(*def, DOROT)) {
                    turn_on(*def, DOROT);
                    msg("You feel your skin starting to rot and peel away!");
                }
            }

            /*
             * Monsters should be able to steal gold from anyone,
             * but until this is rewritten, they will only steal
             * from the player (tough break).
             */
            if (on(*att, STEALGOLD)) {
                /*
                 * steal some gold
                 */
                register long lastpurse;
                register struct linked_list *item;
                register struct object *obj;

                lastpurse = purse;
                purse -= (GOLDCALC * 2);
                if (!save(VS_MAGIC, def, att->t_stats.s_lvl/10)) {
                    if (on(*att, ISUNIQUE))
                        purse -= (GOLDCALC * 5);
                    else
            purse -= (GOLDCALC * 3);
                }
                if (purse < 0)
                    purse = 0;
                if (purse != lastpurse) {
                    msg("You lost some gold! ");

                    /* Give the gold to the thief */
                    for (item=att->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;
                        strcpy(obj->o_damage,"0d0");
                        strcpy(obj->o_hurldmg,"0d0");
                        obj->o_ac = 11;
                        obj->contents = NULL;
                        obj->o_group = 0;
                        obj->o_flags = 0;
                        obj->o_mark[0] = '\0';
                        obj->o_pos = att->t_pos;

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

                turn_on(*att, ISFLEE);
                turn_on(*att, ISINVIS);

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

        /*
         * Stealing happens last since the monster disappears
         * after the act.
         */
        if (on(*att, STEALMAGIC)) {
            register struct linked_list *list, *steal;
            register struct object *obj;
            register int nobj;

            /*
             * steal a magic item, look through the pack
             * and pick out one we like.
             */
            steal = NULL;
            for (nobj = 0, list = def->t_pack; list != NULL; list = next(list))
            {
                obj = OBJPTR(list);
                if (!is_current(obj)     &&
                    list != def->t_using &&
                    obj->o_type != RELIC &&
                    is_magic(obj)        && 
                    rnd(++nobj) == 0)
                        steal = list;
            }
            if (steal != NULL)
            {
                register struct object *obj;
                struct linked_list *item;

                obj = OBJPTR(steal);
                if (on(*att, ISUNIQUE))
                    monsters[att->t_index].m_normal = TRUE;
                item = find_mons(att->t_pos.y, att->t_pos.x);

                killed(item, FALSE, FALSE, FALSE); /* Remove the attacker */

                if (obj->o_count > 1 && obj->o_group == 0) {
                    register int oc;

                    oc = --(obj->o_count);
                    obj->o_count = 1;
                    if (def_player)
                        msg("%s stole %s!", prname(attname, TRUE),
                                        inv_name(obj, TRUE));
                    obj->o_count = oc;
                }
                else {
                    if (def_player) {
                        msg("%s stole %s!", prname(attname, TRUE),
                                        inv_name(obj, TRUE));

                        /* If this is a relic, clear its holding field */
                        if (obj->o_type == RELIC)
                            cur_relic[obj->o_which] = 0;

                        inpack--;
                    }

                    detach(def->t_pack, steal);
                    o_discard(steal);
                }

                updpack(FALSE, def);
            }
        }
    }

    /* Didn't kill the defender */
    return(0);
}