view arogue7/effects.c @ 238:e1cd27c5464f

arogue7, xrogue: improve the handling of the arguments to fuses. fuse() now expects a pointer as the argument to a fuse function. If this is one of the functions that takes int, fuse() follows the pointer and stores that value in the f_list slot, in the integer field of the argument union. When the fuse goes off, do_fuses() recognizes the function and passes it the integer field instead of the pointer. This has the disadvantage of hard-coding the functions that require int in daemon.c, but since the int is copied into f_list, it no longer has to be in static or global memory, which simplifies several files.
author John "Elwin" Edwards
date Fri, 11 Mar 2016 17:40:00 -0500
parents f9ef86cf22b2
children
line wrap: on
line source

/*
 * effects.c  -  functions for dealing with appllying effects to monsters
 *
 * Advanced Rogue
 * Copyright (C) 1984, 1985, 1986 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.
 */
int
effect(struct thing *att, struct thing *def, struct object *weap, bool thrown, 
       bool see_att, bool 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 appears to be weaker now!",
				    weaps[weap->o_which].w_name);
		    }
	    }
	}
		
	/* If the attacker hit something that shrieks, wake the dungeon */
	if (on(*def, CANSHRIEK)) {
	    turn_off(*def, CANSHRIEK);
	    if (see_def)
		msg("%s emits a piercing shriek.", prname(defname, TRUE));
	    else msg("You hear a piercing shriek.");
	    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 <= 0)
	        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 stop glowing red.");
	    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("You feel your life force being drawn from 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 you against itself.",
				prname(attname, TRUE));
		else if (see_att)
		    msg("%s squeezes 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 you");
		else {
		    chg_str(-1);
		    msg(terse ? "A sting has weakened you" :
		    "You feel a sting in your arm and now 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 turned to 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 (see_def)
			    msg("%s turns to stone.", prname(defname, TRUE));
			else if (cansee(unc(def->t_pos)))
			    msg("A new statue appears!");
		    }
		}
		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.", 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 skillfull.", 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.", 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.",
				prname(attname, FALSE));
		    if (on(player, HASSTINK)) lengthen(unstink, STINKTIME);
		    else {
			turn_on(player, HASSTINK);
			fuse(unstink, 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) {
			int fuse_arg = 0;
			fuse(res_strength, &fuse_arg, 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.
	     */
	    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_MONK) {
			    msg("The wound heals quickly.");
		    }
		    else {
			turn_on(*def, HASDISEASE);
			fuse(cure_disease, NULL, roll(HEALTIME,SICKTIME), AFTER);
			msg(terse ? "You have been diseased."
			    : "You have contracted a 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 appears to be weaker now. Oh my!");
			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 and fall off!");
			    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 dissolve. Oh my!");
		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.
	     */
	    if (on(*att, CANINFEST) &&
		rnd(def->t_stats.s_const) < att->t_stats.s_lvl) {
		if (ISWEARING(R_HEALTH)		||
		    player.t_ctype == C_PALADIN	||
		    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, 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) < 25			&&
		(find_slot(suffocate) == 0)) {
		turn_on(*att, DIDSUFFOCATE);
		msg("%s is beginning to suffocate you.", prname(attname, TRUE));
		fuse(suffocate, 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))
		    msg("You smell an unpleasant odor.");
		else {
		    int odor_str = -(rnd(6)+1);
		    int fuse_arg2 = 0;
		    msg("You are overcome by a foul odor.");
		    if (lost_str == 0) {
			chg_str(odor_str);
			fuse(res_strength, &fuse_arg2, 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.
	     */
	    if (on(*att, CANROT)) {
		if (!ISWEARING(R_HEALTH)	&& 
		    player.t_ctype != C_PALADIN	&&
		    player.t_ctype != C_MONK	&&
		    !save(VS_POISON, def, 0)    && 
		    off(*def, DOROT)) {
		    turn_on(*def, DOROT);
		    msg("You feel your skin starting to rot 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 + GOLDCALC;
		if (!save(VS_MAGIC, def, att->t_stats.s_lvl/10)) {
		    if (on(*att, ISUNIQUE))
			purse -= GOLDCALC + GOLDCALC + GOLDCALC + GOLDCALC;
		    purse -= GOLDCALC + GOLDCALC + GOLDCALC + GOLDCALC;
		}
		if (purse < 0)
		    purse = 0;
		if (purse != lastpurse) {
		    msg("Your purse feels lighter");

		    /* 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;
			strncpy(obj->o_damage, "0d0", sizeof(obj->o_damage));
			strncpy(obj->o_hurldmg, "0d0", sizeof(obj->o_hurldmg));
			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);
}