view arogue7/fight.c @ 200:1cd604c827a3

Advanced Rogue 7: initialize multiple variables. MSVC complained that they might be used uninitialized. In some cases, this might have been possible. XRogue already has initializations for all these variables.
author John "Elwin" Edwards
date Wed, 12 Aug 2015 15:42:02 -0400
parents 66b0263af424
children 382ff498acdd
line wrap: on
line source

/*
 * fight.c  -  All the fighting gets done here
 *
 * 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.
 */

/*
 * All the fighting gets done here
 *
 */

#include "curses.h"
#include <ctype.h>
#include <string.h>
#include "rogue.h"

int hit(struct object *weapon, bool see_att, bool see_def, char *er, char *ee, bool back_stab, bool thrown, bool short_msg);
#define CONF_DAMAGE	-1
#define PARAL_DAMAGE	-2
#define DEST_DAMAGE	-3
#define DRAIN_DAMAGE	-4

/*
 * returns true if player has a any chance to hit the monster
 */
player_can_hit(tp, weap)
register struct thing *tp;
register struct object *weap;
{
    if (off(*tp, CMAGICHIT) && off(*tp, BMAGICHIT) && off(*tp, MAGICHIT))
	return(TRUE);
    if (weap && weap->o_type == RELIC)
	return(TRUE);
    if (on(*tp, CMAGICHIT) && weap && (weap->o_hplus>2 || weap->o_dplus>2))
	return(TRUE);
    if (on(*tp, BMAGICHIT) && weap && (weap->o_hplus>1 || weap->o_dplus>1))
	return(TRUE);
    if (on(*tp,  MAGICHIT) && weap && (weap->o_hplus>0 || weap->o_dplus>0))
	return(TRUE);
    if (player.t_ctype == C_MONK) {
	if (on(*tp, CMAGICHIT) && pstats.s_lvl > 15)
	    return(TRUE);
	if (on(*tp, BMAGICHIT) && pstats.s_lvl > 10)
	    return(TRUE);
	if (on(*tp,  MAGICHIT) && pstats.s_lvl > 5)
	    return(TRUE);
    }
    return(FALSE);
}

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

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

    /*
     * 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.  The -1 also tells us that we are in a fight.
     */
    player.t_quiet = -1;
    tp->t_quiet = -1;

    see_def = ((off(*tp, ISINVIS)     || on(player, CANSEE)) &&
	       (off(*tp, ISSHADOW)    || on(player, CANSEE)) &&
	       (!thrown || cansee(unc(tp->t_pos))));

    mname = see_def ? monster_name(tp) : "something";

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

    if (on(*tp, ISSTONE)) {
	killed(item, FALSE, FALSE, FALSE);
	if (see_def) 
	    msg("%s shatters into a million pieces!", prname(mname, TRUE));
	count = 0;
	return (TRUE);
    }
    /*
     * 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))
    {
	if (see_def) {
	    msg("Wait! That's a %s!", mname);
	    turn_off(*tp, ISDISGUISE);
	}
	did_hit = thrown;
    }
    if (on(*tp, CANSURPRISE) && off(player, ISBLIND) && !ISWEARING(R_ALERT)) {
	if (see_def) {
	    msg("Wait! There's a %s!", mname);
	    turn_off(*tp, CANSURPRISE);
	}
	did_hit = thrown;
    }

    /*
     * if he's a thief or assassin and the creature is asleep then he gets 
     * a chance for a backstab
     */
    if ((player.t_ctype == C_THIEF || player.t_ctype == C_ASSASIN)	&&
	!thrown								&&
	!on(*tp, NOSTAB)						&&
	!invisible(tp)							&&
	(!on(*tp, ISRUN) || on(*tp, ISHELD) || tp->t_action == A_FREEZE))
	    back_stab = TRUE;

    /*
     * assassins get an assassination chance, if it fails then its normal
     * damage
     */
    if (back_stab && player.t_ctype == C_ASSASIN) {
	int chance;

	chance = 50 + (pstats.s_lvl - tp->t_stats.s_lvl) * 5;
	if (cur_weapon && (cur_weapon->o_flags & ISPOISON))
	    chance += 20;
	if (roll(1,100) > chance || on(*tp, ISUNIQUE))
	    back_stab = FALSE;
    }

    runto(tp, &hero);

    /* Let the monster know that the player has missiles! */
    if (thrown) tp->t_wasshot = TRUE;

    if (did_hit)
    {

	did_hit = FALSE;
	if (!can_blink(tp)		&& 
	    player_can_hit(tp, weap)	&&
	    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) {
		msg("Your %s passes right through %s!",
		    weaps[weap->o_which].w_name, prname(mname, FALSE));
	    }
	    else if (weap != NULL && weap->o_type == MISSILE && on(*tp, CARRYBAMULET)) {
		    msg("The magic missile has no affect on %s",
			prname(mname, FALSE));
	    }
	    else {
		hit(thrown ? NULL : weap,
		    TRUE, see_def,
		    thrown ? weap_name(weap) : NULL,
		    mname, back_stab, thrown, terse);

	        /* See if there are any special effects */
	        if (effect(&player, tp, weap, thrown, TRUE, see_def) != 0)
		    killed(item, FALSE, FALSE, TRUE);
    
	        /* 
	         * Merchants just disappear if hit 
	         */
	        else if (on(*tp, CANSELL)) {
		    if (see_def)
		        msg("%s disappears with his wares in a flash.",
			    prname(mname, FALSE));
		    killed(item, FALSE, FALSE, FALSE);
	        }
    
	        else if (tp->t_stats.s_hpt <= 0)
		    killed(item, TRUE, TRUE, TRUE);
    
	        else {
		    /* If the victim was charmed, it now gets a saving throw! */
		    if (on(*tp, ISCHARMED) && save(VS_MAGIC, tp, 0)) {
			msg("The eyes of %s turn clear.", prname(mname, FALSE));
			turn_off(*tp, ISCHARMED);
		    }

		    dsrpt_monster(tp, FALSE, see_def); /* Disrupt a spell? */
	        }
	    }
	}
	else {
	    miss(thrown ? NULL : weap,
		 TRUE, see_def,
		 thrown ? weap_name(weap) : NULL,
		 mname, thrown, terse);
	}
    }
    count = 0;
    return did_hit;
}

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

attack(mp, weapon, thrown)
register struct thing *mp;
register struct object *weapon;
bool thrown;
{
    register char *mname;
    register bool see_att, did_hit = FALSE;
    register struct object *wielded;	/* The wielded weapon */
    struct linked_list *get_wield;	/* Linked list header for wielded */

    /*
     * Since this is an attack, stop running and any healing that was
     * going on at the time.  The -1 also tells us that we're fighting.
     */
    running = FALSE;
    player.t_quiet = -1;
    mp->t_quiet = -1;

    if (on(*mp, ISDISGUISE) && off(player, ISBLIND))
	turn_off(*mp, ISDISGUISE);

    see_att = ((off(*mp, ISINVIS)     || on(player, CANSEE)) &&
	       (off(*mp, ISSHADOW)    || on(player, CANSEE)) &&
	       (!thrown || cansee(unc(mp->t_pos))));

    mname = see_att ? monster_name(mp) : "something";

    /*
     * 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.
     */
    get_wield = wield_weap(weapon, mp);
    if (get_wield) wielded = OBJPTR(get_wield);
    else wielded = NULL;

    /* If we aren't wielding a weapon, wield what we found (could be NULL) */
    if (weapon == NULL) weapon = wielded;

    if (roll_em(mp, &player, weapon, thrown, wielded, FALSE)) {
	int death_type;	/* From one of the effects of getting hit */

	did_hit = TRUE;

	if (weapon != NULL && weapon->o_type == MISSILE && cur_relic[STONEBONES_AMULET]) {
	    hit(weapon, see_att, TRUE, mname, NULL, FALSE, thrown, terse);
	    msg("Your amulet seems to absorb the magic missile");
	}
	else {
	    hit(weapon, see_att, TRUE, mname, NULL, FALSE, thrown, terse);
	    dsrpt_player();	/* see if we disrupted some activity */
	    if (pstats.s_hpt <= 0)
		death(mp->t_index);	/* Bye bye life ... */
	    death_type = effect(mp, &player, weapon, thrown, see_att, TRUE);
	    if (death_type != 0) death(death_type);
	}

    }
    else {
	/* If the thing was trying to surprise, no good */
	if (on(*mp, CANSURPRISE)) turn_off(*mp, CANSURPRISE);

	/* If it couldn't surprise, let's tell the player. */
	else miss(weapon, see_att, TRUE, mname, NULL, thrown, terse);
    }
    if (fight_flush) md_flushinp();
    count = 0;
    status(FALSE);
    return(did_hit);
}

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

swing(class, at_lvl, op_arm, wplus)
short class;
int at_lvl, op_arm, wplus;
{
    register int res = rnd(20)+1;
    register int need;

    need = char_class[class].base -
	   char_class[class].factor *
	   ((min(at_lvl, char_class[class].max_lvl) -
	    char_class[class].offset)/char_class[class].range) +
	   (10 - op_arm);
    if (need > 20 && need <= 25) need = 20;

    return (res+wplus >= need);
}

/*
 * roll_em:
 *	Roll several attacks
 */

roll_em(att_er, def_er, weap, hurl, cur_weapon, back_stab)
struct thing *att_er, *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) {
	static char dmgbuf[20];

	/*
	 * monks damage grows with level
	 */
	if (att == &pstats && player.t_ctype == C_MONK) {
	    sprintf(dmgbuf, "%dd4", att->s_lvl/3+1);
	    cp = dmgbuf;
	}
	else
	    cp = att->s_dmg;
    }
    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";
	    when AXE_AKLAD:		if (hurl) cp = "1d6/drain";
				        else	  cp = "3d6/drain";
	}
    }
    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 {
	cp = weap->o_damage;
	/*
	 * Drain a staff of striking
	 */
	if(weap->o_type==STICK && weap->o_which==WS_HIT && weap->o_charges==0)
	{
	    strncpy(weap->o_damage, "1d6", sizeof(weap->o_damage));
	    weap->o_hplus = weap->o_dplus = 0;
	}
    }
    /*
     * If defender is wearing a cloak of displacement -- no damage
     * the first time. (unless its a hurled magic missile or the
     * attacker is very smart and can see thru the illusion)
     */
    if ((weap == NULL || weap->o_type != MISSILE)	&&
	def == &pstats					&&	
	off(*att_er, MISSEDDISP)			&&
	att->s_intel < 22				&&
	((cur_misc[WEAR_CLOAK]!=NULL && 
	  cur_misc[WEAR_CLOAK]->o_which==MM_DISP) ||
	  cur_relic[EMORI_CLOAK])) {
	turn_on(*att_er, MISSEDDISP);
	if (cansee(att_er->t_pos.y, att_er->t_pos.x) && !invisible(att_er))
	    msg("%s looks amazed", prname(monster_name(att_er), TRUE));
	return (FALSE);
    }
    if (on(*def_er, CARRYCLOAK)	&& def != &pstats		&& 
        (weap == NULL || weap->o_type != MISSILE) && off(*att_er, MISSEDDISP) &&
	pstats.s_intel < 22) {
	turn_on(*att_er, MISSEDDISP);
	msg("You feel amazed");
	return(FALSE);
    }
    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;
		when AXE_AKLAD:
		    hplus += 5;
		    dplus += 5;
	    }
	}
	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 */
	while (isspace(*cp)) cp++;
	if (!isdigit(*cp)) {
	    if (strncmp(cp, "confuse", 7) == 0) ndice = CONF_DAMAGE;
	    else if (strncmp(cp, "paralyze", 8) == 0) ndice = PARAL_DAMAGE;
	    else if (strncmp(cp, "destroy", 7) == 0) ndice = DEST_DAMAGE;
	    else if (strncmp(cp, "drain", 5) == 0) ndice = DRAIN_DAMAGE;
	    else ndice = 0;
	    nsides = 0;
	    nplus = 0;
	}
	else {
	    char *oldcp;

	    /* Get the number of damage dice */
	    ndice = atoi(cp);
	    if ((cp = strchr(cp, 'd')) == NULL)
		break;

	    /* Skip the 'd' and get the number of sides per die */
	    nsides = atoi(++cp);

	    /* Check for an addition -- save old place in case none is found */
	    oldcp = cp;
	    if ((cp = strchr(cp, '+')) != NULL) nplus = atoi(++cp);
	    else {
		nplus = 0;
		cp = oldcp;
	    }
	}

	if (def == &pstats) { /* Monster attacks player */
	    if (on(*att_er, NOMETAL))
		def_arm = ac_compute(TRUE) - dext_prot(dex_compute());
	    else
		def_arm = ac_compute(FALSE) - dext_prot(dex_compute());
	    hplus += str_plus(att->s_str)+dext_plus(att->s_dext);
	}
	else if (att == &pstats) {	/* Player attacks monster */
	    def_arm = def->s_arm - dext_prot(def->s_dext);
	    if (player.t_ctype == C_MONK) /* no strength bonus for monk */
	        if (weap == NULL) 
		    hplus += att->s_lvl/5; /* monks hplus varies with level */
	    else
		hplus += str_plus(str_compute())+dext_plus(dex_compute());
	}
	else {	/* Monster attacks monster */
	    def_arm = def->s_arm - dext_prot(def->s_dext);
	    hplus += str_plus(att->s_str)+dext_plus(att->s_dext);
	}

	if (swing(att_er->t_ctype, att->s_lvl, def_arm, hplus)) {
	    register int proll;

	    /* Take care of special effects */
	    switch (ndice) {
	      case CONF_DAMAGE:
		if (def == &pstats) { /* Monster attacks player */
		    if (!save(VS_MAGIC, &player, 0) && off(player, ISCLEAR)) {
			msg("You feel disoriented.");
			if (find_slot(unconfuse))
			    lengthen(unconfuse, HUHDURATION);
			else
			    fuse(unconfuse, 0, HUHDURATION, AFTER);
			turn_on(player, ISHUH);
		    }
		    else msg("You feel dizzy, but it quickly passes.");
		}
		/* Player or monster hits monster */
		else if (!save(VS_MAGIC, def_er, 0) && off(*def_er, ISCLEAR)) { 
		    if (att == &pstats)
			msg("The artifact warms with pleasure.");
		    turn_on(*def_er, ISHUH);
		}
		did_hit = TRUE;
	      when PARAL_DAMAGE:
		if (def == &pstats) { /* Monster attacks player */
		    if (!save(VS_MAGIC, &player, 0) && off(player, CANINWALL)) {
			msg("You stiffen up.");
			player.t_no_move += movement(&player) * FREEZETIME;
			player.t_action = A_FREEZE;
		    }
		}
		else if (!save(VS_MAGIC, def_er, 0)) { /* Player hits monster */
		    if (att == &pstats) msg("The artifact hums happily.");
		    turn_off(*def_er, ISRUN);
		    turn_on(*def_er, ISHELD);
		}
		did_hit = TRUE;
	      when DEST_DAMAGE:
		if (def == &pstats) { /* Monster attacks player */
		    msg("You feel a tug at your life force.");
		    if (!save(VS_MAGIC, &player, -4)) {
			msg("The wand devours your soul.");
			def->s_hpt = 0;
		    }
		}
		/* Player hits monster */
		else if (!save(VS_MAGIC, def_er, -4)) {
		    if (att == &pstats)
			msg("The artifact draws energy.");

		    /* Give the attacker half the monster's hits */
		    att->s_hpt += def->s_hpt/2;
		    if (att->s_hpt > att_er->maxstats.s_hpt)
			att->s_hpt = att_er->maxstats.s_hpt;

		    /* Kill the monster */
		    def->s_hpt = 0;
		}
		did_hit = TRUE;
	    when DRAIN_DAMAGE:
		if (def == &pstats) { /* Monster attacks player */
		    if (!save(VS_MAGIC, &player, -4)) {
			lower_level(att_er->t_index);
		    }
		}
		/* Player hits monster */
		else if (!save(VS_MAGIC, def_er, -4)) {
		    def->s_hpt -= roll(1, 8);
		    def->s_lvl--;
		    if (def->s_lvl <= 0)
			def->s_hpt = 0; /* he's dead */
		    if (att == &pstats) 
			msg("The artifact cackles with laughter");
		}
		did_hit = TRUE;
	      otherwise:
		/* Heil's ankh always gives maximum damage */
		if (att == &pstats && cur_relic[HEIL_ANKH])
		    proll = ndice * nsides;
		else proll = roll(ndice, nsides);

		if (ndice + nsides > 0 && proll < 1)
		    debug("Damage for %dd%d came out %d.",
				ndice, nsides, proll);
		damage = dplus + proll + nplus;
		if (att == &pstats) {
		    /*
		     * Monks do not get strength bonus on damage.  Instead,
		     * if they are wielding a weapon, they get at extra
		     * 1/2 point per level of damage.
		     */
		    if(player.t_ctype == C_MONK) {
			/* Bonus does not apply for hands. */
			if (weap != NULL) damage += att->s_lvl / 2;
		    }
		    else
			damage += add_dam(str_compute());
		}
		else
		    damage += add_dam(att->s_str);

		/* Check for half damage monsters */
		if (on(*def_er, HALFDAMAGE)) damage /= 2;

		/* add in multipliers for backstabbing */
		if (back_stab || 
		    (weap && att != &pstats && on(*att_er, CANBSTAB))) {
		    int mult = 2 + (att->s_lvl-1)/4; /* Normal multiplier */

		    if (mult > 5)
			mult = 5;
		    if (weap && weap->o_type == RELIC && 
                        weap->o_which == MUSTY_DAGGER)
			mult++;
		    damage *= mult;
		}
		if (att == &pstats) {
		    if (cur_weapon && (cur_weapon->o_flags & ISPOISON)) {
			cur_weapon->o_flags &= ~ISPOISON;
			if (save(VS_POISON, def_er, -2))
			    damage += def->s_hpt/4;
			else
			    damage += def->s_hpt/2;
		    }
		    if (back_stab && player.t_ctype == C_ASSASIN)
		        damage = def->s_hpt + 1;
		}
		/* Check for no-damage and division */
		if (on(*def_er, BLOWDIVIDE)) {
		    damage = 0;
		    creat_mons(def_er, def_er->t_index, FALSE);
		    if (cansee(unc(def_er->t_pos))) light(&hero);
		}
		/* check for immunity to metal -- RELICS are always bad */
		if (on(*def_er, NOMETAL) && weap != NULL &&
		    weap->o_type != RELIC && weap->o_flags & ISMETAL) {
		    damage = 0;
		}
		if (weap != NULL && weap->o_type == MISSILE) {
		    if ((def == &pstats && cur_relic[STONEBONES_AMULET]) ||
		        (att == &pstats && on(*def_er, CARRYBAMULET))) {
			damage = 0;
		    }
		}


		def->s_hpt -= max(0, damage);	/* Do the damage */
		did_hit = TRUE;
		vampiric_damage = damage;
		if (def->s_hpt < 0)	/* only want REAL damage inflicted */
		    vampiric_damage += def->s_hpt;
                if (vampiric_damage < 0)
                    vampiric_damage = 0;
		if (att == &pstats && ISWEARING(R_VAMPREGEN) && !hurl) {
		    if ((pstats.s_hpt += vampiric_damage/2) > max_stats.s_hpt)
			pstats.s_hpt = max_stats.s_hpt;
		}
		debug ("hplus=%d dmg=%d", hplus, damage);
	    }
	}
	if ((cp = strchr(cp, '/')) == NULL)
	    break;
	cp++;
    }
    return did_hit;
}

/*
 * prname:
 *	The print name of a combatant
 */

char *
prname(who, upper)
register char *who;
bool upper;
{
    static char tbuf[LINELEN];

    *tbuf = '\0';
    if (who == 0)
	strcpy(tbuf, "you"); 
    else if (on(player, ISBLIND) || strcmp(who, "something") == 0)
	strcpy(tbuf, "something");
    else
    {
	/* If we have a name (starts with a capital), don't use a "the" */
	if (islower(*who)) strcpy(tbuf, "the ");
	strcat(tbuf, who);
    }
    if (upper)
	*tbuf = toupper(*tbuf);
    return tbuf;
}

/*
 * hit:
 *	Print a message to indicate a succesful hit
 */

hit(weapon, see_att, see_def, er, ee, back_stab, thrown, short_msg)
register struct object *weapon;
bool see_att, see_def;
register char *er, *ee;
bool back_stab, thrown, short_msg;
{
    register char *s;
    char          att_name[LINELEN],	/* Name of attacker */
		  def_name[LINELEN]; /* Name of defender */

    /* If we can't see either the attacker or defender, don't say anything */
    if (!see_att && !see_def) return;

    /* What do we call the attacker? */
    strcpy(att_name, see_att ? prname(er, TRUE) : "Something");
    if (er) {	/* A monster is attacking */

	/* If the monster is using a weapon and we can see it, report it */
	if (weapon != NULL && (see_att || thrown)) {
	    strcat(att_name, "'s ");
	    strcat(att_name, weap_name(weapon));
	}
    }

    /* What do we call the defender? */
    strcpy(def_name, see_def ? prname(ee, FALSE) : "something");

    addmsg(att_name);
    if (short_msg) {
	if (back_stab) {
	    if (player.t_ctype == C_ASSASIN)
	        s = (er == 0 ? " assassinate!" : " assassinates!");
	    else
	        s = (er == 0 ? " backstab!" : " backstabs!");
	}
	else
	    s = " hit.";
    }
    else {
	if (back_stab) {
	    if (player.t_ctype == C_ASSASIN)
	        s = (er == 0 ? " have assassinated " : " has assassinated ");
	    else
	        s = (er == 0 ? " have backstabbed " : " has backstabbed ");
	}
	else {
	    switch (rnd(thrown ? 2 : 3))
	    {
		case 0: s = " hit ";
		when 1: s = " injured ";
		when 2: s = " smacked ";
	    }
	}
    }
    if (short_msg) addmsg(s);
    else addmsg("%s%s.", s, def_name);
    endmsg();
}

/*
 * miss:
 *	Print a message to indicate a poor swing
 */

miss(weapon, see_att, see_def, er, ee, thrown, short_msg)
register struct object *weapon;
bool see_att, see_def;
register char *er, *ee;
bool thrown, short_msg;
{
    register char *s;
    char
    	          att_name[LINELEN],	/* Name of attacker */
		  def_name[LINELEN]; /* Name of defender */

    /* If we can't see either the attacker or defender, don't say anything */
    if (!see_att && !see_def) return;

    /* What do we call the attacker? */
    strcpy(att_name, see_att ? prname(er, TRUE) : "Something");
    if (er) {	/* A monster is attacking */

	/* If the monster is using a weapon and we can see it, report it */
	if (weapon != NULL && (see_att || thrown)) {
	    strcat(att_name, "'s ");
	    strcat(att_name, weap_name(weapon));
	}
    }

    /* What do we call the defender? */
    strcpy(def_name, see_def ? prname(ee, FALSE) : "something");

    addmsg(att_name);
    switch (short_msg ? 0 : rnd(thrown ? 3 : 2))
    {
	case 0: s = (er == 0 ? " miss" : " misses");
	when 1: s = (er == 0 ? " don't hit" : " doesn't hit");
	when 2: s = (" whizzes by");
    }
    if (short_msg) addmsg("%s.", s);
    else addmsg("%s %s.", s, def_name);
    endmsg();
}

/*
 * dext_plus:
 *	compute to-hit bonus for dexterity
 */

dext_plus(dexterity)
register int dexterity;
{
	return (dexterity > 10 ? (dexterity-13)/3 : (dexterity-10)/3);
}


/*
 * dext_prot:
 *	compute armor class bonus for dexterity
 */

dext_prot(dexterity)
register int dexterity;
{
    return ((dexterity-10)/2);
}
/*
 * str_plus:
 *	compute bonus/penalties for strength on the "to hit" roll
 */

str_plus(str)
register short str;
{
    return((str-10)/3);
}

/*
 * add_dam:
 *	compute additional damage done for exceptionally high or low strength
 */

add_dam(str)
register short str;
{
    return((str-9)/2);
}

/*
 * hung_dam:
 *	Calculate damage depending on players hungry state
 */
hung_dam()
{
	reg int howmuch;

	switch(hungry_state) {
		case F_SATIATED:
		case F_OKAY:
		case F_HUNGRY:	howmuch = 0;
		when F_WEAK:	howmuch = -1;
		when F_FAINT:	howmuch = -2;
	}
	return howmuch;
}

#ifdef THUNK
/*
 * thunk:
 *	A missile hits a monster
 */

thunk(weap, tp, mname)
register struct object *weap;
register struct thing *tp;	/* Defender */
register char *mname;
{
    char *def_name;	/* Name of defender */

    /* What do we call the defender? */
    if (!cansee(tp->t_pos.y, tp->t_pos.x) || invisible(tp))
	def_name = "something";
    else def_name = prname(mname, FALSE);

    if (weap->o_type == WEAPON)
	msg("The %s hits %s.", weaps[weap->o_which].w_name, def_name);
    else if (weap->o_type == MISSILE)
	msg("The %s hits %s.",ws_magic[weap->o_which].mi_name, def_name);
    else
	msg("You hit %s.", def_name);
}

/*
 * mthunk:
 *	 A missile from a monster hits the player
 */

m_thunk(weap, tp, mname)
register struct object *weap;
register struct thing *tp;
register char *mname;
{
    char *att_name;	/* Name of attacker */

    /* What do we call the attacker? */
    if (!cansee(tp->t_pos.y, tp->t_pos.x) || invisible(tp))
	att_name = "Something";
    else att_name = prname(mname, TRUE);

    if (weap->o_type == WEAPON)
	msg("%s's %s hits you.", att_name, weaps[weap->o_which].w_name);
    else if (weap->o_type == MISSILE)
	msg("%s's %s hits you.", att_name, ws_magic[weap->o_which].mi_name);
    else
	msg("%s hits you.", att_name);
}

/*
 * bounce:
 *	A missile misses a monster
 */

bounce(weap, tp, mname)
register struct object *weap;
register struct thing *tp;	/* Defender */
register char *mname;
{
    char *def_name;	/* Name of defender */

    /* What do we call the defender? */
    if (!cansee(tp->t_pos.y, tp->t_pos.x) || invisible(tp))
	def_name = "something";
    else def_name = prname(mname, FALSE);

    if (weap->o_type == WEAPON)
	msg("The %s misses %s.",weaps[weap->o_which].w_name, def_name);
    else if (weap->o_type == MISSILE)
	msg("The %s misses %s.",ws_magic[weap->o_which].mi_name, def_name);
    else
	msg("You missed %s.", def_name);
}

/*
 * m_bounce:
 *	A missle from a monster misses the player
 */

m_bounce(weap, tp, mname)
register struct object *weap;
register struct thing *tp;
register char *mname;
{
    char *att_name;	/* Name of attacker */

    /* What do we call the attacker? */
    if (!cansee(tp->t_pos.y, tp->t_pos.x) || invisible(tp))
	att_name = "Something";
    else att_name = prname(mname, TRUE);

    if (weap->o_type == WEAPON)
	msg("%s's %s misses you.", att_name, weaps[weap->o_which].w_name);
    else if (weap->o_type == MISSILE)
	msg("%s's %s misses you.", att_name, ws_magic[weap->o_which].mi_name);
    else
	msg("%s misses you.", att_name);
}
#endif


/*
 * is_magic:
 *	Returns true if an object radiates magic
 */

is_magic(obj)
register struct object *obj;
{
    switch (obj->o_type)
    {
	case ARMOR:
	    return obj->o_ac != armors[obj->o_which].a_class;
	when WEAPON:
	    return obj->o_hplus != 0 || obj->o_dplus != 0;
	when POTION:
	case SCROLL:
	case STICK:
	case RING:
	case MM:
	case RELIC:
	    return TRUE;
    }
    return FALSE;
}

/*
 * killed:
 *	Called to put a monster to death
 */

int chance = 0;/* cumulative chance for goodies to loose it */

killed(item, pr, points, treasure)
register struct linked_list *item;
bool pr, points, treasure;
{
    register struct thing *tp, *mp;
    register struct linked_list *pitem, *nexti, *mitem;
    char *monst;

    tp = THINGPTR(item);

    if (pr)
    {
	addmsg(terse ? "Defeated " : "You have defeated ");
	if (on(player, ISBLIND))
	    msg("it.");
	else
	{
	    if (cansee(tp->t_pos.y, tp->t_pos.x) && !invisible(tp))
		monst = monster_name(tp);
	    else {
		if (terse) monst = "something";
		else monst = "thing";
	    }
	    if (!terse)
		addmsg("the ");
	    msg("%s.", monst);
	}
    }

    /* Take care of any residual effects of the monster */
    check_residue(tp);

    /* Make sure that no one is still chasing us */
    for (mitem = mlist; mitem != NULL; mitem = next(mitem)) {
	mp = THINGPTR(mitem);
	if (mp->t_dest == &tp->t_pos) {
	    mp->t_dest = &hero;
	    mp->t_wasshot = FALSE;
	    turn_off(*mp, ISFLEE);	/* Be sure we aren't running away! */
	}
    }

    if (points) {
	if ((off(*tp, ISMEAN) || on(*tp, ISFRIENDLY)) &&
	    (player.t_ctype == C_RANGER || player.t_ctype == C_PALADIN)) {
		if (tp->t_stats.s_exp > pstats.s_exp)
			pstats.s_exp = 0;
		else
			pstats.s_exp -= tp->t_stats.s_exp;
		if (roll(1,100) < chance++)
			changeclass(C_FIGHTER);
		else
			msg("You feel uneasy for a moment");
	}
	else {
		unsigned long test;	/* For overflow check */
		/* 
		 * Do an overflow check before increasing experience 
		 */
		test = pstats.s_exp + tp->t_stats.s_exp;
		if (test > pstats.s_exp) 
			pstats.s_exp = test;
	}

	/*
	 * Do adjustments if he went up a level
	 */
	check_level();
    }

    /*
     * Empty the monsters pack
     */
    pitem = tp->t_pack;

    /*
     * Get rid of the monster.
     */
    mvwaddch(mw, tp->t_pos.y, tp->t_pos.x, ' ');
    mvwaddch(cw, tp->t_pos.y, tp->t_pos.x, tp->t_oldch);
    detach(mlist, item);
    if (on(*tp, AREMANY) && levtype == NORMLEV) /* AREMANYs stick together */
	wake_room(roomin(&tp->t_pos));
    /*
     * empty his pack
     */
    while (pitem != NULL)
    {
	nexti = next(tp->t_pack);
	(OBJPTR(pitem))->o_pos = tp->t_pos;
	detach(tp->t_pack, pitem);
	if (treasure) 
	    fall(pitem, FALSE);
	else 
	    o_discard(pitem);
	pitem = nexti;
    }
    turn_on(*tp,ISDEAD);
    attach(monst_dead,item);
}


/*
 * Returns a pointer to the weapon the monster is wielding corresponding to
 * the given thrown weapon.  If no thrown item is given, try to find any
 * decent weapon.
 */

struct linked_list *
wield_weap(thrown, mp)
struct object *thrown;
struct thing *mp;
{
    int look_for,	/* The projectile weapon we are looking for */
	new_rate,	/* The rating of a prospective weapon */
        cand_rate = -1; /* Rating of current candidate -- higher is better */
    register struct linked_list *pitem, *candidate = NULL;
    register struct object *obj;

    if (thrown != NULL) {	/* Using a projectile weapon */
      switch (thrown->o_which) {
	case BOLT:	look_for = CROSSBOW;	/* Find the crossbow */
	when ARROW:	look_for = BOW;		/* Find the bow */
	when ROCK:	look_for = SLING;	/* find the sling */
	otherwise:	return(NULL);
      }
    }
    else if (off(*mp, ISUNIQUE) && off(*mp, CARRYWEAPON)) return(NULL);

    for (pitem=mp->t_pack; pitem; pitem=next(pitem)) {
	obj = OBJPTR(pitem);

	/*
	 * If we have a thrown weapon, just return the first match
	 * we come to.
	 */
	if (thrown != NULL && obj->o_type == WEAPON && obj->o_which == look_for)
	    return(pitem);

	/* If we have a usable RELIC, return it */
	if (thrown == NULL && obj->o_type == RELIC) {
	    switch (obj->o_which) {
		case MUSTY_DAGGER:
		case YEENOGHU_FLAIL:
		case HRUGGEK_MSTAR:
		case AXE_AKLAD:
		case MING_STAFF:
		case ASMO_ROD:
		case ORCUS_WAND:
		    return(pitem);
	    }
	}

	/* Otherwise if it's a usable weapon, it is a good candidate */
	else if (thrown == NULL && obj->o_type == WEAPON) {
	    switch (obj->o_which) {
		case DAGGER:
		case SPEAR:
		    new_rate = 0;
		when BATTLEAXE:
		    new_rate = 1;
		when MACE:
		    new_rate = 2;
		when SWORD:
		    new_rate = 3;
		when PIKE:
		    new_rate = 4;
		when HALBERD:
		case SPETUM:
		    new_rate = 6;
		when BARDICHE:
		    new_rate = 7;
		when TRIDENT:
		    new_rate = 8;
		when BASWORD:
		    new_rate = 9;
		when TWOSWORD:
		    new_rate = 10;
		otherwise:
		    new_rate = -1;
	    }

	    /* Only switch if this is better than the current candidate */
	    if (new_rate > cand_rate) {
		cand_rate = new_rate;
		candidate = pitem;
	    }
	}
    }

    return(candidate);
}
explode(tp)
register struct thing *tp;
{

    register int x,y, damage;
    struct linked_list *item;
    struct thing *th;

    /*
     * check to see if it got the hero
     */
     if (off(player, ISINWALL) &&
	 DISTANCE(hero.x, hero.y, tp->t_pos.x, tp->t_pos.y) <= 25) {
	msg("The explosion hits you");
	damage = roll(6,6);
	if (save(VS_WAND, &player, 0))
	    damage /= 2;
	pstats.s_hpt -= damage;
    }

    /*
     * now check for monsters in vicinity
     */
     for (x = tp->t_pos.x-5; x<=tp->t_pos.x+5; x++) {
	 if (x < 0 || x > cols - 1) 
	     continue;
	 for (y = tp->t_pos.y-5; y<=tp->t_pos.y+5; y++) {
	    if (y < 1 || y > lines - 3)
		continue;
	    if (isalpha(mvwinch(mw, y, x))) {
		if ((item = find_mons(y, x)) != NULL) {
		    th = THINGPTR(item);
		    if (th == tp || /* don't count gas spore */
			on(*th, ISINWALL)) /* Don't count monsters in wall */
			continue;
		    damage = roll(6, 6);
		    if (save(VS_WAND, th, 0))
			damage /= 2;
		    runto(th, &hero);
		    if ((th->t_stats.s_hpt -= damage) <= 0) {
			msg("The explosion kills %s", 
			    prname(monster_name(th), FALSE));
			killed(item, FALSE, FALSE, TRUE);
		    }
		}
	    }
	}
    }
}

/*
 * skirmish:
 *	Called when one monster attacks another monster.
 */

skirmish(attacker, mp, weap, thrown)
register struct thing *attacker;
register coord *mp;
struct object *weap;
bool thrown;
{
    register struct thing *defender;
    register struct linked_list *item;
    register bool did_hit = TRUE, see_att, see_def;
    char attname[LINELEN+1], defname[LINELEN+1];
    struct object *wielded;	/* The wielded weapon */
    struct linked_list *get_wield;	/* Linked list header for wielded */

    /*
     * Find the monster we want to fight
     */
    if ((item = find_mons(mp->y, mp->x)) == NULL) {
	return(FALSE); /* must have killed him already */
    }
    defender = THINGPTR(item);

    /* Can the player see either of the fighters? */
    see_att = (cansee(unc(attacker->t_pos)) &&
	       (off(*attacker, ISINVIS)     || on(player, CANSEE)) &&
	       (off(*attacker, ISSHADOW)    || on(player, CANSEE)));
    see_def = (cansee(unc(defender->t_pos)) &&
	       (off(*defender, ISINVIS)     || on(player, CANSEE)) &&
	       (off(*defender, ISSHADOW)    || on(player, CANSEE)));

    /*
     * Since we are fighting, things are not quiet so no healing takes
     * place.  The -1 also tells us that we are in a fight.
     */
    attacker->t_quiet = -1;
    defender->t_quiet = -1;

    if (see_att) strcpy(attname, monster_name(attacker));
    else strcpy(attname, "something");

    if (see_def) strcpy(defname, monster_name(defender));
    else strcpy(defname, "something");

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

    if (on(*defender, ISSTONE)) {
	killed(item, FALSE, FALSE, FALSE);
	if (see_def)
	    msg("%s shatters into a million pieces!", prname(defname, TRUE));
	return (TRUE);
    }

    /*
     * Let him know it was really a mimic (if it was one).
     */
    if (see_def && on(*defender, ISDISGUISE) &&
	(defender->t_type != defender->t_disguise)) {
	msg("Wait! There's a %s!", defname);
	turn_off(*defender, ISDISGUISE);
	did_hit = thrown;
    }

    if (see_def && on(*defender, CANSURPRISE) && !ISWEARING(R_ALERT)) {
	msg("Wait! There's a %s!", defname);
	turn_off(*defender, CANSURPRISE);
	did_hit = thrown;
    }

    if (did_hit) {

	did_hit = FALSE;

	/*
	 * 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.
	 */
	get_wield = wield_weap(weap, attacker);
	if (get_wield) wielded = OBJPTR(get_wield);
	else wielded = NULL;

#ifdef DOBLINK
	/*
	 * For now Blink Dogs will not blink away from monsters.  We
	 * have to fix can_blink so it isn't dependant on the player
	 * before we can add it.
	 */
	if (!can_blink(defender) &&
#endif
	if (((weap && weap->o_type == RELIC) ||
	     ((off(*defender, MAGICHIT) ||
	       attacker->t_stats.s_lvl > 4 ||
	       (weap && (weap->o_hplus > 0 || weap->o_dplus > 0))) &&
	      (off(*defender, BMAGICHIT) ||
	       attacker->t_stats.s_lvl > 6 ||
	       (weap && (weap->o_hplus > 1 || weap->o_dplus > 1))) &&
	      (off(*defender, CMAGICHIT) ||
	       attacker->t_stats.s_lvl > 8 ||
	       (weap && (weap->o_hplus > 2 || weap->o_dplus > 2)))))
	    && roll_em(attacker, defender, weap, thrown, wielded, FALSE))
	{
	    did_hit = TRUE;

	    /* Should we start to chase this creature? */
	    if (attacker->t_index != defender->t_index	&&
		(off(*defender, ISRUN) || rnd(100) < 50)) {
		/*
		 * If we're intelligent enough to realize that this
		 * is a friendly monster, we will attack the hero instead.
		 */
		if (on(*attacker, ISFRIENDLY) &&
		    roll(3,6) < defender->t_stats.s_intel) {
		    runto(defender, &hero);
		    debug("%s attacking %s's hero", defname, attname);
		}

		/* Otherwise, let's chase the monster */
		else {
		    runto(defender, &attacker->t_pos);
		    debug("%s now attacking %s", defname, attname);
		}
	    }
	    else if (off(*defender, ISRUN)) runto(defender, &hero);

	    /* Let the defender know that the attacker has missiles! */
	    if ((defender->t_dest == &attacker->t_pos) && thrown)
		defender->t_wasshot = TRUE;

	    if (on(*defender, NOMETAL) && weap != NULL &&
		weap->o_type != RELIC && weap->o_flags & ISMETAL) {
		if (see_def && see_att)
		    msg("The %s passes right through %s!",
			weaps[weap->o_which].w_name, prname(defname, FALSE));
	    }
	    else {
		hit(weap, see_att, see_def,
		    attname, defname, FALSE, thrown, FALSE);
	    }

	    /* See if there are any special effects */
	    if (effect(attacker, defender,
		       weap, thrown, see_att, see_def) != 0) {
		killed(item, FALSE, FALSE, TRUE);
		if (see_def) msg("%s dies.", prname(defname, TRUE));
		else msg("You hear a blood-curdling scream.");
	    }

	    /* 
	     * Merchants just disappear if hit 
	     */
	    else if (on(*defender, CANSELL)) {
		if (see_def)
		    msg("%s disappears with his wares in a flash.",
			    prname(defname, TRUE));
		killed(item, FALSE, FALSE, FALSE);
	    }

	    else if (defender->t_stats.s_hpt <= 0) {
		killed(item, FALSE, FALSE, TRUE);
		if (see_def) msg("%s dies.", prname(defname, TRUE));
		else msg("You hear a blood-curdling scream.");
	    }

	    else {
		/* Did we disrupt a spell?				*/
		/* Don't turn on WASDISRUPTED since player didn't do it */
		if (defender->t_action == A_SUMMON ||
		    defender->t_action == A_MISSILE) {
		    /* Just make the old fellow start over again */
		    defender->t_action = A_NIL;
		    defender->t_no_move = movement(defender);
		    defender->t_using = NULL;

		    if (see_def)
			msg("%s was disrupted.", prname(defname, TRUE));
		}

#ifdef FLEEMONST
		/*
		 * If the monster is fairly intelligent and about to die, it
		 * may turn tail and run.  WE STILL NEED TO FIGURE OUT HOW
		 * WE WANT THIS TO WORK.
		 */
		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);

		    /* It is okay to turn tail */
		    tp->t_oldpos = tp->t_pos;
		}
#endif
	    }
	}
	else {
	    /* If the thing was trying to surprise, no good */
	    if (on(*attacker, CANSURPRISE)) {
		/* If we can't see it, it keeps surprise (from us) */
	        if (see_att) turn_off(*attacker, CANSURPRISE);
	    }

	    miss(weap, see_att, see_def, attname, defname, thrown, FALSE);
	}
    }
    return did_hit;
}