/*
 * 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 <stdlib.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, bool see_att, bool see_def, char *er, char *ee, 
    bool back_stab, bool thrown, bool short_msg);
void miss(struct object *weapon, bool see_att, bool see_def, char *er, char *ee,
     bool thrown, bool short_msg);
int add_dam(short str);
int hung_dam(void);

#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
 */
bool
player_can_hit(struct thing *tp, 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.
 */

bool
fight(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
 */

bool
attack(struct thing *mp, 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
 */

bool
swing(short class, int at_lvl, int op_arm, int 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
 */

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) {
	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, NULL, 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(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
 */

void
hit(struct object *weapon, bool see_att, bool see_def, char *er, char *ee, 
    bool back_stab, bool thrown, bool 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
 */

void
miss(struct object *weapon, bool see_att, bool see_def, char *er, char *ee, 
     bool thrown, bool 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
 */

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


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

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

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

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

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

/*
 * hung_dam:
 *	Calculate damage depending on players hungry state
 */
int
hung_dam(void)
{
	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
 *	tp: defender
 */

void
thunk(struct object *weap, struct thing *tp, 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
 */

void
m_thunk(struct object *weap, struct thing *tp, 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
 *	tp: defender
 */

void
bounce(struct object *weap, struct thing *tp, 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
 */

void
m_bounce(struct object *weap, struct thing *tp, 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
 */

bool
is_magic(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 */

void
killed(struct linked_list *item, bool pr, bool points, bool 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(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);
}

void
explode(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.
 */

bool
skirmish(struct thing *attacker, 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;
}
