view arogue7/fight.c @ 265:7fcb2f9f57e6

Mention UltraRogue in the top-level README.
author John "Elwin" Edwards
date Sun, 19 Feb 2017 19:54:17 -0500
parents e1cd27c5464f
children e52a8a7ad4c5
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 <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
 */