view arogue5/fight.c @ 227:696277507a2e

Rogue V4, V5: disable a cheat granting permanent monster detection. In these two games, a potion of monster detection turns on the player's SEEMONST flag. A fuse is set to call turn_see() to turn the flag back off. But the save and restore functions do not recognize turn_see() and fail to set the fuse up again. When restoring, Rogue V4 merely sets the fuse's function to NULL and leaves it burning. When it goes off, a segfault results. Rogue V5 clears all the fuse's fields, and the player retains the ability to see all monsters on the level. The save and restore code can now handle the fuse. The function used is a new wrapper, turn_see_off(), which should lead to less problems with daemons being multiple incompatible types. Also, Rogue V4 and Super-Rogue now properly clear unrecognized daemon and fuse slots when restoring a saved game.
author John "Elwin" Edwards
date Sat, 05 Mar 2016 12:10:20 -0500
parents 56e748983fa8
children 0990adf580ee
line wrap: on
line source

/*
 * All the fighting gets done here
 *
 * 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 "curses.h"
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "rogue.h"

bool roll_em(struct thing *att_er, struct thing *def_er, struct object *weap, 
             bool hurl, struct object *cur_weapon, bool back_stab);
void hit(struct object *weapon, struct thing *tp, char *er, char *ee, 
         bool back_stab);
void miss(struct object *weapon, struct thing *tp, char *er, char *ee);
int dext_plus(int dexterity);
int str_plus(short str);
int add_dam(short str);
int hung_dam(void);
void thunk(struct object *weap, struct thing *tp, char *mname);
void m_thunk(struct object *weap, struct thing *tp, char *mname);
void bounce(struct object *weap, struct thing *tp, char *mname);
void m_bounce(struct object *weap, struct thing *tp, char *mname);
struct object *wield_weap(struct object *thrown, struct thing *mp);
void explode(struct thing *tp);

#define CONF_DAMAGE	-1
#define PARAL_DAMAGE	-2
#define DEST_DAMAGE	-3

static const struct matrix att_mat[5] = {
/* Base		Max_lvl,	Factor,		Offset,		Range */
{  10,		25,		2,		1,		2 },
{  9,		18,		2,		1,		5 },
{  10,		19,		2,		1,		3 },
{  10,		21,		2,		1,		4 },
{   7,		25,		1,		0,		2 }
};

/*
 * fight:
 *	The player attacks the monster.
 */

bool
fight(coord *mp, struct object *weap, bool thrown)
{
    register struct thing *tp;
    register struct linked_list *item;
    register bool did_hit = TRUE;
    bool back_stab = FALSE;

    /*
     * Find the monster we want to fight
     */
    if ((item = find_mons(mp->y, mp->x)) == NULL) {
	return(FALSE); /* must have killed him already */
    }
    tp = THINGPTR(item);
    /*
     * Since we are fighting, things are not quiet so no healing takes
     * place.
     */
    player.t_quiet = 0;
    tp->t_quiet = 0;

    /*
     * if its in the wall, we can't hit it
     */
    if (on(*tp, ISINWALL) && off(player, CANINWALL))
	return(FALSE);

    /*
     * Let him know it was really a mimic (if it was one).
     */
    if (on(*tp, ISDISGUISE) && (tp->t_type != tp->t_disguise) &&
	off(player, ISBLIND))
    {
	msg("Wait! That's a %s!", monsters[tp->t_index].m_name);
	turn_off(*tp, ISDISGUISE);
	did_hit = thrown;
    }
    if (on(*tp, CANSURPRISE) && off(player, ISBLIND) && !ISWEARING(R_ALERT)) {
	msg("Wait! There's a %s!", monsters[tp->t_index].m_name);
	turn_off(*tp, CANSURPRISE);
	did_hit = thrown;
    }
    /*
     * if he's a thief and the creature is asleep then he gets a chance
     * for a backstab
     */
    if (player.t_ctype == C_THIEF			 	 && 
	(!on(*tp, ISRUN) || on(*tp, ISHELD) || tp->t_no_move > 0)&&
	!on(*tp, NOSTAB))
	back_stab = TRUE;

    runto(tp, &hero);

    if (did_hit)
    {
	register const char *mname;

	did_hit = FALSE;
	mname = (on(player, ISBLIND)) ? "it" : monsters[tp->t_index].m_name;
	if (!can_blink(tp) &&
	    ( ((weap != NULL) && (weap->o_type == RELIC)) ||
	     ((off(*tp, MAGICHIT)  || ((weap != NULL) && (weap->o_hplus > 0 || weap->o_dplus > 0)) ) &&
	      (off(*tp, BMAGICHIT) || ((weap != NULL) && (weap->o_hplus > 1 || weap->o_dplus > 1)) ) &&
	      (off(*tp, CMAGICHIT) || ((weap != NULL) && (weap->o_hplus > 2 || weap->o_dplus > 2)) ) ) )
	    && roll_em(&player, tp, weap, thrown, cur_weapon, back_stab))
	{
	    did_hit = TRUE;

	    if (on(*tp, NOMETAL) && weap != NULL &&
		weap->o_type != RELIC && weap->o_flags & ISMETAL) {
		sprintf(outstring,"Your %s passes right through the %s!",
		    weaps[weap->o_which].w_name, mname);
		msg(outstring);
	    }
	    else if (thrown) {
		tp->t_wasshot = TRUE;
		thunk(weap, tp, mname);
	    }
	    else
		hit(weap, tp, NULL, mname, back_stab);

	    /* If the player hit a rust monster, he better have a + weapon */
	    if (on(*tp, CANRUST) && !thrown && (weap != NULL) &&
		weap->o_type != RELIC &&
		(weap->o_flags & ISMETAL) &&
		!(weap->o_flags & ISPROT) &&
		(weap->o_hplus < 1) && (weap->o_dplus < 1)) {
		if (rnd(100) < 50) weap->o_hplus--;
		else weap->o_dplus--;
		msg(terse ? "Your %s weakens!"
			  : "Your %s appears to be weaker now!",
		    weaps[weap->o_which].w_name);
	    }
		
	    /* If the player hit something that shrieks, wake the dungeon */
	    if (on(*tp, CANSHRIEK)) {
		turn_off(*tp, CANSHRIEK);
		msg("The %s emits a piercing shriek.", mname);
		aggravate();
	    }

	    /* If the player hit something that can surprise, it can't now */
	    if (on(*tp, CANSURPRISE)) turn_off(*tp, CANSURPRISE);


	    /* 
	     * Can the player confuse? 
	     */
	    if (on(player, CANHUH) && !thrown) {
		msg("Your hands stop glowing red");
		msg("The %s appears confused.", mname);
		turn_on(*tp, ISHUH);
		turn_off(player, CANHUH);
	    }
	    /*
	     * does the creature explode when hit?
	     */
	    if (on(*tp, CANEXPLODE))
		explode(tp);

	    /* 
	     * Merchants just disappear if hit 
	     */
	    if (on(*tp, CANSELL)) {
		msg("The %s disappears with his wares in a flash.",mname);
		killed(item, FALSE, FALSE);
	    }

	    else if (tp->t_stats.s_hpt <= 0)
		killed(item, TRUE, TRUE);

	    /* 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(suffocate);
		}

		/* If monster held us, stop it */
		if (on(*tp, DIDHOLD) && (--hold_count == 0))
			turn_off(player, ISHELD);
		turn_off(*tp, DIDHOLD);
	    }
	}
	else {
	    if (thrown)
		bounce(weap, tp, mname);
	    else
		miss(weap, tp, NULL, mname);
	}
    }
    count = 0;
    return did_hit;
}

/*
 * attack:
 *	The monster attacks the player
 */

bool
attack(struct thing *mp, struct object *weapon, bool thrown)
{
    register const char *mname;
    register bool did_hit = FALSE;
    register struct object *wielded;	/* The wielded weapon */

    /*
     * Since this is an attack, stop running and any healing that was
     * going on at the time.
     */
    running = FALSE;
    player.t_quiet = 0;
    mp->t_quiet = 0;

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

    /*
     * Try to find a weapon to wield.  Wield_weap will return a
     * projector if weapon is a projectile (eg. bow for arrow).
     * If weapon is NULL, it will try to find a suitable weapon.
     */
    wielded = wield_weap(weapon, mp);
    if (weapon == NULL) weapon = wielded;

    if (roll_em(mp, &player, weapon, thrown, wielded, FALSE)) {
	did_hit = TRUE;

	if (thrown) m_thunk(weapon, mp, mname);
	else hit(weapon, mp, mname, NULL, FALSE);

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

	/*
	 * suprising monsters appear after they shoot at you 
	 */
	if (thrown) {
	    if (on(*mp, CANSURPRISE)) 
		turn_off(*mp, CANSURPRISE);
	}
	if (!thrown) {
	    /*
	     * If a vampire hits, it may take half your hit points
	     */
	    if (on(*mp, CANSUCK) && !save(VS_MAGIC, &player, 0)) {
		if (pstats.s_hpt == 1) death(mp->t_index);
		else {
		  pstats.s_hpt /= 2;
		  msg("You feel your life force being drawn from you.");
		}
	    }

	    /*
	     * Stinking monsters make player weaker (to hit)
	     */
	    if (on(*mp, CANSTINK)) {
		turn_off(*mp, CANSTINK);
		if (!save(VS_POISON, &player, 0)) {
		    msg("The stench of the %s sickens you.", mname);
		    if (on(player, HASSTINK)) lengthen(unstink, STINKTIME);
		    else {
			turn_on(player, HASSTINK);
			fuse(unstink, 0, STINKTIME, AFTER);
		    }
		}
	    }

	    /*
	     * Chilling monster reduces strength each time
	     */
	    if (on(*mp, CANCHILL)) {
		if (!ISWEARING(R_SUSABILITY) && !save(VS_POISON, &player, 0)) {
		    msg("You cringe at the %s's chilling touch.", mname);
		    chg_str(-1);
		    if (lost_str++ == 0)
			fuse(res_strength, 0, CHILLTIME, AFTER);
		    else lengthen(res_strength, CHILLTIME);
		}
	    }

	    /*
	     * itching monsters reduce dexterity (temporarily)
	     */
	    if (on(*mp, CANITCH) && !save(VS_POISON, &player, 0)) {
		msg("The claws of the %s scratch you", mname);
		if(ISWEARING(R_SUSABILITY)) {
		    msg("The scratch has no effect");
		}
		else {
		    turn_on(player, HASITCH);
		    add_dexterity(TRUE);
		    lost_dext++;
		    fuse(un_itch, 0, roll(HEALTIME,SICKTIME), AFTER);
		}
	    }


	    /*
	     * If a hugging monster hits, it may SQUEEEEEEEZE
	     */
	    if (on(*mp, CANHUG)) {
		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);
		}
	    }

	    /*
	     * If a disease-carrying monster hits, there is a chance the
	     * player will catch the disease
	     */
	    if (on(*mp, CANDISEASE) &&
		(rnd(pstats.s_const) < mp->t_stats.s_lvl) &&
		off(player, HASDISEASE)) {
		if (ISWEARING(R_HEALTH)) msg("The wound heals quickly.");
		else {
		    turn_on(player, HASDISEASE);
		    fuse(cure_disease, 0, roll(HEALTIME,SICKTIME), AFTER);
		    msg(terse ? "You have been diseased."
			: "You have contracted a disease!");
		}
	    }

	    /*
	     * If a rust monster hits, you lose armor
	     */
	    if (on(*mp, 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 < pstats.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
	     */
	    if (on(*mp, 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 < pstats.s_arm+1) {
		msg(terse ? "Your armor dissolves"
		    : "Your armor appears to dissolve. Oh my!");
		cur_armor->o_ac++;
	    }

	    /* If a surprising monster hit you, you can see it now */
	    if (on(*mp, CANSURPRISE)) turn_off(*mp, CANSURPRISE);

	    /*
	     * If an infesting monster hits you, you get a parasite or rot
	     */
	    if (on(*mp, CANINFEST) && rnd(pstats.s_const) < mp->t_stats.s_lvl) {
		if (ISWEARING(R_HEALTH)) msg("The wound quickly heals.");
		else {
		    turn_off(*mp, CANINFEST);
		    msg(terse ? "You have been infested."
			: "You have contracted a parasitic infestation!");
		    infest_dam++;
		    turn_on(player, HASINFEST);
		}
	    }

	    /*
	     * Ants have poisonous bites
	     */
	    if (on(*mp, CANPOISON) && !save(VS_POISON, &player, 0)) {
		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");
		}
	    }
	    /*
	     * does it take wisdom away?
	     */
	    if (on(*mp, TAKEWISDOM)		&& 
		!save(VS_MAGIC, &player, 0)	&&
		!ISWEARING(R_SUSABILITY)) {
			add_wisdom(TRUE);
	    }
	    /*
	     * does it take intelligence away?
	     */
	    if (on(*mp, TAKEINTEL)		&& 
		!save(VS_MAGIC, &player, 0)	&&
		!ISWEARING(R_SUSABILITY)) {
			add_intelligence(TRUE);
	    }
	    /*
	     * Cause fear by touching
	     */
	    if (on(*mp, TOUCHFEAR)) {
		turn_off(*mp, TOUCHFEAR);
		if (!ISWEARING(R_HEROISM)	&&
		    !save(VS_WAND, &player, 0)	&&
		    !(on(player, ISFLEE) && (player.t_dest == &mp->t_pos))) {
			turn_on(player, ISFLEE);
			player.t_dest = &mp->t_pos;
			msg("The %s's touch terrifies you.", mname);
		}
	    }

	    /*
	     * make the hero dance (as in otto's irresistable dance)
	     */
	    if (on(*mp, CANDANCE) 	&& 
		!on(player, ISDANCE)	&&
		!save(VS_MAGIC, &player, -4)) {
		    turn_off(*mp, CANDANCE);
		    turn_on(player, ISDANCE);
		    msg("You begin to dance uncontrollably!");
		    fuse(undance, 0, roll(2,4), AFTER);
	    }

	    /*
	     * Suffocating our hero
	     */
	    if (on(*mp, CANSUFFOCATE) && (rnd(100) < 15) &&
		(find_slot(suffocate) == FALSE)) {
		turn_on(*mp, DIDSUFFOCATE);
		msg("The %s is beginning to suffocate you.", mname);
		fuse(suffocate, 0, roll(4,2), AFTER);
	    }

	    /*
	     * Turning 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, &player, 0) && rnd(100) < 15) {
		        msg("Your body begins to solidify.");
		        msg("You are turned to stone !!! --More--");
		        wait_for(cw,' ');
			death(D_PETRIFY);
		    }
		    else {
			msg("The %s's touch stiffens your limbs.", mname);
			no_command += STONETIME;
		    }
		}
	    }

	    /*
	     * Wraiths might drain energy levels
	     */
	    if ((on(*mp, CANDRAIN) || on(*mp, DOUBLEDRAIN)) && rnd(100) < 15) {
		lower_level(mp->t_index);
		if (on(*mp, DOUBLEDRAIN)) lower_level(mp->t_index);
		turn_on(*mp, DIDDRAIN);  
	    }

	    /*
	     * Violet fungi stops the poor guy from moving
	     */
	    if (on(*mp, CANHOLD) && off(*mp, DIDHOLD)) {
		turn_on(player, ISHELD);
		turn_on(*mp, DIDHOLD);
		hold_count++;
	    }

	    /*
	     * Sucker will suck blood and run
	     */
	    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);
	    }

	    /*
	     * Bad smell will force a reduction in strength
	     */
	    if (on(*mp, CANSMELL)) {
		turn_off(*mp, CANSMELL);
		if (save(VS_MAGIC, &player, 0) || ISWEARING(R_SUSABILITY))
		    msg("You smell an unpleasant odor.");
		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, 0, SMELLTIME, AFTER);
			lost_str -= odor_str;
		    }
		    else lengthen(res_strength, SMELLTIME);
		}
	    }

	    /*
	     * Paralyzation
	     */
	    if (on(*mp, CANPARALYZE)) {
		turn_off(*mp, CANPARALYZE);
		if (!save(VS_PARALYZATION, &player, 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;
		    }
		}
	    }

	    /*
	     * Painful wounds make you faint
	     */
	     if (on(*mp, CANPAIN)) {
		turn_off(*mp, CANPAIN);
		if (!ISWEARING(R_ALERT) && !save(VS_POISON, &player, 0)) {
			msg("You faint from the painful wound");
			no_command += PAINTIME;
		}
	    }

	    /*
	     * The monsters touch slows the hero down
	     */
	     if (on(*mp, CANSLOW)) {
		turn_off(*mp, CANSLOW);
		if (!save(VS_PARALYZATION, &player, 0)) 
			add_slow();
	    }

	    /*
	     * Rotting
	     */
	    if (on(*mp, CANROT)) {
		if (!ISWEARING(R_HEALTH) && 
		    !save(VS_POISON, &player, 0)     && 
		    off(player, DOROT)) {
		    turn_on(player, DOROT);
		    msg("You feel your skin starting to rot away!");
		}
	    }

	    if (on(*mp, 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, &player, mp->t_stats.s_lvl/10)) {
		    if (on(*mp, 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=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;
			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 = mp->t_pos;

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

		turn_on(*mp, ISFLEE);
		turn_on(*mp, ISINVIS);
	    }

	    if (on(*mp, 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 = pack; list != NULL; list = next(list))
		{
		    obj = OBJPTR(list);
		    if (!is_current(obj) &&
			is_magic(obj)	 && 
			rnd(++nobj) == 0)
			    steal = list;
		}
		if (steal != NULL)
		{
		    register struct object *obj;
		    struct linked_list *item;

		    obj = OBJPTR(steal);
		    if (on(*mp, ISUNIQUE))
			monsters[mp->t_index].m_normal = TRUE;
		    item = find_mons(mp->t_pos.y, mp->t_pos.x);
		    killed(item, FALSE, FALSE);
		    if (obj->o_count > 1 && obj->o_group == 0) {
			register int oc;

			oc = --(obj->o_count);
			obj->o_count = 1;
			sprintf(outstring,"The %s stole %s!", mname, inv_name(obj, TRUE));
			msg(outstring);
			obj->o_count = oc;
		    }
		    else {
			sprintf(outstring,"The %s stole %s!", mname, inv_name(obj, TRUE));
			msg(outstring);
			detach(pack, steal);

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

			o_discard(steal);
		        inpack--;
		    }
		    updpack(FALSE);
		}
	    }
	}
    }
    else {
	/* If the thing was trying to surprise, no good */
	if (on(*mp, CANSURPRISE)) turn_off(*mp, CANSURPRISE);

	else if (thrown) m_bounce(weapon, mp, mname);
	else miss(weapon, mp, mname, NULL);
    }
    if (fight_flush)
	md_flushinp();
    count = 0;
    status(FALSE);
    return(did_hit);
}

/*
 * swing:
 *	returns true if the swing hits
 */

bool
swing(short class, int at_lvl, int op_arm, int wplus)
{
    register int res = rnd(20)+1;
    register 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);
}

/*
 * roll_em:
 *	Roll several attacks
 */

bool
roll_em(struct thing *att_er, struct thing *def_er, struct object *weap, 
        bool hurl, struct object *cur_weapon, bool back_stab)
{
    register struct stats *att, *def;
    register char *cp = NULL;
    register int ndice, nsides, nplus, def_arm;
    bool did_hit = FALSE;
    int prop_hplus, prop_dplus;
    int vampiric_damage;

    /* Get statistics */
    att = &att_er->t_stats;
    def = &def_er->t_stats;

    prop_hplus = prop_dplus = 0;
    if (weap == NULL)
	cp = att->s_dmg;
    else if (hurl) {
	if ((weap->o_flags&ISMISL) && cur_weapon != NULL &&
	  cur_weapon->o_which == weap->o_launch)
	{
	    cp = weap->o_hurldmg;
	    prop_hplus = cur_weapon->o_hplus;
	    prop_dplus = cur_weapon->o_dplus;
	}
	else
	    cp = (weap->o_flags&ISMISL ? weap->o_damage : weap->o_hurldmg);
    }
    else {
	if (weap->o_type == RELIC) {
	    switch (weap->o_which) {
		case MUSTY_DAGGER:	cp = "1d4+1/1d4+1";
		when YEENOGHU_FLAIL:	cp = "3d6/paralyze/confuse";
		when HRUGGEK_MSTAR:	cp = "3d10";
		when MING_STAFF:	cp = "1d8";
		when ASMO_ROD:		cp = "2d8+1";
		when ORCUS_WAND:	cp = "destroy";
	    }
	}
	else cp = weap->o_damage;
	/*
	 * Drain a staff of striking
	 */
	if (weap->o_type == STICK && weap->o_which == WS_HIT
	    && weap->o_charges == 0)
		{
		    strcpy(weap->o_damage,"0d0");
		    weap->o_hplus = weap->o_dplus = 0;
		}
    }
    for (;;)
    {
	int damage;
	int hplus = prop_hplus;
	int dplus = prop_dplus;

	if (weap != NULL && weap->o_type == RELIC) {
	    switch (weap->o_which) {
		case MUSTY_DAGGER:
		    if (att != &pstats || /* Not player or good stats */
			(str_compute() > 15 && dex_compute() > 15)) {

			hplus += 6;
			dplus += 6;

			/* Give an additional strength and dex bonus */
			if (att == &pstats) {
			    hplus += str_plus(str_compute()) +
				     dext_plus(dex_compute());
			    dplus += dext_plus(dex_compute()) +
				     add_dam(str_compute());
			}
			else {
			    hplus += str_plus(att->s_str) +
				     dext_plus(att->s_dext);
			    dplus += dext_plus(att->s_dext) +
				     add_dam(att->s_str);
			}
		    }
		    else {
			hplus -= 3;
			dplus -= 3;
		    }
		when YEENOGHU_FLAIL:
		case HRUGGEK_MSTAR:
		    hplus += 3;
		    dplus += 3;
		when MING_STAFF:
		    hplus += 2;
		    dplus += 2;
	    }
	}
	else if (weap != NULL) {
	    hplus += weap->o_hplus;
	    dplus += weap->o_dplus;
	}

	/* Is attacker weak? */
	if (on(*att_er, HASSTINK)) hplus -= 2;

	if (att == &pstats)	/* Is the attacker the player? */
	{
	    hplus += hitweight();	/* adjust for encumberence */
	    dplus += hung_dam();	/* adjust damage for hungry player */
	    dplus += ring_value(R_ADDDAM);
	}
	if (back_stab || (weap && att != &pstats && on(*att_er, CANBSTAB)))
	    hplus += 4;	/* add in pluses for backstabbing */

	/* Get the damage */