daleken/
daleken/data/notes/
daleken/data/player/
daleken/data/system/poses/
daleken/doc/Homepage/images/
daleken/log/
/*___________________________________________________________________________*
   )()(			  DalekenMUD 1.12 (C) 2000			)()(
   `]['		       by Martin Thomson, Lee Brooks,			`]['
    ||		       Ken Herbert and David Jacques			 ||
    || ----------------------------------------------------------------- ||
    || Envy Diku Mud improvements copyright (C) 1994 by Michael Quan,	 ||
    || David Love, Guilherme 'Willie' Arnold, and Mitchell Tse.		 ||
    || Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael	 ||
    || Chastain, Michael Quan, and Mitchell Tse.			 ||
    || Original Diku Mud copyright (C) 1990, 1991			 ||
    || by Sebastian Hammer, Michael Seifert, Hans Henrik St{rfeldt,	 ||
    || Tom Madsen, and Katja Nyboe.					 ||
    || ----------------------------------------------------------------- ||
    || Any use of this software must follow the licenses of the		 ||
    || creators.  Much time and thought has gone into this software and	 ||
    || you are benefitting. We hope that you share your changes too.	 ||
    || What goes around, comes around.					 ||
    || ----------------------------------------------------------------- ||
    ||                             fight.c                               ||
    || Combat, combat skills and death code.                             ||
 *_/<>\_________________________________________________________________/<>\_*/


#include <math.h>
#include "mud.h"
#include "event.h"



/*
 * Local functions.
 */
bool check_shield_block	args( ( CHAR_DATA *ch, CHAR_DATA *victim ) );
bool check_blink	args( ( CHAR_DATA *ch, CHAR_DATA *victim ) );
bool check_dodge	args( ( CHAR_DATA *ch, CHAR_DATA *victim ) );
bool check_parry	args( ( CHAR_DATA *ch, CHAR_DATA *victim ) );
bool check_stumble	args( ( CHAR_DATA *ch, CHAR_DATA *victim ) );
bool check_familiar	args( ( CHAR_DATA *ch, CHAR_DATA *victim, int dam ) );
void dam_message	args( ( CHAR_DATA *ch, CHAR_DATA *victim, int dam,
				int dt, int wpn ) );
void group_gain		args( ( CHAR_DATA *ch, CHAR_DATA *victim ) );
bool is_wielding_poisoned args( ( CHAR_DATA *ch, int wpn ) );
void make_corpse	args( ( CHAR_DATA *ch ) );
void one_hit		args( ( CHAR_DATA *ch, CHAR_DATA *victim, int dt,
				int wpn ) );
void raw_kill		args( ( CHAR_DATA *ch, CHAR_DATA *victim ) );
void set_fighting	args( ( CHAR_DATA *ch, CHAR_DATA *victim ) );
bool disarm		args( ( CHAR_DATA *ch, CHAR_DATA *victim ) );
void trip		args( ( CHAR_DATA *ch, CHAR_DATA *victim ) );

void use_magical_item	args( ( CHAR_DATA *ch ) );
void strangle_hit	args( ( CHAR_DATA *ch ) );
void strangle_struggle	args( ( CHAR_DATA *ch ) );
void check_critical	args( ( CHAR_DATA *ch, CHAR_DATA *victim, int dam,
				int wpn, int dt ) );
void remove_part	args( ( CHAR_DATA *ch, CHAR_DATA *victim, int part ) );
void damage_eq		args( ( CHAR_DATA *ch, CHAR_DATA *victim ) );
void start_fights	args( ( CHAR_DATA *ch ) );
void attack		args( ( CHAR_DATA *ch, int dt, int number ) );
void barehand_hit	args( ( CHAR_DATA *ch, CHAR_DATA *victim ) );
void flashing_blades	args( ( CHAR_DATA *ch, CHAR_DATA *victim,
				int dt, int wpn ) );
bool get_juggle_attack	args( ( CHAR_DATA *ch, int num ) );
void dirty_fighting	args( ( CHAR_DATA *ch ) );
void end_battle_check	args( ( CHAR_DATA *victim ) );


#define ATTACK_COUNT 10

const int attack_lag[ATTACK_COUNT] =
{ 0, 2, 12, 18, 25, 32, 43, 55, 75, 100 };

const struct limb_loss part_loss_table[] =
{
    { 1, BODY_PART_HEAD|BODY_PART_EYES|BODY_PART_EAR_L|BODY_PART_EAR_R|
      BODY_PART_NOSE|BODY_PART_HORNS, 50,
      "&yYour head is chopped off!",
      "&y$n's head goes flying with an arc of blood!" },
    { 3, BODY_PART_EYES, 51,
      "&yYour eyes are gouged out.",
      "&y$n's eyes drop out on the ground!" },
    { 13, BODY_PART_ARM_L | BODY_PART_HAND_L | BODY_PART_FINGERS_L, 52,
      "&yYour left arm is chopped off!",
      "&y$n's left arm goes flying!" },
    { 23, BODY_PART_ARM_R | BODY_PART_HAND_R | BODY_PART_FINGERS_R, 52,
      "&yYour right arm is chopped off!",
      "&y$n's right arm goes flying!" },
    { 29, BODY_PART_HAND_L | BODY_PART_FINGERS_L, 53,
      "&yYour left hand is chopped off!",
      "&y$n's left hand goes flying!" },
    { 35, BODY_PART_HAND_R | BODY_PART_FINGERS_R, 53,
      "&yYour right hand is chopped off!",
      "&y$n's right hand goes flying!" },
    { 38, BODY_PART_FINGERS_L, 54,
      "&yYour left fingers are chopped off!",
      "&y$n's left fingers drop to the ground!" },
    { 41, BODY_PART_FINGERS_R, 54,
      "&yYour right fingers are chopped off!",
      "&y$n's right fingers drop to the ground!" },
    { 51, BODY_PART_LEG_L | BODY_PART_FOOT_L, 55,
      "&yYour left leg is chopped off!",
      "&y$n's left leg goes flying!" },
    { 61, BODY_PART_LEG_R | BODY_PART_FOOT_R, 55,
      "&yYour right leg is chopped off!",
      "&y$n's right leg goes flying!" },
    { 67, BODY_PART_FOOT_L, 56,
      "&yYour left foot is chopped off!",
      "&y$n's left foot goes flying!" },
    { 73, BODY_PART_FOOT_R, 56,
      "&yYour right foot is chopped off!",
      "&y$n's right foot goes flying!" },
    { 75, BODY_PART_TAIL, 57,
      "&yYour tail is severed!",
      "&y$n's tail is severed!" },
    { 77, BODY_PART_HORNS, 58,
      "&yYour horns are shorn from your head!",
      "&y$n's horns are shorn from $s head!" },
    { 79, BODY_PART_WINGS, 59,
      "&yYour wings are chopped off!",
      "&y$n's wings flutter to the ground!" },
    { 81, BODY_PART_LUNGS, 60,
      "&yYour lungs are torn out!",
      "&y$n's lungs are torn out!" },
    { 83, BODY_PART_GILLS, 61,
      "&yYour gills are ripped off!",
      "&y$n's gills are ripped off!" },
    { 86, BODY_PART_EAR_L, 62,
      "&yYour left ear drops to the ground.",
      "&y$n's left ear drops to the ground." },
    { 89, BODY_PART_EAR_R, 62,
      "&yYour right ear drops to the ground.",
      "&y$n's right ear drops to the ground." },
    { 91, BODY_PART_NOSE, 63,
      "&yYour nose drops to the ground.",
      "&y$n's nose drops to the ground." },
    { 0, 0, 1, NULL, NULL }
};


/*
 * Called from violence_update
 * starts everyone fighting each other
 * this is the most expensive part of violence update.
 */
void start_fights( CHAR_DATA *ch )
{
    CHAR_DATA *victim;
    CHAR_DATA *rch;
    bool mobfighting = FALSE;

    if( IS_AFFECTED( ch, AFF_BLIND )
	|| ( IS_NPC( ch ) && ch->pIndexData->pShop )
	|| !IS_AWAKE( ch ) )
	return;

    if( ( victim = ch->fighting ) )
    {
	if( victim->deleted || ch->in_room != victim->in_room )
	    stop_fighting( ch, FALSE );
	/* switch to a charmie's master if there is one */
	else if( IS_AFFECTED( victim, AFF_CHARM )
		 && IS_NPC( victim )
		 && !xIS_SET( victim->act, ACT_MERCENARY )
		 && victim->master && !victim->master->deleted
		 && ch->in_room == victim->master->in_room
		 && number_bits( 3 ) > 3 )
	{
	    stop_fighting( ch, FALSE );
	    set_fighting( ch, victim->master );
	}
	return;
    }

    /*
     * Ok. So ch is not fighting anyone.
     * Is there a fight going on?
     * will we join in?
     */
    for( rch = ch->in_room->people; rch; rch = rch->next_in_room )
    {
	if( rch->deleted
	    || !IS_AWAKE( rch )
	    || !( victim = rch->fighting ) )
	    continue;

	/* a player or mercenary (ch) will assist
	 * players or charmies (rch) in the same group
	 * that are fighting NPCs
	 */
	if( ( !IS_NPC( ch )
	      || ( IS_AFFECTED( ch, AFF_CHARM )
		   && xIS_SET( ch->act, ACT_MERCENARY ) ) )
	    && ( !IS_NPC( rch ) || IS_AFFECTED( rch, AFF_CHARM ) )
	    && is_same_group( ch, rch )
	    && IS_NPC( victim ) )
	    break;

	if( IS_NPC( ch ) && IS_NPC( rch )
	    && !IS_NPC( victim ) )
	{
	    mobfighting = TRUE;
	    break;
	}
    }

    if( !victim || !rch )
	return;
    /*
     * Now that someone is fighting, consider fighting another pc
     * or not at all.
     */
    if( mobfighting )
    {
	CHAR_DATA *vch;
	int number;

	number = 0;
	for( vch = ch->in_room->people; vch; vch = vch->next_in_room )
	{
	    if( can_see( ch, vch )
		&& is_same_group( vch, victim )
		&& ( number_range( 0, number ) == 0
		     || IS_AFFECTED( vch, AFF_TAUNT ) ) )
	    {
		victim = vch;
		number++;
	    }
	}

	if( !xIS_SET( ch->act, ACT_ASSIST )
	    && ( ( rch->pIndexData != ch->pIndexData )
		 || ( IS_GOOD( ch ) && IS_GOOD( victim ) )
		 || ( !IS_EVIL( ch ) && !IS_EVIL( victim )
		      && number_bits( 2 ) != 0 )
		 || abs( victim->level - ch->level ) > 4 ) )
	    return;
    }
    set_fighting( ch, victim );
    return;
}


/*
 * Attack.
 * Called from update_violence and multi_hit
 * Does one attack depending on number.
 */
void attack( CHAR_DATA *ch, int dt, int number )
{
    CHAR_DATA *victim;
    int wield = WEAR_WIELD_R;

    if( !( victim = ch->fighting ) || victim->deleted )
	return;

    if( get_eq_char( ch, WEAR_WIELD_DOUBLE ) )
	wield = WEAR_WIELD_DOUBLE;
    else if( !get_eq_char( ch, WEAR_WIELD_R )
	     && get_eq_char( ch, WEAR_WIELD_L ) )
	wield = WEAR_WIELD_L;

    if( number > 0 && ( dt == gsn_backstab || dt == gsn_circle
			|| dt == gsn_slit_throat || dt == gsn_impale
			|| dt == gsn_atemi || dt == gsn_charge ) )
	return;

    if( wield == WEAR_WIELD_DOUBLE && number > 0
	&& number != 2 && number != 9 )
	return;

    if( number != -1 && ( !IS_NPC( ch )
			  && ch->pcdata->pc_bits >= PC_BIT_STRANGLE ) )
	return;

    /*
     * The action starts here, special considerations
     * are taken into account like dual and so forth
     */
    switch( number )
    {
    case -1:
	if( !IS_NPC( ch ) )
	{
	    if( ch->pcdata->pc_bits >= PC_BIT_STRANGLE )
		strangle_hit( ch );
	    else if( get_success( ch, gsn_dirty_fighting, 50 ) )
		dirty_fighting( ch );
	}
	return;

    case 0:
	one_hit( ch, victim, dt, wield );
	return;

    case 1:
	if( IS_AFFECTED( ch, AFF_HASTE ) && number_bits( 5 ) < 25 )
	    one_hit( ch, victim, dt, wield );
	return;

    case 2:
	if( IS_NPC_CLASS( ch ) ? number_percent( ) < ch->level
	    : get_success( ch, gsn_second_attack, 70 ) )
	    one_hit( ch, victim, dt, wield );
	return;

    case 3:
	if( dt == TYPE_UNDEFINED
	    && IS_SET( ch->body_parts, BODY_PART_TAIL )
	    && number_bits( 2 ) == 0 )
	    one_hit( ch, victim, gsn_race_tail, WEAR_WIELD_R );
	return;

    case 4:
	if( wield == WEAR_WIELD_DOUBLE )
	{
	    if( IS_NPC( ch ) )
	    {
		if( number_percent( ) < UMIN( 50, ch->level / 2 ) )
		    one_hit( ch, victim, dt, wield );
		return;
	    }
	    if( ch->pcdata->learned[gsn_second_attack] * 3 / 4 > number_bits( 7 )
		&& ch->pcdata->learned[gsn_third_attack] / 2 > number_bits( 7 ) )
		one_hit( ch, victim, dt, wield );
	}
	else if( IS_NPC_CLASS( ch ) ? number_bits( 5 ) < ch->level
	    : get_success( ch, gsn_third_attack, 50 ) )
	    one_hit( ch, victim, dt, wield );
	return;

    case 5:
	if( get_juggle_attack( ch, 1 )
	    || IS_NPC_CLASS( ch ) ? number_bits( 6 ) < ch->level
	       : get_success( ch, gsn_fourth_attack, 30 ) )
	    one_hit( ch, victim, dt, wield );
	return;

    case 6:
	if( get_juggle_attack( ch, 2 )
	    || IS_NPC_CLASS( ch ) ? number_bits( 7 ) < ( ch->level - 25	)
	       : get_success( ch, gsn_fifth_attack, 25 ) )
	    one_hit( ch, victim, dt, wield );
	return;

    case 7:
	if( get_juggle_attack( ch, 3 )
	    || ( IS_NPC_CLASS( ch )
		 && number_bits( 8 ) < ( ch->level - 100 ) / 2 ) )
	    one_hit( ch, victim, dt, wield );
	return;

    case 8:
	if( IS_NPC( ch ) && number_bits( 10 ) < ch->level )
	    one_hit( ch, victim, dt, wield );
	return;

    case 9:
	if( IS_NPC( ch ) )
	    one_hit( ch, victim, dt, wield );
	return;

    default:
	bug( "attack: Unknown attack action %d from %s.", number, ch->name );
	return;
    }
    return;
}


/*
 * Update all the fighting in a single room.
 * This has the advantage of a very short list when working out initiatives
 * so inserts are extremely simple.
 */
void room_violence_update( ROOM_INDEX_DATA *room )
{
    CHAR_DATA *ch;
    CHAR_DATA *list = NULL;
    CHAR_DATA *threads[ATTACK_COUNT];
    CHAR_DATA *extra_threads[ATTACK_COUNT];
    int i, count;
    int min = 100000, max = -100000;

    for( ch = room->people; ch; ch = ch->next_in_room )
    {
	if( ch->deleted )
	    continue;

	start_fights( ch );
	if( !ch->fighting )
	    continue;

	ch->temp = get_speed( ch ) + number_range( 1, 10 );

	/* get minimum/maximum values so we can fix it */
	if( ch->temp > max )
	    max = ch->temp;
	if( ch->temp < min )
	    min = ch->temp;

	if( !list || ch->temp > list->temp )
	{
	    ch->next_initiative = list;
	    list = ch;
	}
	else
	{
	    CHAR_DATA *tmp;

	    for( tmp = list; tmp && tmp->next_initiative;
		 tmp = tmp->next_initiative )
		if( ch->temp > tmp->next_initiative->temp )
		    break;

	    if( !tmp )
	    {
		bug( "room_violence_update: can't insert %s",
			   ch->name );
		continue;
	    }

	    ch->next_initiative = tmp->next_initiative;
	    tmp->next_initiative = ch;
	}
    }
    if( !list )
	return;

    /* special attacks as well as a little twiddling
     * it would be nice to have all the values in
     * a restricted range...
     */
    max = UMAX( max, min + 50 );
    for( ch = list; ch; ch = ch->next_initiative )
    {
	ch->temp = ( ch->temp - min ) * 100 / ( max - min );
	if( !ch->deleted && ch->fighting )
	    attack( ch, TYPE_UNDEFINED, -1 );
    }

    /* there is a thread for each value in attack() */
    for( i = 0; i < ATTACK_COUNT; ++i )
    {
	threads[i] = list;
	extra_threads[i] = list;
    }

    for( count = 0; count < 125; ++count )
    {
	for( i = 0; i < ATTACK_COUNT; ++i )
	{
	    if( threads[i] )
	    {
		if( threads[i]->deleted || !threads[i]->fighting )
		    threads[i] = threads[i]->next_initiative;
		else if( threads[i]->temp - attack_lag[i] == list->temp - count )
		{
		    attack( threads[i], TYPE_UNDEFINED, i );
		    threads[i] = threads[i]->next_initiative;
		}
	    }

	    /* A bonus to any with high initiative */
	    if( extra_threads[i] )
	    {
		if( extra_threads[i]->deleted
		    || !extra_threads[i]->fighting )
		    extra_threads[i] = extra_threads[i]->next_initiative;
		else if( extra_threads[i]->temp - 50 - 2 * attack_lag[i]
			 == list->temp - count )
		{
		    if( number_bits( 1 ) )
			attack( extra_threads[i], TYPE_UNDEFINED, i );
		    extra_threads[i] = extra_threads[i]->next_initiative;
		}
	    }
	}
    }

    /* clean up */
    for( ch = list; ch; ch = ch->next_initiative )
	if( !ch->deleted )
	    ch->extra_bits &= ~CH_EX_RESET;

    create_room_event( room, evn_room_violence, PULSE_VIOLENCE );
}


/*
 * Do one group of attacks.
 * Now passed on to attack( ), only used to set fighting
 */
void multi_hit( CHAR_DATA *ch, CHAR_DATA *victim, int dt )
{
    int i;

    /*
     * Set the fighting fields now.
     */
    if( victim->position > POS_STUNNED )
    {
	if( !victim->fighting )
	    set_fighting( victim, ch );
	if( victim->position != POS_SMASHED
	    && victim->position != POS_SLEEPING
	    && victim->position != POS_GETTING_UP )
	    victim->position = POS_FIGHTING;
	if( !ch->fighting )
	    set_fighting( ch, victim );
    }

    for( i = 0; i < ATTACK_COUNT; i++ )
	attack( ch, dt, i );
    if( !IS_NPC( victim ) && dt != gsn_retaliate
	&& get_success( victim, gsn_retaliate, 40 ) )
	multi_hit( victim, ch, gsn_retaliate );
    return;
}


/*
 * Hit one guy once.
 * one_hit() modified for new damage/combat system.
 * --Symposium (6/1998)
 */
void one_hit( CHAR_DATA *ch, CHAR_DATA *victim, int dt, int wpn )
{
    OBJ_DATA *wield = NULL;
    int combat_no;
    int dam, basic_dam;
    int save_hit;
    bool offwield = FALSE;

    /*
     * Can't beat a dead char!
     * Guard against weird room-leavings.
     */
    if( victim->position == POS_DEAD || ch->in_room != victim->in_room )
    {
	bug( "one_hit: ch %s not with victim %s, or victim POS_DEAD",
		 ch->name, victim->name );
	return;
    }

    if( !IS_NPC( victim ) && victim->pcdata->pc_bits >= PC_BIT_STRANGLE
	&& victim->fighting == ch )
    {
	strangle_struggle( ch );
	return;
    }

    /* Juggle those weapons!
     * Make sure this is only for regular hits, not specials.
     * Otherwise people could backstab with the wrong weapon :)
     */
    if( dt == TYPE_UNDEFINED )
    {
	juggle_shuffle( ch );
	if( ch->deleted || !ch->fighting || ch->position <= POS_STUNNED )
	    return;
    }

    /*
     * Figure out the type of damage message.
     */
    if( wpn != WEAR_NONE )
	wield = get_eq_char( ch, wpn );

    if( dt == TYPE_UNDEFINED )
    {
	if( wpn == WEAR_WIELD_R )
	    offwield = TRUE;
	dt = TYPE_HIT;
	if( wield && wield->item_type == ITEM_WEAPON )
	    dt += wield->value[3];
    }

    dam = GET_AC( victim ) - 100;
    dam += ( get_size( ch ) - get_size( victim ) ) / 2;
    if( !can_see( victim, ch ) )
	dam += 50;

    if( ch->class == CLASS_NONE )
	combat_no = 400 + ch->level / 5;
    else
	combat_no = 375 + class_table[ch->class].combat_no;
    dam = dam * 100 + ch->level * combat_no;
    dam = UMIN( -1, dam / 100 );
    dam += number_range( 0, get_hitroll( ch ) * 6 + 5 );
    dam += number_range( 0, ( ch->level - 3 ) * 4 + 10 );
    if( dam > ch->level )
	dam = UMIN( dam, ( ch->level + get_hitroll( ch ) ) * 12 );

    /* MISSED 'IM
     * (1/32 chance of always hit and always miss)
     */
    if( ( dam <= 0 || number_bits( 5 ) == 0 )
	&& !IS_AFFECTED( ch, AFF_NEVERMISS )
	&& number_bits( 5 ) )
    {
	damage( ch, victim, 0, dt, wpn );
	return;
    }
    else if( dam < 0 )
	dam = 1;

    /* if you hit you get the weapon's base damage added */
    if( IS_NPC( ch ) )
    {
	basic_dam = number_range( ch->level / 3, ch->level ) + 1;
	if( wield )
	{
	    if( wield->wear_loc == WEAR_WIELD_DOUBLE )
		basic_dam *= 3;
	    else
		basic_dam += basic_dam * 2 / 3;
	}
    }
    else if( wield && dt != gsn_race_tail )	/* weaponed PC */
    {
	basic_dam = number_range( wield->value[1], wield->value[2] );
	if( basic_dam > 100000 )
	{
	    bug( "One_hit basic_dam range > 100000 from %d to %d",
		     wield->value[1], wield->value[2] );
	}
	if( IS_SET( wield->extra_flags, ITEM_HORNED )
	    && number_bits( 5 ) == 0 )
	    basic_dam *= 2;
	if( wield->wear_loc == WEAR_WIELD_DOUBLE )
	{
	    if( dt == gsn_backstab || dt == gsn_atemi )
		basic_dam += basic_dam
		    * get_success( ch, gsn_two_handed, 200 ) / 50;
	    else
		basic_dam += basic_dam
		    * get_success( ch, gsn_two_handed, 200 ) / 30;
	}
	if( wield->required_skill > 0 )
	{
	    if( get_success( ch, gsn_weapon_skill, 100 ) )
		basic_dam = basic_dam / 2 + basic_dam
		    * get_success( ch, wield->required_skill, 200 ) / 180;
	    else
		basic_dam = basic_dam
		    * get_success( ch, wield->required_skill, 200 ) / 90;
	}
	if( ch->pcdata->learned[gsn_weapon_skill] > 0 )
	    basic_dam += basic_dam
		* get_success( ch, gsn_weapon_skill, 250 ) / 150;
    }
    else	/* no weapon basic damage */
    {
	basic_dam = get_size( ch ) / 10 + 1;
	if( dt != gsn_race_tail )	/* barehand */
	{
	    int i;

	    basic_dam /= 2;
	    if( ( i = get_success( ch, gsn_barehand, 100 ) ) )
	    {
		basic_dam += i * ch->level / ( 400 - get_curr_dex( ch ) * 3 );
	    }
	    basic_dam = number_range( basic_dam, basic_dam * 2 );
	    if( dt == gsn_smash )
	    {
		basic_dam += get_curr_str( ch ) * ch->level / 30;
	    }
	    else if( dt == TYPE_HIT && number_bits( 5 ) == 0
		     && get_success( ch, gsn_golden_touch, 60 ) )
	    {
		dt = gsn_golden_touch;
		dam = dam * 5 / 2;
	    }
	}
	else
	    basic_dam = number_range( basic_dam / 2, basic_dam );
    }

    /*
     * An idea I had, maybe this will work if I put some thought into it.
     dam = dam * ( 75 + get_damroll( ch ) );
     * --Symposium
     */
    dam = dam / 10 + basic_dam + get_damroll( ch );

    /*
     * Now the other bonuses.
     */
    if( wield && IS_SET( wield->extra_flags, ITEM_CHARGED ) )
    {
	if( ch->level < L_SEN )
	    REMOVE_BIT( wield->extra_flags, ITEM_CHARGED );
	dam += dam * get_curr_int( ch ) / 12;
    }
    if( wield && wpn == WEAR_WIELD_L && get_eq_char( ch, WEAR_WIELD_R ) )
	dam /= 2;
    if( !IS_NPC( ch ) && ch->pcdata->learned[gsn_enhanced_damage] > 0 )
	dam += dam * get_success( ch, gsn_enhanced_damage, 250 ) / 150;
    if( !wield && !str_cmp( race_table[ch->race].name, "Vampire" ) )
	dam += dam / 2;
    if( IS_AFFECTED( ch, AFF_BLEEDING ) )
	dam -= dam / 3;

    if( dt == gsn_whirlwind )
	dam += dam / 2;
    else if( dt == gsn_headbutt )
    {
	OBJ_DATA *helmet;

	helmet = get_eq_char( ch, WEAR_HEAD );
	if( ch != victim )
	    dam *= 2;
	if( ( helmet && IS_SET( helmet->extra_flags, ITEM_HORNED ) )
	    || ( !helmet && IS_SET( ch->body_parts, BODY_PART_HORNS ) ) )
	    dam += dam / 2;
    }
    else if( dt == gsn_stomp )
    {
	dam += get_damroll( ch );
	dam += get_size( ch ) / 2;
	dam -= get_size( victim ) / 3;
	dam = dam * ( get_curr_str( ch ) * 5 + get_curr_dex( ch ) * 2 ) / 120;
	if( !str_cmp( class_table[ch->class].who_name, "cru" ) )
	{
	    dam *= 3;
	    dam /= 2;
	}
    }
    else if( dt == gsn_impale )
    {
	dam *= get_curr_str( ch );
	dam /= 6;
	if( !number_range( 0, UMAX( 70, 200 - ch->pcdata->learned[gsn_impale] ) ) )
	{
	    send_to_char( "You have struck a vital organ!\n\r", ch );
	    act( "$n has struck a vital organ!", ch, NULL, victim, TO_ROOM );
	    dam *= 4;
	}
    }
    else if( dt == gsn_backstab || dt == gsn_atemi )
    {
	dam = power( dam * ( 2 + UMIN( ( ch->level / 10 ), 4 ) ),
		     3, get_curr_dex( ch ) - 18 );
	if( number_range( 0, power( 1000, 1, 20 - get_curr_dex( ch ) ) ) == 1 )
	    dam *= 4;
    }
    else if( dt == gsn_slit_throat )
    {
	dam = power( dam * ( 2 + UMIN( ( ch->level / 8 ), 8 ) ),
		     6, get_curr_dex( ch ) - 20 );
	if( number_range( 0, power( 250, 2, 20 - get_curr_dex( ch ) ) ) == 0 )
	{
	    OBJ_DATA *head;
	    char buf[MAX_INPUT_LENGTH];

	    act( "$n tear$% $N's head clean off!", ch, NULL, victim, TO_ALL );
	    head = create_object( get_obj_index( 50 ), victim->level );
	    head->value[0] = BODY_PART_HEAD;
	    sprintf( buf, head->short_descr, IS_NPC( victim )
		     ? victim->short_descr : victim->name );
	    free_string( head->short_descr );
	    head->short_descr = str_dup( buf );
	    if( IS_AFFECTED( victim, AFF_POISON ) )
		head->value[3] = 1;

	    obj_to_room( head, ch->in_room );
	    victim->hit /= 2;
	    damage( ch, victim, victim->max_hit * 3, dt, wpn );
	    return;
	}
    }
    else if( dt == gsn_circle )	/* 150% to 200% at lev. 50 */
	dam += dam / 2 + ( dam * ch->level ) / 100;
    else if( dt == gsn_retaliate )
	dam *= 2;
    else if( dt == gsn_roundhouse )
	dam = dam + dam * get_success( ch, dt, 100 ) / 50;
    else if( dt == gsn_uppercut )
	dam = dam + dam * get_success( ch, dt, 100 ) / 100;
    else if( dt == gsn_elbow )
	dam = dam + 3 * dam * get_success( ch, dt, 100 ) / 400;
    else if( dt == gsn_knee )
	dam = dam + dam * get_success( ch, dt, 100 ) / 200;
    else if( dt == gsn_backhand )
	dam = dam + dam * get_success( ch, dt, 100 ) / 400;
    else if( dt == gsn_smash )
	dam *= 2;
    else if( dt == gsn_golden_touch )
	dam = dam * 5 / 2;

    /* they have hit so they must do SOME damage */
    if( dam <= 0 )
	dam = 1;

    save_hit = victim->hit;
    damage( ch, victim, dam, dt, wpn );
    if( !victim->deleted && ch->fighting && victim->hit < save_hit )
	damage_eq( ch, victim );
    if( wield && number_bits( 7 )
	< pow( 8, ( get_curr_str( ch ) - wield->weight - 20 ) / 12. )
	&& !get_success( ch, gsn_weapon_skill, 30 ) )
    {
	act( "&rYou struck out too powerfully that stroke ...",
	     ch, NULL, NULL, TO_CHAR );
	mod_item_condition( ch, wield,
			    UMAX( 1, ( get_curr_str( ch ) + number_bits( 3 )
				       - wield->weight ) / 27 ) );
    }

    if( ch->fighting && offwield
	&& dt >= TYPE_HIT && get_eq_char( ch, WEAR_WIELD_L )
	&& ( ( IS_SET( race_table[ch->race].race_abilities, RACE_DUAL_WIELD )
	       && number_percent( ) < 40 )
	     || IS_NPC_CLASS( ch ) ? number_percent( ) < UMIN( ch->level, 40 )
	     : get_success( ch, gsn_dual, 50 ) ) )
	one_hit( ch, victim, TYPE_UNDEFINED, WEAR_WIELD_L );

    flashing_blades( ch, victim, dt, wpn );
    return;
}


/*
 * casts a spell from a weapon
 */
void cast_weapon_spell( CHAR_DATA *ch, CHAR_DATA *victim, OBJ_DATA *wield )
{
    int cost[MAGIC_MAX], i;
    for( i = 0; i < MAGIC_MAX; ++i )
    {
	cost[i] = mana_cost( ch, wield->value[0], i ) / 3;
	if( ch->mana[i] < cost[i] )
	    return;
    }
    for( i = 0; i < MAGIC_MAX; ++i )
	ch->mana[i] -= cost[i];
    ( *skill_table[wield->value[0]].spell_fun )
	( gsn_weapon_spell, ( wield->level / 2 ), ch, victim );
}


/*
 * Apply any damage modifiers for spell sphere and other magical considerations
 */
int sphere_damage_mod( int dam, int sn, CHAR_DATA *ch, CHAR_DATA *victim )
{
    int i, total, sphere[MAGIC_MAX];
    float diff;
    for( i = 0; i < MAGIC_MAX; ++i )
    {
	sphere[i] = 0;
	if( skill_table[sn].min_mana[i] != 0 )
	    break;
    }
    if( i >= MAGIC_MAX )	/* this isn't a spell */
	return dam;
    dam *= 1000;
    total = 0;
    for( ; i < MAGIC_MAX; ++i )
    {
	if( skill_table[sn].min_mana[i] > 0 )
	    total += sphere[i] = skill_table[sn].min_mana[i];
	else
	    sphere[i] = 0;
    }
    for( i = 0; i < MAGIC_MAX; ++i )
    {
	if( ( sphere[i] = sphere[i] * 1000 / total ) <= 0 )
	    continue;
	diff = ch->level - victim->level;
	diff = pow( 1.4, diff ) / 3;
	diff += get_magic( ch, i ) - get_magic( victim, i );
	diff -= get_magic_resist( victim ) / 40;

	/*
	 * 1.5 times the damage if diff is 10,
	 * 0.5 times damage if diff is -10
	 * formula is inverse tan so that maximum damage is 2 times
	 * and minimum is zero.
	 */
	dam *= 1 + sphere[i] * atan( diff / 10 ) / ( M_PI * 500 );
    }
    /* intelligence mod, base damage at 20, 1.5 times at 30, 0.5 times at 10 */
    diff = get_curr_int( ch ) * 2 - get_curr_wis( victim );
    diff = diff / 20 - 1;
    dam *= 1 + 2 * atan( diff ) / M_PI;
    /* body temperature mods */
    if( IS_SET( skill_table[sn].skill_type, SKILL_TYPE_FIRE ) )
	dam -= ( dam * get_curr_temp( ch ) ) / 250;
    if( IS_SET( skill_table[sn].skill_type, SKILL_TYPE_ICE ) )
	dam += ( dam * get_curr_temp( ch ) ) / 250;
    return UMAX( 1, dam / 1000 );
}


/*
 * Inflict damage from a hit.
 */
void damage( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dt, int wpn )
{
    int sntemp;

    if( is_safe( ch, victim ) )
    {
	if( ch->fighting )
	    stop_fighting( ch, TRUE );
	return;
    }

    if( victim->position == POS_DEAD )
	return;

    if( IS_AFFECTED( ch, AFF_FAST_TALK ) )
	affect_strip( ch, skill_lookup( "fast talk" ) );

    /*
     * Stop up any residual loopholes.
     */
    if( dam > 100000 && ch->level <= LEVEL_HERO )
    {
	if( IS_NPC( ch ) && ch->desc )
	    bug( "Damage: %d from %s by %s: > 100000 points with %d dt!",
		     dam, ch->name, ch->desc->original->name, dt );
	else
	    bug( "Damage: %d from %s: > 100000 points with %d dt!",
		     dam, ch->name, dt );
    }

    if( victim != ch )
    {
	/*
	 * Certain attacks are forbidden.
	 * Most other attacks are returned.
	 */
	check_killer( ch, victim );

	if( victim->position > POS_STUNNED && dt != TYPE_MPDAMAGE )
	{
	    if( !victim->fighting )
		set_fighting( victim, ch );
	    if( victim->position != POS_SMASHED
		&& victim->position != POS_SLEEPING
		&& victim->position != POS_GETTING_UP )
		victim->position = POS_FIGHTING;

	    if( !ch->fighting )
		set_fighting( ch, victim );

	    /*
	     * If NPC victim is following, ch might attack victim's master.
	     * No charm check here because charm would be dispelled from
	     * tanking mobile when combat ensues thus ensuring PC charmer is
	     * not harmed.
	     * Check for is_same_group wont work as following mobile is not
	     * always grouped with PC charmer - Kahn
	     */
	    if( IS_NPC( ch )
		&& IS_NPC( victim )
		&& victim->master
		&& victim->master->in_room == ch->in_room
		&& number_bits( 2 ) == 0 )
	    {
		stop_fighting( ch, FALSE );
		set_fighting( ch, victim->master );
		return;
	    }
	}

	/*
	 * More charm stuff.
	 */
	if( victim->master == ch )
	    stop_follower( victim );

	/*
	 * Inviso attacks ... not.
	 */
	if( IS_NPC( ch ) && xIS_SET( ch->act, ACT_BURIED )
	    && dt != TYPE_MPDAMAGE )
	{
	    act( "$n tears $mself up from the earth.",
		 ch, NULL, NULL, TO_ROOM );
	    xREMOVE_BIT( ch->act, ACT_BURIED );
	}
	if( IS_AFFECTED( ch, AFF_INVISIBLE ) && dt != TYPE_MPDAMAGE )
	{
	    affect_strip( ch, gsn_invis );
	    affect_strip( ch, gsn_mass_invis );
	    if( !is_affected( ch, gsn_vanish ) )
	    {
		xREMOVE_BIT( ch->affected_by, AFF_INVISIBLE );
		act( "$n fades into existence.", ch, NULL, NULL, TO_ROOM );
	    }
	    else
		xSET_BIT( ch->affected_by, AFF_INVISIBLE );
	}

	/*
	 * Damage modifiers.
	 */
	/* Surge bonus, before changing from the weapon spell */
	if( dt >= 0 && dt < MAX_SKILL
	    && IS_SET( skill_table[dt].skill_type, SKILL_TYPE_MAGIC )
	    && !IS_NPC( ch ) && IS_SET( ch->pcdata->pc_bits, PC_BIT_SURGE ) )
	    dam += dam;

	if( dt == gsn_weapon_spell )
	{
	    OBJ_DATA *wield;

	    wield = get_eq_char( ch, WEAR_WIELD_R );
	    if( !wield || wield->value[0] <= 0 )
		wield = get_eq_char( ch, WEAR_WIELD_DOUBLE );
	    if( !wield || wield->value[0] <= 0 )
		wield = get_eq_char( ch, WEAR_WIELD_L );
	    if( !wield )
		bug( "Weird weapon spell bug [%s].", ch );
	    else
		dt = wield->value[0];
	}

	if( dt < 0 || dt > MAX_SKILL
	    || !IS_SET( skill_table[dt].skill_type, SKILL_TYPE_NO_RESIL ) )
	{
	    dam *= get_curr_resil( victim );
	    dam /= 1000;
	}

	/* magic spells */
	if( dt >= 0 && dt < MAX_SKILL
	    && IS_SET( skill_table[dt].skill_type, SKILL_TYPE_MAGIC ) )
	    dam = sphere_damage_mod( dam, dt, ch, victim );

	/* weapon strikes + warp flesh */
	if( IS_AFFECTED( victim, AFF_WARP_FLESH )
	    && ( ( dt > 0 && dt < MAX_SKILL &&
		   IS_SET( skill_table[dt].skill_type, SKILL_TYPE_WEAPONSTRIKE ) )
		 || dt >= TYPE_HIT ) )
	    dam *= 2;

	switch( victim->position )
	{
	case POS_SMASHED:
	    dam += dam / 2;
	    break;
	case POS_SITTING:
	    dam += dam / 10;
	    break;
	case POS_RESTING:
	    dam += dam / 5;
	    break;
	default:
	    break;
	}

	if( IS_AFFECTED( victim, AFF_SANCTUARY )
	    || ( !IS_NPC( victim )
		 && IS_SET( race_table[victim->race].race_abilities, RACE_SANCT ) ) )
	    dam /= 2;

	if( ( IS_AFFECTED( victim, AFF_PROTECT )
	      || IS_SET( race_table[victim->race].race_abilities,
			 RACE_PROTECTION ) )
	    && ( ( IS_EVIL( ch ) && IS_GOOD( victim ) )
		 || ( IS_GOOD( ch ) && IS_EVIL( victim ) ) ) )
	    dam -= dam / 4;

	if( dam < 0 )
	    dam = 0;

	/*
	 * Make sure you start counting hits.
	 */
	if( ch->num_hits < 0 && victim == ch->fighting )
	    ch->num_hits = 0;

	/*
	 * Check for disarm, trip, parry, and dodge.
	 */
	if( dt >= TYPE_HIT || dt == gsn_kick || dt == gsn_race_tail )
	{
	    int leveldiff = ch->level - victim->level - 10;

	    if( IS_NPC( ch ) && dam == 0
		&& number_percent( ) * 2
		< ( leveldiff < -5 ? ch->level / 2 : UMAX( 10, leveldiff ) ) )
		disarm( ch, victim );
	    if( IS_NPC( ch ) && dam == 0
		&& number_percent( ) * 2 < UMIN( 30, leveldiff ) )
		trip( ch, victim );
	    if( IS_NPC( ch )
		&& !IS_SET( race_table[ch->race].race_abilities,
			   RACE_NO_WEAPON_WIELD )
		&& number_percent( ) < UMIN( 25, UMAX( 10, ch->level ) )
		&& !IS_NPC( victim ) )
		use_magical_item( ch );
	    if( dam > 0 && !IS_AFFECTED( ch, AFF_NEVERMISS ) )
	    {
		if( check_stumble( ch, victim ) )
		    return;
		if( check_parry( ch, victim ) )
		    return;
		if( check_dodge( ch, victim ) )
		    return;
		if( check_shield_block( ch, victim ) )
		    return;
		if( check_blink( ch, victim ) )
		    return;
		if( check_familiar( ch, victim, dam ) )
		    return;
	    }
	}
    }

    /* sleep modifier, has to be after dodge and parry */
    if( victim->position == POS_SLEEPING && dam > 0 )
    {
	dam += ch->level * 2 + dam;
	victim->position = POS_GETTING_UP;
    }
    /*
     * We moved dam_message out of the victim != ch if above
     * so self damage would show.  Other valid type_undefined
     * damage is ok to avoid like mortally wounded damage - Kahn
     */
    if( dt != TYPE_UNDEFINED && dt != TYPE_MPDAMAGE )
	dam_message( ch, victim, dam, dt, wpn );

    /*
     * Hurt the victim.
     * Inform the victim of his new state.
     */
    victim->hit -= dam;
    if( !IS_NPC( victim )
	&& victim->level >= LEVEL_IMMORTAL
	&& victim->hit < 1 )
	victim->hit = 1;

    if( ( ( dt > 0 && dt < MAX_SKILL &&
	    IS_SET( skill_table[dt].skill_type, SKILL_TYPE_WEAPONSTRIKE ) )
	  || ( dt >= TYPE_HIT && dt != TYPE_MPDAMAGE ) ) && dam > 1
	&& victim != ch && IS_AFFECTED( victim, AFF_ATTACKSHIELD ) )
    {
	sntemp = skill_lookup( "flamestrike" );
	( *skill_table[sntemp].spell_fun ) ( sntemp, ( victim->level / 4 ),
					     victim, ch );
    }

    if( victim->position <= POS_STUNNED
	&& IS_AFFECTED( victim, AFF_BERSERK ) )
	affect_strip( victim, gsn_berserk );

    update_pos( victim );

    /*
     * Sleep spells and extremely wounded folks.
     */
    if( !IS_AWAKE( victim ) && victim->fighting )
	stop_fighting( victim, FALSE );
    switch( victim->position )
    {
    case POS_MORTAL:
	send_to_char(
	    "You are mortally wounded, and will die soon, if not aided.\n\r",
	    victim );
	act( "$n is mortally wounded, and will die soon, if not aided.",
	     victim, NULL, NULL, TO_ROOM );
	break;

    case POS_INCAP:
	send_to_char(
	    "You are incapacitated and will slowly die, if not aided.\n\r",
	    victim );
	act( "$n is incapacitated and will slowly die, if not aided.",
	     victim, NULL, NULL, TO_ROOM );
	break;

    case POS_STUNNED:
	send_to_char( "You are stunned, but will probably recover.\n\r",
		      victim );
	act( "$n is stunned, but will probably recover.",
	     victim, NULL, NULL, TO_ROOM );
	break;

    case POS_DEAD:
	if( IS_AFFECTED( victim, AFF_DEAD ) )
	    return;
	send_to_char( "&RYou have been &fKILLED!!&n\n\r\n\r", victim );
	act( "&r$n is DEAD!!", victim, NULL, NULL, TO_ROOM );
	if( dt == gsn_hand_of_kaz )
	{
	    OBJ_DATA *obj;
	    char buf[MAX_INPUT_LENGTH];

	    act( "&yYou lift $N's still beating heart from $S chest in triumph.",
		 ch, NULL, victim, TO_CHAR );
	    obj = create_object( get_obj_index( OBJ_VNUM_TORN_HEART ), victim->level );
	    sprintf( buf, obj->short_descr,
		     IS_NPC( victim ) ? victim->short_descr : victim->name );
	    free_string( obj->short_descr );
	    obj->short_descr = str_dup( buf );

	    sprintf( buf, obj->description,
		     IS_NPC( victim ) ? victim->short_descr : victim->name );
	    free_string( obj->description );
	    obj->description = str_dup( buf );
	    obj->cost /= 20;

	    obj_to_char( obj, ch );
	    if( !get_eq_char( ch, WEAR_HOLD_L )
		&& !get_eq_char( ch, WEAR_SHIELD )
		&& !get_eq_char( ch, WEAR_WIELD_L )
		&& !get_eq_char( ch, WEAR_WIELD_DOUBLE ) )
		equip_char( ch, obj, WEAR_HOLD_L );
	    else if( !get_eq_char( ch, WEAR_HOLD_R )
		     && !get_eq_char( ch, WEAR_WIELD_R )
		     && !get_eq_char( ch, WEAR_WIELD_DOUBLE ) )
		equip_char( ch, obj, WEAR_HOLD_R );
	}
	break;

    default:
	if( dam > victim->max_hit / 5 )
	    send_to_char( "&rThat really did &RHURT!&n\n\r", victim );
	check_critical( ch, victim, dam, wpn, dt );
	if( victim->position != POS_DEAD
	    && victim->hit < victim->max_hit / 5 )
	{
	    xSET_BIT( victim->affected_by, AFF_BLEEDING );
	    send_to_char( "You sure are &rBLEEDING!&n\n\r", victim );
	}
	break;
    }

    /*
     * Payoff for killing things.
     */
    if( victim->position == POS_DEAD )
    {
	group_gain( ch, victim );

	if( !IS_NPC( ch ) )
	{
	    ch->pcdata->killed++;
	    if( ch->pcdata->clan
		&& ( ( ( IS_NPC( victim )
			 && ch->pcdata->clan->clan_type != CLAN_NORMAL ) )
		     || ( !IS_NPC( victim )
			  && ch->pcdata->clan->clan_type == CLAN_NORMAL ) ) )
		ch->pcdata->clan->kills++;
	}

	if( !IS_NPC( victim ) )
	{
	    SysInfo->deaths++;
	    if( victim->pcdata->clan
		&& ( ( IS_NPC( ch )
		       && victim->pcdata->clan->clan_type != CLAN_NORMAL )
		     || ( !IS_NPC( ch )
			  && victim->pcdata->clan->clan_type == CLAN_NORMAL ) ) )
		victim->pcdata->clan->deaths++;
	    victim->pcdata->died++;
	    sprintf( log_buf, "%s killed by %s at %s",
		     victim->name,
		     ch == victim ? "their own stupidity"
		     : ( IS_NPC( ch ) ? ch->short_descr : ch->name ),
		     victim->in_room->name );
	    talk_channel( NULL, log_buf, CHANNEL_INFO, "INFO" );
	    log_string( "%s (%d)", log_buf, victim->in_room->vnum );
	    wiznetf( victim, WIZ_DEATHS, get_trust( victim ), "%s (%d)",
		     log_buf, victim->in_room->vnum );

	    /*
	     * Battle stuff.
	     * Need to keep the battle flag set so they can keep
	     * all their gear and get a restore.
	     */
	    end_battle_check( victim );

	    /*
	     * Dying penalty:
	     * 1/2 way back to previous 2 levels.
	     */
	    if( IS_NPC( ch ) && !xIS_SET( victim->act, PLR_BATTLE ) )
	    {
		if( victim->exp < get_tnl( victim ) - 4 )
		    gain_exp( victim, ( victim->exp - get_tnl( victim ) ) / 3 );
		else
		    gain_exp( victim, -4 );
	    }
	}

	if( !IS_NPC( victim ) && victim->pcdata->bounty > 0
	    && !xIS_SET( victim->act, PLR_BATTLE )
	    && !IS_SET( victim->in_room->room_flags, ROOM_ARENA ) )
	{
	    act( "&g$n collect$% the bounty on $O head.",
		 ch, NULL, victim, TO_ALL );
	    ch->gold += victim->pcdata->bounty;
	    victim->pcdata->bounty = 0;
	}
	if( !IS_NPC( victim ) && !IS_AFFECTED( victim, AFF_DEAD )
	    && get_success( victim, gsn_swan_song, 60 ) )
	{
	    xSET_BIT( victim->affected_by, AFF_DEAD );
	    update_pos( victim );
	    create_char_event( victim, evn_swan_song,
			       dice( 4, 24 ) * PULSE_PER_SECOND );
	    return;
	}

	raw_kill( ch, victim );

	/* Ok, now we want to remove the deleted flag from the
	 * PC victim.
	 */
	if( !IS_NPC( victim ) )
	    victim->deleted = FALSE;

	if( !IS_NPC( ch ) && IS_NPC( victim ) )
	{
	    if( ch->pcdata->pc_bits >= PC_BIT_STRANGLE )
		ch->pcdata->pc_bits %= PC_BIT_STRANGLE;

	    /* Autogold by Morpheus */
	    if( xIS_SET( ch->act, PLR_AUTOGOLD ) )
		do_get( ch, "coins corpse" );	/* autogold mod by Canth */

	    if( xIS_SET( ch->act, PLR_AUTOLOOT ) )
		do_get( ch, "all corpse" );
	    else
		do_look( ch, "in corpse" );

	    if( xIS_SET( ch->act, PLR_AUTOSAC ) )
		do_sacrifice( ch, "corpse" );
	}
	else if( IS_NPC( ch ) && xIS_SET( ch->act, ACT_MERCENARY )
		 && IS_NPC( victim ) )
	{
	    do_get( ch, "all corpse" );
	    sort_inventory( ch );
	}

	/*
	 * Remove victims of pk who no longer have exps left
	 */
	if( !IS_NPC( victim ) && xIS_SET( victim->act, PLR_DENY ) )
	{
	    do_quit( victim, "" );
	}

	return;
    }
    else
    {
	if( dam > 0 && dt == TYPE_HIT && !get_eq_char( ch, WEAR_HOLD_R )
	    && !get_eq_char( ch, WEAR_HOLD_L ) && !get_eq_char( ch, WEAR_SHIELD ) )
	{
	    barehand_hit( ch, victim );
	    if( victim->deleted || victim->position == POS_DEAD )
		return;
	}
	else if( ( dt > 0 && dt < MAX_SKILL &&
		   IS_SET( skill_table[dt].skill_type, SKILL_TYPE_WEAPONSTRIKE ) )
		 || ( dt >= TYPE_HIT && dt != TYPE_MPDAMAGE ) )
	{
	    OBJ_DATA *wield;

	    if( dam > 0 && ( wield = get_eq_char( ch, wpn ) ) )
	    {
		if( IS_SET( wield->extra_flags, ITEM_POISONED ) )
		{
		    ( *skill_table[gsn_poison].spell_fun )
			( gsn_poison, ( wield->level / 2 ), ch, victim );
		}
		if( victim->deleted || victim->position == POS_DEAD )
		    return;
		if( wield->item_type == ITEM_WEAPON && wield->value[0] > 0 )
		    cast_weapon_spell( ch, victim, wield );
		if( victim->deleted || victim->position == POS_DEAD )
		    return;
	    }
	}
    }

    if( victim == ch )
	return;

    /*
     * Take care of link dead people.
    if( !IS_NPC( victim ) && !victim->desc )
    {
	if( number_range( 0, victim->wait ) == 0 )
	{
	    do_recall( victim, "" );
	    return;
	}
    }
     */

    /*
     * Wimp out?
     */
    if( IS_NPC( victim ) && dam > 0 )
    {
	if( ( xIS_SET( victim->act, ACT_WIMPY ) && number_bits( 1 ) == 0
	      && victim->hit < victim->max_hit / 3 )
	    || ( IS_AFFECTED( victim, AFF_CHARM ) && victim->master
		 && victim->master->in_room != victim->in_room ) )
	    do_flee( victim, "" );
    }

    if( !IS_NPC( victim )
	&& victim->hit > 0
	&& victim->hit <= victim->wimpy
	&& victim->wait == 0 )
	do_flee( victim, "" );
    return;
}


bool is_safe( CHAR_DATA *ch, CHAR_DATA *victim )
{
    if( IS_SET( SysInfo->flags, SYSINFO_NOFIGHT ) )
    {
	send_to_char( "You can't fight!\n\r", ch );
	return TRUE;
    }

    /* the organised PK options... checked before some of the standard
     * 'safe' options such as a safe room.
     */
    if( !IS_NPC( ch ) && !IS_NPC( victim ) )
    {
	if( IS_SET( victim->in_room->room_flags, ROOM_ARENA ) )
	    return FALSE;

	if( xIS_SET( ch->act, PLR_BATTLE )
	    && xIS_SET( victim->act, PLR_BATTLE )
	    && ( !IS_SET( SysInfo->flags, SYSINFO_HOLYWAR )
		 || ch->pcdata->religion != victim->pcdata->religion ) )
	    return FALSE;
    }

    if( !IS_NPC( victim ) && xIS_SET( victim->act, PLR_OUTLAW ) )
	return FALSE;

    if( !IS_NPC( ch ) && IS_AFFECTED( ch, AFF_GHOUL ) )
    {
	send_to_char(
	    "You may not participate in combat while in ghoul form.\n\r",
	    ch );
	return TRUE;
    }

    if( !IS_NPC( victim ) && IS_AFFECTED( victim, AFF_GHOUL ) )
    {
	act( "&g$o attack passes through $N.", ch, NULL, victim, TO_ALL );
	return TRUE;
    }

    if( IS_SET( victim->in_room->room_flags, ROOM_SAFE ) )
    {
	act( "&g$N is in a safe room.", ch, NULL, victim, TO_CHAR );
	return TRUE;
    }

    if( is_clan_enemy( victim, ch ) )
	return FALSE;

    if( !IS_NPC( ch ) && IS_NPC( victim ) && victim->spec_fun
	&& IS_SET( spec_table[victim->spec_fun].usage, SPEC_ATTACK ) )
    {
	return (*spec_table[victim->spec_fun].spec_fun)
	    ( victim, ch, SPEC_ATTACK, NULL );
    }

    if( IS_NPC( ch ) )
	return FALSE;

    if( IS_NPC( victim ) )
    {
	/*
	 * Mobprog for getting attacked.
	 * Note that the hard coded prog has precedence, if it exists
	 * these will be ignored.
	 */
	if( !victim->fighting
	    && xIS_SET( victim->pIndexData->progtypes, ATTACK_PROG ) )
	    mprog_percent_check( victim, ch,
				 NULL, NULL, ATTACK_PROG );
	if( ch->in_room != victim->in_room
	    || ch->in_room->area != victim->in_room->area )
	    return TRUE;
	return FALSE;
    }

    if( ch != victim && str_cmp( race_table[victim->race].name, "Vampire" ) )
    {
	if( victim->level > LEVEL_HERO )
	{
	    act( "$N is a &uHERO&n and is automatically safe.",
		 ch, NULL, victim, TO_CHAR );
	    return TRUE;
	}
	return TRUE;
    }

    return FALSE;
}


void show_opponent( CHAR_DATA *ch )
{
    int state;
    const char *message;

    if( !ch->fighting )
	return;

    state = ch->fighting->hit * 100 / ch->fighting->max_hit;
    if( state < 12 )
	message = "&b$N is really buggered, $E looks ready to drop.&n";
    else if( state < 25 )
	message = "&b$N is wounded badly, $E looks gone for all money.&n";
    else if( state < 38 )
	message = "&b$N is bleeding freely from many cuts.&n";
    else if( state < 51 )
	message = "&b$N sways in the breeze, looking dazed.&n";
    else if( state < 64 )
	message = "&b$N is bruised and sore looking.&n";
    else if( state < 77 )
	message = "&b$N has a few minor scratches and abrasions.&n";
    else if( state < 90 )
	message = "&b$N is slightly hurt, but would still pass physical.&n";
    else
	message = "&b$N has no obvious health problems.&n";

    act( message, ch, NULL, ch->fighting, TO_CHAR );
    if( ch->in_room != ch->fighting->in_room )
    {
	stop_fighting( ch->fighting, FALSE );
	stop_fighting( ch, FALSE );
    }
}


void barehand_hit( CHAR_DATA *ch, CHAR_DATA *victim )
{
    struct bh_type
    {
	int * num;
	int cha;
    };
    const struct bh_type bh_gsns[] =
    {
	{ &gsn_backhand,	10 },
	{ &gsn_jab,		20 },
	{ &gsn_knee,		40 },
	{ &gsn_elbow,		80 },
	{ &gsn_uppercut,	160 },
	{ &gsn_roundhouse,	320 },
	{ &gsn_headbutt,	500 },
	{ NULL, 0 }
    };
    int i, chance;
    int cost = 20;

    if( !IS_NPC( ch ) && IS_SET( ch->pcdata->pc_bits, PC_BIT_SURGE ) )
	cost *= 3;
    /* if you are barehanded you get the chance at the barehanded effects */
    if( IS_AFFECTED( ch, AFF_MAGICHANDS ) )
    {
	if( ch->mana[MAGIC_FIRE] >= cost
	    && is_affected( ch, gsn_burning_hands ) )
	{
	    ch->mana[MAGIC_FIRE] -= cost;
	    spell_power_3( gsn_burn, ch->level, ch, victim );
	}
	else if( ch->mana[MAGIC_WATER] >= cost
		 && is_affected( ch, gsn_chill_touch ) )
	{
	    ch->mana[MAGIC_WATER] -= cost;
	    spell_freeze( skill_lookup( "freeze" ), ch->level, ch, victim );
	}
    }
    if( ch->fighting != victim || victim->deleted
	|| victim->position == POS_DEAD )
	return;
    if( IS_AFFECTED( ch, AFF_MAGICHANDS ) && ch->mana[MAGIC_AIR] >= cost
	&& is_affected( ch, gsn_shocking_grasp ) )
    {
	ch->mana[MAGIC_AIR] -= cost;
	spell_power_5( skill_lookup( "shock" ), ch->level, ch, victim );
	if( ch->fighting != victim || victim->deleted
	    || victim->position == POS_DEAD )
	    return;
    }
    if( IS_AFFECTED( ch, AFF_MAGICHANDS )
	&& is_affected( ch, gsn_dark_claws ) )
    {
	spell_poison( skill_lookup( "poison" ), ch->level * 2 / 3, ch, victim );
	if( ch->fighting != victim || victim->deleted
	    || victim->position == POS_DEAD )
	    return;
    }

    if( IS_NPC( ch ) )
	return;

    for( i = 0; bh_gsns[i].num; ++i )
    {
	if( !can_use( ch, *bh_gsns[i].num ) )
	    continue;
	chance = 300 * bh_gsns[i].cha;
	chance /= 2 * ch->pcdata->learned[*bh_gsns[i].num]
	    + ch->pcdata->learned[gsn_barehand] + 2 * get_curr_dex( ch );
	if( number_range( 0, chance ) == 0 )
	{
	    one_hit( ch, victim, *bh_gsns[i].num, WEAR_WIELD_R );
	    return;
	}
    }
    return;
}


void flashing_blades( CHAR_DATA *ch, CHAR_DATA *victim, int dt, int wpn )
{
    int chance;

    if( IS_NPC_CLASS( ch ) || ch->fighting != victim || dt < TYPE_HIT
	|| ( wpn != WEAR_WIELD_L && wpn != WEAR_WIELD_R )
	|| !can_use( ch, gsn_flashing_blades ) )
	return;

    if( ( wpn == WEAR_WIELD_L && get_eq_char( ch, WEAR_WIELD_R ) )
	|| !get_eq_char( ch, wpn ) )
	return;

    chance = power( get_success( ch, gsn_flashing_blades, 100 ),
		    -2, ch->carry_weight - 10 );
    if( number_bits( 9 ) < chance )
	one_hit( ch, victim, dt, wpn );
    return;
}


bool get_juggle_attack( CHAR_DATA *ch, int num )
{
    OBJ_DATA * obj;
    int amount = 0;
    int chance;

    if( ch->deleted || !ch->fighting || ch->position < POS_FIGHTING )
	return FALSE;
    for( obj = get_eq_char( ch, WEAR_JUGGLED ); obj; obj = obj->next_content )
    {
	if( obj->deleted || obj->wear_loc != WEAR_JUGGLED )
	    continue;
	amount++;
    }
    /* only a small reduction of chance for higher number attacks as these
       are less likely to occur due to initiative lossage.  */
    chance = num * 5 + power( 20, 5, 22 - get_curr_dex( ch ) );
    if( amount < num || number_bits( 7 ) < chance )
	return FALSE;
    return TRUE;
}


void juggle_shuffle( CHAR_DATA *ch )
{
    OBJ_DATA *obj;
    OBJ_DATA *juggled[MAX_JUGGLED];
    int i = 0;
    int slot;

    if( !get_eq_char( ch, WEAR_JUGGLED ) )
	return;
    for( obj = ch->carrying; i < MAX_JUGGLED && obj; obj = obj->next_content )
    {
	if( !obj->deleted && ( obj->wear_loc == WEAR_JUGGLED
			       || obj->wear_loc == WEAR_WIELD_L
			       || obj->wear_loc == WEAR_WIELD_R ) )
	{
	    juggled[i++] = obj;
	    obj->wear_loc = WEAR_JUGGLED;
	}
    }
    if( i < 3 )
    {
	bug( "%s is juggling but not enough.\n\r", ch->name );
	return;
    }
    do	/* pick new right wield */
    {
	slot = number_range( 0, i - 1 );
    }
    while( juggled[slot] == NULL );
    juggled[slot]->wear_loc = WEAR_WIELD_R;
    juggled[slot] = NULL;
    do	/* pick new left wield */
    {
	slot = number_range( 0, i - 1 );
    }
    while( juggled[slot] == NULL );
    juggled[slot]->wear_loc = WEAR_WIELD_L;
    juggled[slot] = NULL;
    if( !get_success( ch, gsn_juggle, 130 - i * 10 ) )
    {
	if( get_success( ch, gsn_juggle, 30 + get_curr_dex( ch ) ) )
	{
	    act( "$n stuff$% up but make$% a hasty recovery.",
		 ch, NULL, NULL, TO_ALL );
	    WAIT_STATE( ch, skill_table[gsn_juggle].beats );
	    return;
	}
	act( "$n stuffs up $s juggling and drops weapons all over...",
	     ch, NULL, NULL, TO_ROOM );
	act( "You stuff up your juggling routine.", ch, NULL, NULL, TO_CHAR );
	while( ( obj = get_eq_char( ch, WEAR_JUGGLED ) ) )
	{
	    if( number_bits( i ) > 10 )
	    {
		slot = obj->level * ( i - 3 );
		slot = number_range( slot, slot * 3 / 2 );
		damage( ch, ch, slot, gsn_juggle, WEAR_NONE );
	    }
	    act( "$p falls to the ground.", ch, obj, NULL, TO_ALL );
	    obj_from_char( obj );
	    obj_to_room( obj, ch->in_room );
	}
    }
    return;
}


void check_critical( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int wpn, int dt )
{
    OBJ_DATA *obj = NULL;
    int chance = 50 - get_curr_con( victim );
    int part, i;

    if( dam <= victim->level		/* rare occurance */
	|| victim->level < 10 || number_bits( 5 ) )
	return;

    if( wpn != WEAR_NONE )
	obj = get_eq_char( ch, wpn );

    if( !obj )
	chance -= 10;
    else
    {
	if( obj->item_type == ITEM_WEAPON )
	    return;		/* can't do any damage without a weapon */
	if( IS_SET( obj->extra_flags, ITEM_SHARP ) )
	    chance += 5;
	if( dam > victim->max_hit / 5 )
	    chance += 12;
	else if( dam > victim->max_hit / 10 )
	    chance += 5;
    }
    chance += ( get_curr_str( ch ) ) - 20;

    part = number_percent( );
    for( i = 0; part_loss_table[i].chance > 0; ++i )
    {
	if( part > part_loss_table[i].chance
	    || ( part_loss_table[i].body_parts & victim->body_parts ) == 0 )
	    continue;
	if( IS_SET( victim->damaged_parts, part_loss_table[i].body_parts ) )
	    chance += 20;
	break;
    }

    chance = UMIN( chance, 25 ) - number_bits( 7 );

    if( chance > 15 && part_loss_table[i].chance > 0
	&& dt > TYPE_HIT && dt != TYPE_HIT + 2
	&& ( dt <= TYPE_HIT + 5 || dt >= TYPE_HIT + 10 )
	&& dt != TYPE_HIT + 11 && dt != TYPE_HIT + 12 )
	remove_part( ch, victim, i );
    else if( chance > 0 )
    {
	act( "You get a fluke shot on $N and blood spurts from a fresh wound.",
	     ch, NULL, victim, TO_CHAR );
	act( "$n gets a fluke shot on you, opening a new wound.",
	     ch, NULL, victim, TO_VICT );
	act( "$n strikes $N, making an ugly wound appear.",
	     ch, NULL, victim, TO_NOTVICT );
	if( IS_SET( victim->damaged_parts, part_loss_table[i].body_parts ) )
	    dam += dam / 2;
	SET_BIT( victim->damaged_parts, part_loss_table[i].body_parts );
	xSET_BIT( victim->affected_by, AFF_BLEEDING );
    }

    if( chance > 0 && victim->position != POS_DEAD )
    {
	xSET_BIT( victim->affected_by, AFF_BLEEDING );
	chance = UMAX( 10, chance );
	dam = number_range( dam * chance / 75, dam * chance / 30 );
	damage( ch, victim, dam, gsn_lucky_blow, wpn );
    }
    return;
}


void remove_part( CHAR_DATA *ch, CHAR_DATA *victim, int part )
{
    int smeg;
    OBJ_DATA *obj, *obj_next;
    char buf[MAX_INPUT_LENGTH];

    obj = create_object( get_obj_index( part_loss_table[part].limb_vnum ),
			 victim->level );
    sprintf( buf, obj->short_descr, IS_NPC( victim )
	     ? victim->short_descr : victim->name );
    free_string( obj->short_descr );
    obj->short_descr = str_dup( buf );
    obj->value[0] = victim->body_parts & part_loss_table[part].body_parts;
    if( IS_AFFECTED( ch, AFF_POISON ) )
	SET_BIT( obj->extra_flags, ITEM_POISONED );
    if( IS_AFFECTED( victim, AFF_POISON ) )
	obj->value[3] = 1;
    obj->cost /= 20;
    obj_to_room( obj, victim->in_room );

    act( part_loss_table[part].act_char, victim, NULL, ch, TO_CHAR );
    act( part_loss_table[part].act_other, victim, NULL, ch, TO_ROOM );

    for( obj = victim->carrying; obj; obj = obj_next )
    {
	obj_next = obj->next_content;
	if( obj->wear_loc == WEAR_NONE )
	    continue;

	smeg = part_loss_table[part].body_parts & victim->body_parts;
	smeg &= wear_table[obj->wear_loc].body_parts;
	if( smeg )
	{
	    act( "&m$p&m drops to the ground.&n",
		 victim, obj, NULL, TO_ALL );
	    unequip_char( ch, obj );
	    obj_from_char( obj );
	    obj_to_room( obj, victim->in_room );
	}
    }

    victim->body_parts &= ~part_loss_table[part].body_parts;

    if( IS_SET( part_loss_table[part].body_parts, BODY_PART_HEAD )
	&& victim->position != POS_DEAD )
    {
	act( "$n die$%.", victim, NULL, ch, TO_ALL );
	victim->hit = -10;
	victim->position = POS_DEAD;
    }
    return;
}


void damage_eq( CHAR_DATA *ch, CHAR_DATA *victim )
{
    OBJ_DATA *obj;

    if( number_bits( 10 ) > 65 - get_curr_dex( victim ) )
	return;
    for( obj = victim->carrying; obj; obj = obj->next_content )
    {
	if( obj->deleted || obj->wear_loc == WEAR_NONE
	    || number_bits( 10 ) > 40 - get_curr_dex( victim ) )
	    continue;

	mod_item_condition( ch, obj, 1 );
	return;
    }
    return;
}


/*
 * Check to see if the battle that is currently raging is over.
 */
void end_battle_check( CHAR_DATA *victim )
{
    CHAR_DATA *rch;
    int contin = 0;
    RELIGION_DATA *pReligion = NULL;
    CLAN_DATA *pClan;

    if( !xIS_SET( victim->act, PLR_BATTLE ) )
	return;

    if( IS_SET( SysInfo->flags, SYSINFO_HOLYWAR ) )
    {
	for( rch = char_list; rch; rch = rch->next )
	{
	    if( rch->deleted || IS_NPC( rch ) || rch == victim
		|| !xIS_SET( rch->act, PLR_BATTLE ) )
		continue;

	    if( !pReligion )
		pReligion = rch->pcdata->religion;
	    else if( pReligion != rch->pcdata->religion )
		return;		/* More than one religion left, no action */
	}

	for( rch = char_list; rch; rch = rch->next )
	    if( !IS_NPC( rch ) )
		xREMOVE_BIT( rch->act, PLR_BATTLE );
	REMOVE_BIT( SysInfo->flags, SYSINFO_HOLYWAR );

	if( !pReligion )
	{
	    bug( "In the grasp of a Holy War and there is no winner." );
	    return;
	}
	sprintf( log_buf, "%s has won the Holy War for %s!",
		 pReligion->display_name, pReligion->god_name );
	talk_channel( NULL, log_buf, CHANNEL_INFO, "INFO" );

	for( pClan = clan_first; pClan; pClan = pClan->next )
	    if( pClan->clan_type == CLAN_ORDER
		&& pClan->religion == pReligion )
		add_karma( pClan, 250 );
    }
    else if( battle_min && battle_max )
    {
	for( rch = char_list; rch; rch = rch->next )
	{
	    if( !IS_NPC( rch ) && xIS_SET( rch->act, PLR_BATTLE )
		&& rch != victim )
		contin++;
	}
	if( contin < 2 )
	{
	    for( rch = char_list; rch; rch = rch->next )
		if( !IS_NPC( rch ) && rch != victim &&
		    xIS_SET( rch->act, PLR_BATTLE ) )
		    break;
	    if( rch )
	    {
		xREMOVE_BIT( rch->act, PLR_BATTLE );
		sprintf( log_buf, "%s has won the battle!",
			 rch->name );
		talk_channel( NULL, log_buf, CHANNEL_INFO, "INFO" );
		do_recall( rch, "auto" );
	    }
	    battle_min = 0;
	    battle_max = 0;
	}
    }
}

/*
 * See if an attack justifies a OUTLAW flag.
 */
void check_killer( CHAR_DATA *ch, CHAR_DATA *victim )
{
    OBJ_DATA *obj;
    OBJ_DATA *obj_next;
    char buf[MAX_STRING_LENGTH];

    /*
     * NPC's are fair game.
     */
    if( IS_NPC( victim ) )
	return;

    /*
     * NPC's are cool of course
     * Hitting yourself is cool too (bleeding).
     * Hitting immortals are fine, if you can.
     */
    if( IS_NPC( ch ) || ch == victim || victim->level > LEVEL_HERO )
	return;

    /*
     * OUTLAWs are fair game.
     * OUTLAW aggressors should not be penalized 1000 exps per attack but
     * per combat started.
     */
    if( xIS_SET( victim->act, PLR_OUTLAW )
	|| ( xIS_SET( ch->act, PLR_OUTLAW ) && ch->fighting ) )
	return;

    if( IS_SET( ch->in_room->room_flags, ROOM_ARENA )
	|| ( xIS_SET( ch->act, PLR_BATTLE ) &&
	     xIS_SET( victim->act, PLR_BATTLE ) ) )
	return;

    /* Allowing attacks on temporary vampires. */
    if( !str_cmp( race_table[victim->race].name, "Vampire" )
	&& IS_AFFECTED( victim, AFF_VAMP_BITE ) )
	return;

    send_to_char( "You are a &ROUTLAW!&n  You lose 1000 exps.\n\r", ch );
    sprintf( buf, "Help!  I'm being attacked by %s!", ch->name );
    do_shout( victim, buf );
    xSET_BIT( ch->act, PLR_OUTLAW );
    gain_exp( ch, -100000 );
    demote_level( ch );

    for( obj = ch->carrying; obj; obj = obj_next )
    {
	obj_next = obj->next_content;

	if( obj->deleted )
	    continue;
	obj_from_char( obj );

	/*
	 * Remove item inventories
	 * Includes licenses to kill
	 */
	if( IS_SET( obj->extra_flags, ITEM_INVENTORY ) )
	{
	    extract_obj( obj );
	    continue;
	}

	obj_to_char( obj, ch );
    }

    save_char_obj( ch );

    return;
}


/*
 * Check to see if weapon is poisoned.
 */
bool is_wielding_poisoned( CHAR_DATA *ch, int wpn )
{
    OBJ_DATA *obj;

    if( ( obj = get_eq_char( ch, wpn ) )
	&& IS_SET( obj->extra_flags, ITEM_POISONED ) )
	return TRUE;

    return FALSE;
}


/*
 * Check for block.
 */
bool check_shield_block( CHAR_DATA *ch, CHAR_DATA *victim )
{
    int chance;

    if( IS_NPC_CLASS( victim )
	|| IS_SET( victim->extra_bits, CH_EX_SHIELD_BLOCK )
	|| !IS_AWAKE( victim ) )
	return FALSE;

    if( get_eq_char( victim, WEAR_SHIELD ) == NULL )
	return FALSE;

    chance = get_success( victim, gsn_shield_block, 200 )
	* get_curr_str( victim ) / 40;

    if( number_percent( ) >= chance + victim->level - ch->level )
	return FALSE;

    if( victim->desc && !xIS_SET( CH( victim->desc )->act, PLR_BATTLESELF ) )
	act( "&gYou block $n's attack with your shield.", ch, NULL, victim,
	     TO_VICT );
    if( ch->desc && !xIS_SET( CH( ch->desc )->act, PLR_BATTLESELF ) )
	act( "&g$N blocks your attack with a shield.", ch, NULL, victim,
	     TO_CHAR );
    if( number_bits( 2 ) )
	SET_BIT( victim->extra_bits, CH_EX_SHIELD_BLOCK );
    return TRUE;
}


/*
 * Check for parry.
 */
bool check_parry( CHAR_DATA *ch, CHAR_DATA *victim )
{
    int chance;

    if( IS_SET( victim->extra_bits, CH_EX_PARRY )
	|| !IS_AWAKE( victim ) )
	return FALSE;

    if( IS_NPC_CLASS( victim ) )
    {
	/* Tuan was here. :) */
	chance = UMIN( 40, victim->level - ch->level / 3 );
	if( !get_eq_char( victim, WEAR_WIELD_R ) )
	{
	    if( !get_eq_char( victim, WEAR_WIELD_L ) )
		chance /= 2;
	    else
		chance = 3 * chance / 4;
	}
    }
    else
    {
	if( !get_eq_char( victim, WEAR_WIELD_R ) )
	{
	    if( !get_eq_char( victim, WEAR_WIELD_L ) )
		return FALSE;
	    chance = get_success( victim, gsn_parry, 200 ) / 4;
	}
	else
	    chance = get_success( victim, gsn_parry, 200 ) / 2;
	if( !IS_NPC( victim ) )
	    chance -= victim->pcdata->condition[COND_DRUNK] / 10;
    }
    if( ch->position < POS_FIGHTING )
	chance = chance * 3 / 4;

    if( number_percent( ) >= chance + victim->level - ch->level )
	return FALSE;

    if( number_bits( 2 ) )
	SET_BIT( victim->extra_bits, CH_EX_PARRY );
    if( ch->desc && !xIS_SET( CH( ch->desc )->act, PLR_BATTLESELF ) )
	act( "&g$N parries your attack.", ch, NULL, victim, TO_CHAR );
    if( victim->desc && !xIS_SET( CH( victim->desc )->act, PLR_BATTLESELF ) )
	act( "&gYou parry $n's attack.", ch, NULL, victim, TO_VICT );
    return TRUE;
}


/*
 * Check for dodge.
 */
bool check_dodge( CHAR_DATA *ch, CHAR_DATA *victim )
{
    int chance, which_bit;

    if( !IS_AWAKE( victim ) )
	return FALSE;

    if( IS_SET( victim->extra_bits, CH_EX_DODGE ) )
    {
	if( IS_SET( victim->extra_bits, CH_EX_DODGE_2 ) )
	    return FALSE;
	else
	    which_bit = CH_EX_DODGE_2;
    }
    else
	which_bit = CH_EX_DODGE;

    if( IS_NPC_CLASS( victim ) )
	/* Tuan was here. :) */
	chance = UMIN( 25, victim->level - ch->level / 3 );
    else
    {
	if( which_bit == CH_EX_DODGE )
	    chance = get_success( victim, gsn_dodge, 200 ) / 3;
	else
	    chance = get_success( victim, gsn_avoidance, 200 ) / 3;
	chance = power( chance, 2, get_curr_dex( ch ) - 20 );
	if( !IS_NPC( victim ) )
	    chance -= victim->pcdata->condition[COND_DRUNK] / 10;
    }
    if( chance > 0 )
	chance += UMIN( get_speed( victim ), 30 );
    if( ch->position < POS_FIGHTING )
	chance = chance * 2 / 3;

    if( number_percent( ) >= chance + victim->level - ch->level )
	return FALSE;

    if( number_bits( 2 ) )
	SET_BIT( victim->extra_bits, which_bit );
    if( ch->desc && !xIS_SET( CH( ch->desc )->act, PLR_BATTLESELF ) )
	act( "&g$N dodges your attack.", ch, NULL, victim, TO_CHAR );
    if( victim->desc && !xIS_SET( CH( victim->desc )->act, PLR_BATTLESELF ) )
	act( "&gYou dodge $n's attack.", ch, NULL, victim, TO_VICT );
    return TRUE;
}


/*
 * Check for dodge.
 */
bool check_blink( CHAR_DATA *ch, CHAR_DATA *victim )
{
    int chance;

    if( !IS_AWAKE( victim ) || IS_NPC( victim )
	|| victim->pcdata->blink <= 0 )
	return FALSE;

    chance = get_success( victim, gsn_blink, 100 ) * victim->pcdata->blink / 100;
    chance = power( chance, 2, get_curr_int( ch ) - 20 );
    chance -= victim->pcdata->condition[COND_DRUNK] / 10;
    if( chance > 0 )
	chance += UMIN( get_speed( victim ), 30 );
    if( ch->position < POS_FIGHTING )
	chance = chance * 2 / 3;

    if( number_percent( ) >= chance + victim->level - ch->level )
	return FALSE;

    if( number_bits( 2 ) )
	SET_BIT( victim->extra_bits, CH_EX_BLINK );
    if( ch->desc && !xIS_SET( CH( ch->desc )->act, PLR_BATTLESELF ) )
	act( "&g$N blinks out of existance avoiding your strike.",
	     ch, NULL, victim, TO_CHAR );
    if( victim->desc && !xIS_SET( CH( victim->desc )->act, PLR_BATTLESELF ) )
	act( "&gYou blink out of existance, narrowly avoiding $n's attack.",
	     ch, NULL, victim, TO_VICT );
    take_generic_mana( victim, victim->pcdata->blink * MAGIC_MAX );
    return TRUE;
}


/*
 * One of the advantages to being drunk.
 */
bool check_stumble( CHAR_DATA *ch, CHAR_DATA *victim )
{
    if( IS_NPC( victim ) || IS_SET( victim->extra_bits, CH_EX_STUMBLE )
	|| !IS_AWAKE( victim ) )
	return FALSE;

    if( number_percent( ) < ( victim->pcdata->condition[COND_DRUNK] / 10 - 20 ) / 2 )
    {
	if( ch->desc && !xIS_SET( CH( ch->desc )->act, PLR_BATTLESELF ) )
	    act( "&g$N stumbles drunkenly and your blow goes wide.",
		 ch, NULL, victim, TO_CHAR );
	if( victim->desc && !xIS_SET( CH( victim->desc )->act, PLR_BATTLESELF ) )
	    act( "&gYou trip over your weapon and land in a heap on the ground.",
		 ch, NULL, victim, TO_VICT );
	victim->position = POS_GETTING_UP;
	WAIT_STATE( victim, PULSE_VIOLENCE / 2 );
	SET_BIT( victim->extra_bits, CH_EX_STUMBLE );
	return TRUE;
    }
    return FALSE;
}


#define chance 42
bool check_familiar( CHAR_DATA *ch, CHAR_DATA *victim, int dam )
{
    if( IS_NPC( victim ) || IS_SET( victim->extra_bits, CH_EX_FAMILIAR )
	|| !IS_AWAKE( victim ) || victim->pcdata->familiar <= 0 )
	return FALSE;

    if( number_percent( ) > UMIN( 60, chance - victim->level + ch->level ) )
    {
	if( number_percent( ) > UMIN( 5, ( chance - victim->level + ch->level ) / 12 ) )
	    return FALSE;
	else
	{
	    if( victim->desc && !xIS_SET( CH( victim->desc )->act, PLR_BATTLESELF ) )
		act( "&gYour familiar distracts $n from $s attack.",
		     ch, NULL, victim, TO_VICT );
	    if( ch->desc && !xIS_SET( CH( ch->desc )->act, PLR_BATTLESELF ) )
		act( "&g$N's familiar distracts you and you miss $M, #&&*@!",
		     ch, NULL, victim, TO_CHAR );
	    SET_BIT( victim->extra_bits, CH_EX_FAMILIAR );
	    return TRUE;
	}
    }

    if( victim->desc && !xIS_SET( CH( victim->desc )->act, PLR_BATTLESELF ) )
	act( "&gYour familiar bravely takes $n's attack for you.",
	     ch, NULL, victim, TO_VICT );
    if( ch->desc && !xIS_SET( CH( ch->desc )->act, PLR_BATTLESELF ) )
	act( "&g$N's familiar gets in the way of your attack.",
	     ch, NULL, victim, TO_CHAR );
    victim->pcdata->familiar -= dam;
    if( victim->pcdata->familiar <= 0 )
    {
	victim->pcdata->familiar = 0;
	act( "&y$o familiar screams and disappears.", victim, NULL, ch, TO_ALL );
    }
    if( number_bits( 2 ) )
	SET_BIT( victim->extra_bits, CH_EX_FAMILIAR );
    return TRUE;
}
#undef chance


void dirty_fighting( CHAR_DATA *ch )
{
    CHAR_DATA *victim = ch->fighting;
    AFFECT_DATA af;
    OBJ_DATA *obj;
    int i;
    char buf[MAX_INPUT_LENGTH];

    if( !victim )
	return;
    switch( number_bits( 3 ) )
    {
    case 0:	case 1:
	act( "$n fling$% a handful of dirt in $O face, blinding $M!",
	     ch, NULL, victim, TO_ALL );
	af.type = gsn_dirty_fighting;
	af.location = APPLY_HITROLL;
	af.modifier = -4 - ch->level / 25;
	af.duration = 0;
	vset( af.bitvector, AFF_BLIND );
	affect_to_char( victim, &af, NULL );
	break;

    case 2:
	act( "$n knee$% $N in the groin.", ch, NULL, victim, TO_ALL );
	if( victim->sex != SEX_MALE )
	{
	    act( "$n shrugs off the effect of the blow.",
		 victim, NULL, NULL, TO_ROOM );
	    send_to_char( "That didn't hurt too much.\n\r", victim );
	    damage( ch, victim, number_range( ch->level / 2, ch->level ),
		    gsn_dirty_fighting, WEAR_NONE );
	}
	else
	{
	    act( "$n groans in agony and doubles over, clutching at $s groin.",
		 victim, NULL, NULL, TO_ROOM );
	    send_to_char( "OOOMMPH... you double over in agony!\n\r", victim );
	    damage( ch, victim, number_range( ch->level, ch->level * 3 ),
		    gsn_dirty_fighting, WEAR_NONE );
	    victim->position = POS_GETTING_UP;
	    WAIT_STATE( victim, PULSE_VIOLENCE );
	}
	break;

    case 3:
	if( get_curr_dex( ch ) - get_curr_dex( victim ) + number_range( -10, 10 ) < 0 )
	    break;
	act( "$n grabs $O ears and crushes $S face to a pulp with $s forehead.",
	     ch, NULL, victim, TO_ROOM );
	act( "You grab $N and headbutt him, square between the eyes.",
	     ch, NULL, victim, TO_CHAR );
	if( get_eq_char( victim, WEAR_FACE )
	    || !IS_SET( victim->body_parts, BODY_PART_NOSE ) )
	    damage( ch, victim, number_range( ch->level / 2,  ch->level ),
		    gsn_dirty_fighting, WEAR_NONE );
	else
	{
	    act( "$O nose is splattered across $S face.",
		 ch, NULL, victim, TO_ALL );
	    damage( ch, victim, number_range( ch->level, ch->level * 3 ),
		    gsn_dirty_fighting, WEAR_NONE );
	    REMOVE_BIT( victim->body_parts, BODY_PART_NOSE );
	    xSET_BIT( victim->affected_by, AFF_BLEEDING );
	}
	break;

    case 4:
	act( "$n leap$% for $O eyes, fingers clawed.",
	     ch, NULL, victim, TO_ALL );
	if( number_bits( 3 ) || get_eq_char( victim, WEAR_FACE )
	    || !IS_SET( victim->body_parts, BODY_PART_EYES )
	    || get_curr_dex( ch ) - get_curr_dex( victim ) + number_range( -10, 10 ) < 0 )
	    break;
	for( i = 0; part_loss_table[i].body_parts != BODY_PART_EYES; )
	    ++i;
	obj = create_object( get_obj_index( part_loss_table[i].limb_vnum ),
			     victim->level );
	sprintf( buf, obj->short_descr, IS_NPC( victim )
		 ? victim->short_descr : victim->name );
	free_string( obj->short_descr );
	obj->short_descr = str_dup( buf );
	obj->value[0] = victim->body_parts & part_loss_table[i].body_parts;
	if( IS_AFFECTED( victim, AFF_POISON ) )
	    obj->value[3] = 1;
	obj->cost /= 20;
	obj_to_char( obj, ch );
	act( part_loss_table[i].act_char, victim, NULL, ch, TO_CHAR );
	act( part_loss_table[i].act_other, victim, NULL, ch, TO_ROOM );
	break;

    case 5:
	act( "$n deliver$% a swift kick to $O shins.", ch, NULL, victim, TO_ALL );
	damage( ch, victim, number_range( ch->level / 2, ch->level ),
		gsn_dirty_fighting, WEAR_NONE );
	break;

    default:
	break;
    }
}


/* Mobs using magical items by Spitfire from merc mailing list */
/* Modified to give every magical item an equal chance of being used plus
 * eating pills -Kahn */
void use_magical_item( CHAR_DATA *ch )
{
    OBJ_DATA *obj;
    OBJ_DATA *cobj = NULL;
    int number = 0;
    char buf[MAX_INPUT_LENGTH];

    for( obj = ch->carrying; obj; obj = obj->next_content )
    {
	if( ( obj->item_type == ITEM_SCROLL
	      || obj->item_type == ITEM_WAND
	      || obj->item_type == ITEM_STAFF
	      || obj->item_type == ITEM_PILL )
	    && number_range( 0, number ) == 0 )
	{
	    cobj = obj;
	    number++;
	}
    }

    if( !cobj )
	return;

    switch( cobj->item_type )
    {
    case ITEM_SCROLL:
	do_recite( ch, "scroll" );
	break;
    case ITEM_WAND:
	if( cobj->wear_loc == WEAR_HOLD_L
	    || cobj->wear_loc == WEAR_HOLD_R )
	    do_zap( ch, "" );
	break;
    case ITEM_STAFF:
	if( cobj->wear_loc == WEAR_HOLD_L
	    || cobj->wear_loc == WEAR_HOLD_R )
	    do_brandish( ch, "" );
	break;
    case ITEM_POTION:
	do_quaff( ch, "potion" );
	break;
    case ITEM_PILL:
	sprintf( buf, "%s", cobj->name );
	do_eat( ch, buf );
	break;
    }
    return;
}


/*
 * Set position of a victim.
 */
void update_pos( CHAR_DATA *victim )
{
    if( victim->hit > 0 || IS_AFFECTED( victim, AFF_DEAD ) )
    {
	if( victim->position <= POS_STUNNED )
	    victim->position = POS_STANDING;
	return;
    }

    if( IS_NPC( victim ) || victim->hit <= -11 )
    {
	victim->position = POS_DEAD;
	return;
    }

    if( victim->hit <= -6 )
	victim->position = POS_MORTAL;
    else if( victim->hit <= -3 )
	victim->position = POS_INCAP;
    else
	victim->position = POS_STUNNED;

    return;
}


/*
 * Start fights.
 */
void set_fighting( CHAR_DATA *ch, CHAR_DATA *victim )
{
    if( ch->fighting )
    {
	bug( "Set_fighting: already fighting" );
	bug( "...%s attacking %s at %d",
		 ( IS_NPC( ch ) ? ch->short_descr : ch->name ),
		 ( IS_NPC( victim ) ? victim->short_descr : victim->name ),
		 victim->in_room->vnum );
	return;
    }

    if( IS_AFFECTED( ch, AFF_SLEEP ) )
	affect_strip( ch, gsn_sleep );

    ch->fighting = victim;
    if( ch->position != POS_SMASHED
	&& ch->position != POS_SLEEPING
	&& ch->position != POS_GETTING_UP )
	ch->position = POS_FIGHTING;

    if( IS_NPC( ch ) )
	add_mob_fight_triggers( ch );
    if( get_time_left( ch->in_room->events, evn_room_violence ) < 0 )
	create_room_event( ch->in_room, evn_room_violence,
			   PULSE_VIOLENCE );

    return;
}


/*
 * Stop fights.
 */
void stop_fighting( CHAR_DATA *ch, bool fBoth )
{
    CHAR_DATA *fch;

    purge_spam( ch->in_room );
    for( fch = char_list; fch; fch = fch->next )
    {
	if( fch == ch || ( fBoth && fch->fighting == ch ) )
	{
	    fch->fighting = NULL;
	    if( fch->position != POS_SMASHED )
		fch->position = POS_STANDING;
	    if( IS_AFFECTED( fch, AFF_BERSERK ) )
		affect_strip( fch, gsn_berserk );
	    if( !IS_NPC( fch ) && fch->pcdata->pc_bits >= PC_BIT_STRANGLE )
		fch->pcdata->pc_bits %= PC_BIT_STRANGLE;
	    update_pos( fch );
	}
    }
    return;
}


/*
 * Stop fights only in the character's room.
 * easier on CPU from mobile_update
 */
void stop_fighting_room( CHAR_DATA *ch, bool fBoth )
{
    CHAR_DATA *fch;

    if( ch->in_room->area->nplayer )
	purge_spam( ch->in_room );
    for( fch = ch->in_room->people; fch; fch = fch->next_in_room )
    {
	if( fch == ch || ( fBoth && fch->fighting == ch ) )
	{
	    fch->fighting = NULL;
	    if( fch->position != POS_SMASHED )
		fch->position = POS_STANDING;
	    if( IS_AFFECTED( fch, AFF_BERSERK ) )
		affect_strip( fch, gsn_berserk );
	    if( !IS_NPC( fch ) && fch->pcdata->pc_bits >= PC_BIT_STRANGLE )
		fch->pcdata->pc_bits %= PC_BIT_STRANGLE;
	    update_pos( fch );
	}
    }
    return;
}


/*
 * Make a corpse out of a character.
 */
void make_corpse( CHAR_DATA *ch )
{
    OBJ_DATA *corpse;
    OBJ_DATA *obj;
    OBJ_DATA *obj_next;
    char *name;
    char buf[MAX_STRING_LENGTH];

    if( IS_NPC( ch ) )
    {
	/*
	 * This longwinded corpse creation routine comes about because
	 * we dont want anything created AFTER a corpse to be placed
	 * INSIDE a corpse.  This had caused crashes from obj_update( )
	 * in extract_obj( ) when the updating list got shifted from
	 * object_list to obj_free.	     --- Thelonius (Monk)
	 */

	name = ch->short_descr;
	if( ch->gold > 0 )
	{
	    OBJ_DATA *coins;

	    coins = create_money( ch->gold );
	    corpse = create_object( get_obj_index( OBJ_VNUM_CORPSE_NPC ), 0 );
	    obj_to_obj( coins, corpse );
	    ch->gold = 0;
	}
	else
	{
	    corpse = create_object( get_obj_index( OBJ_VNUM_CORPSE_NPC ), 0 );
	}

	set_timer_tick( corpse, number_fuzzy( 2 ) );
    }
    else
    {
	name = ch->name;
	corpse = create_object( get_obj_index( OBJ_VNUM_CORPSE_PC ), 0 );
	set_timer_tick( corpse, number_range( 20, 30 ) );
    }

    corpse->value[0] = ch->race;
    sprintf( buf, "%s %s", corpse->name, name );
    free_string( corpse->name );
    corpse->name = str_dup( buf );

    sprintf( buf, corpse->short_descr, name );
    free_string( corpse->short_descr );
    corpse->short_descr = str_dup( buf );

    sprintf( buf, corpse->description, name );
    free_string( corpse->description );
    corpse->description = str_dup( buf );

    corpse->level = ch->level;

    for( obj = ch->carrying; obj; obj = obj_next )
    {
	obj_next = obj->next_content;

	if( obj->deleted )
	    continue;
	obj_from_char( obj );

	/*
	 * Remove item inventories from all corpses.
	 * Includes licenses to kill
	 */
	if( IS_SET( obj->extra_flags, ITEM_INVENTORY ) )
	{
	    extract_obj( obj );
	}
	else
	{
	    obj_to_obj( obj, corpse );
	}
    }

    obj_to_room( corpse, ch->in_room );
    strip_events( &corpse->events, evn_imp_grab );
    return;
}


/*
 * Improved Death_cry contributed by Diavolo.
 */
void death_cry( CHAR_DATA *ch )
{
    OBJ_DATA *obj;
    char buf[MAX_STRING_LENGTH];
    const char *msg;
    int vnum;
    int door;
    int i, chance;

    vnum = 0;
    switch( number_bits( 4 ) )
    {
    default:
	msg = "&yYou hear $n's death cry.";
	break;
    case 0:
    case 1:
    case 2:
	msg = "&yYou hear $n's death cry.";
	vnum = -1;		/* take an actual limb */
	break;
    case 3:
	msg = "&y$n hits the ground ... DEAD.";
	break;
    case 4:
	msg = "&y$n splatters blood on your armour.";
	break;
    case 5:
    case 6:
	msg = "&yYou smell $n's sphincter releasing in death.";
	vnum = OBJ_VNUM_FINAL_TURD;
	break;
    case 7:
	msg = "&y$n crumples to the ground, lifeless.";
	break;
    case 8:
    case 9:
	msg = "&yYou lift $o still beating heart from $s chest in triumph.";
	vnum = OBJ_VNUM_TORN_HEART;
	break;
    case 10:
	msg = "&y$n screams in agony as $s life leaves $m.";
	break;
    case 11:
	msg = "&yYou slice $n open and spill $s guts on the ground.";
	vnum = OBJ_VNUM_SPILLED_GUTS;
	break;
    case 12:
	msg = "&yYour blow smacks $n fair in the mouth and broken teeth fly.";
	vnum = OBJ_VNUM_LOOSE_TEETH;
	break;
    }

    if( !IS_NPC( ch ) && number_bits( 1 ) )
	vnum = 0;

    if( vnum > 0 )
    {
	char *name;

	act( msg, ch, NULL, NULL, TO_ALL );

	name = IS_NPC( ch ) ? ch->short_descr : ch->name;
	obj = create_object( get_obj_index( vnum ), ch->level );
	set_timer_tick( obj, number_range( 4, 7 ) );
	if( IS_AFFECTED( ch, AFF_POISON ) )
	    SET_BIT( obj->extra_flags, ITEM_POISONED );

	sprintf( buf, obj->short_descr, name );
	free_string( obj->short_descr );
	obj->short_descr = str_dup( buf );

	sprintf( buf, obj->description, name );
	free_string( obj->description );
	obj->description = str_dup( buf );

	if( IS_AFFECTED( ch, AFF_POISON ) )
	    obj->value[3] = 1;
	obj->cost /= 20;

	obj_to_room( obj, ch->in_room );
    }
    else
    {
	chance = number_percent( );
	for( i = 0; vnum < 0 && part_loss_table[i].chance > 0; ++i )
	{
	    if( chance > part_loss_table[i].chance
		|| ( part_loss_table[i].body_parts & ch->body_parts ) == 0 )
		continue;

	    remove_part( ch, ch, i );
	    break;
	}
	if( part_loss_table[i].chance <= 0 )
	    act( msg, ch, NULL, NULL, TO_ALL );
    }

    if( IS_NPC( ch ) )
	msg = "&yYou hear something's death cry.&n";
    else
	msg = "&yYou hear someone's death cry.&n";

    for( door = 0; door <= 5; door++ )
    {
	EXIT_DATA *pexit;

	if( ( pexit = ch->in_room->exit[door] )
	    && pexit->to_room
	    && pexit->to_room != ch->in_room )
	    send_to_room( msg, pexit->to_room );
    }

    return;
}


void raw_kill( CHAR_DATA *ch, CHAR_DATA *victim )
{
    AFFECT_DATA *paf;
    bool safe_kill = FALSE;
    EVENT *e, *e_next;
    int can_cry = 1;

    if( !IS_NPC( victim ) && ( xIS_SET( victim->act, PLR_BATTLE )
			       || IS_SET( victim->in_room->room_flags,
					  ROOM_ARENA ) ) )
	safe_kill = TRUE;

    stop_fighting( victim, TRUE );
    if( ch != victim && IS_NPC( victim )
	&& xIS_SET( victim->pIndexData->progtypes, DEATH_PROG ) )
    {
	victim->position = POS_STANDING;
	can_cry = mprog_percent_check( victim, ch, NULL, NULL, DEATH_PROG );
    }
    if( ch != victim && can_cry && can_cry != NO_FLAG )
	death_cry( victim );

    if( victim->spec_fun
	&& IS_SET( spec_table[victim->spec_fun].usage, SPEC_DEATH ) )
    {
	if( !( *spec_table[victim->spec_fun].spec_fun )
	    ( victim, ch, SPEC_DEATH, NULL ) )
	    make_corpse( victim );
    }
    else if( safe_kill )
    {
	OBJ_DATA *obj;

	for( obj = victim->carrying; obj; obj = obj->next_content )
	{
	    if( !obj->deleted && obj->wear_loc != WEAR_NONE )
		unequip_char( victim, obj );
	}
    }
    else
	make_corpse( victim );

    if( !IS_NPC( victim ) && IS_AFFECTED( victim, AFF_VAMP_BITE ) )
    {
	paf = new_affect( );
	paf->type = gsn_vampiric_bite;
	paf->location = APPLY_RACE;
	paf->modifier = race_lookup( "Vampire" ) - victim->race;
	paf->duration = 1000;
	vset( paf->bitvector, AFF_POLYMORPH );
	xSET_BIT( paf->bitvector, AFF_VAMP_BITE );
	paf->next = victim->affected;
	victim->affected = paf;
    }

    for( paf = victim->affected; paf; paf = paf->next )
    {
	if( paf->deleted || xIS_SET( paf->bitvector, AFF_POLYMORPH ) )
	    continue;

	/* Keep the ghoul affect, not
	   if( !IS_NPC( victim ) && IS_AFFECTED( victim, AFF_GHOUL ) )
	   continue; */

	affect_remove( victim, paf );
    }
    for( e = ch->events; e; e = e_next )
    {
	e_next = e->next_local;
	if( !IS_SET( e->flags ^ event_table[e->type].flags, EVENT_DEATH_REMOVE ) )
	    continue;
	event_remove_global( e );
	event_remove_local( &ch->events, e );
	free_event( e );
    }


    if( IS_NPC( victim ) )
    {
	if( !IS_NPC( ch ) && ch->pcdata->quest->target == victim->pIndexData )
	{
	    send_to_char( "&BYou have almost completed your QUEST!&n\n\r", ch );
	    send_to_char( "Better hurry and finish it now.\n\r", ch );
	    ch->pcdata->quest->type = QUEST_KILL_COMPLETE;
	}
	SysInfo->mob_deaths++;
	victim->pIndexData->killed++;
	kill_table[URANGE( 0, victim->level, ( LEVEL_HERO * 2 ) - 1 )].killed++;
	extract_char( victim, TRUE );
	return;
    }

    extract_char( victim, FALSE );
    victim->position = POS_RESTING;
    clean_char( victim );
    victim->body_parts &= race_table[victim->race].body_parts;
    if( safe_kill )
    {
	int i;

	victim->hit = victim->max_hit;
	for( i = 0; i < MAGIC_MAX; ++i )
	    victim->mana[i] = victim->max_mana[i];
	victim->move = victim->max_move;
	xREMOVE_BIT( victim->affected_by, AFF_BLEEDING );
	xREMOVE_BIT( victim->act, PLR_BATTLE );
	victim->pcdata->died--;
	victim->body_parts = race_table[victim->race].body_parts;
    }
    if( !str_cmp( race_table[victim->race].name, "Vampire" ) )
    {
	victim->pcdata->condition[COND_FULL] = 10;
	victim->pcdata->condition[COND_THIRST] = 10;
    }

    save_char_obj( victim );
    return;
}


void group_gain( CHAR_DATA *ch, CHAR_DATA *victim )
{
    CHAR_DATA *gch;
    char buf[MAX_STRING_LENGTH];
    int members, member_levels, highest_member = 0;
    int xp;

    /*
     * Monsters don't get kill xp's or alignment changes.
     * Dying of mortal wounds or poison doesn't give xp to anyone!
     */
    if( IS_NPC( ch ) || victim == ch )
	return;

    members = 0;
    member_levels = 0;
    for( gch = ch->in_room->people; gch; gch = gch->next_in_room )
    {
	if( is_same_group( gch, ch ) )
	{
	    if( gch->level > highest_member )
		highest_member = gch->level;
	    members++;
	    member_levels += gch->level;
	}
    }

    if( members == 0 || member_levels == 0 )
    {
	bug( "Group_gain: members-%d levels-%d", members,
		   member_levels );
	members = 1;
	member_levels = ch->level;
    }

    members--;
    for( gch = ch->in_room->people; gch; gch = gch->next_in_room )
    {
	OBJ_DATA *obj;
	OBJ_DATA *obj_next;

	if( !is_same_group( gch, ch ) )
	    continue;

	xp = xp_compute( gch, victim, -1 ) * gch->level / member_levels;
	xp += xp * members / 10;

	/* So high levels can't drag littlies around, xp drops off.
	 * Once the highest character in the group is 18 levels more
	 * than gch, gch gets no experience.  --Symp
	 */
	if( gch->level + 5 < highest_member )
	    xp = 2 * xp - power( xp, 4, highest_member - gch->level );
	if( xp < 0 )
	    xp = 0;

	if( xp >= 2500 )
	    sprintf( buf, "&YYou receive %d experience points.&n\n\r",
		     xp / 100 );
	else
	    sprintf( buf, "&YYou receive %d.%2.2d experience points.&n\n\r",
		     xp / 100, xp % 100 );
	send_to_char( buf, gch );
	gain_exp( gch, xp );

	for( obj = gch->carrying; obj; obj = obj_next )
	{
	    obj_next = obj->next_content;
	    if( obj->deleted || obj->wear_loc == WEAR_NONE
		|| number_bits( 1 ) )
		continue;

	    if( ( IS_OBJ_STAT( obj, ITEM_ANTI_EVIL )
		  && IS_EVIL( gch ) )
		|| ( IS_OBJ_STAT( obj, ITEM_ANTI_GOOD )
		     && IS_GOOD( gch ) )
		|| ( IS_OBJ_STAT( obj, ITEM_ANTI_NEUTRAL )
		     && IS_NEUTRAL( gch ) ) )
	    {
		act( "&m$n is zapped by $p.&n", gch, obj, NULL, TO_ALL );
		obj_from_char( obj );
		obj_to_room( obj, gch->in_room );
	    }
	}
    }

    return;
}


/*
 * Compute xp for a kill.
 * Also adjust alignment of OUTLAW.
 * Edit this function to change xp computations.
 */
int xp_compute( CHAR_DATA *gch, CHAR_DATA *victim, int dam )
{
    float bonus;
    int xp;
    int extra;
    int level;
    int number;

    /*
     * alignment change if this is a k"ng blow
     * this has grown a little to include religious follow-on
     * in the event of significant alignment change.
     */
    if( dam < 0 )
    {
	extra = gch->alignment - victim->alignment * 3 / 2;
	extra += race_table[gch->race].natural_align / 10;
	bonus = log( (double)( abs( extra ) + 1 ) );
	extra = bonus * bonus * ( ( extra < 0 ) ? -1 : 1 );
	gch->alignment = URANGE( -1000, gch->alignment + extra, 1000 );

	if( gch->pcdata->clan && gch->pcdata->clan->clan_type == CLAN_ORDER
	    && gch->pcdata->clan_rank != RANK_EXILED
	    && victim->alignment >= gch->pcdata->religion->align_min
	    && victim->alignment <= gch->pcdata->religion->align_max )
	    add_karma( gch->pcdata->clan, -3 );

	if( !IS_NPC( gch ) && gch->pcdata->religion
	    && ( !xIS_SET( gch->act, PLR_OUTLAW ) || number_bits( 1 ) )
	    && ( gch->alignment < gch->pcdata->religion->align_min
		 || gch->alignment > gch->pcdata->religion->align_max ) )
	{
	    send_to_char( "&ROh NO!&r You have angered your god!\n\r", gch );
	    act( "$n has angered his god!.", gch, NULL, NULL, TO_ROOM );
	    act( "A large bolt of lightning strikes $n from the heavens.",
		 gch, NULL, NULL, TO_ROOM );
	    act( "A large bolt of lightning strikes you from the heavens.",
		 gch, NULL, NULL, TO_CHAR );
	    spell_power_6( skill_lookup( "lightning bolt" ),
				  gch->level + 5, gch, gch );

	    xSET_BIT( gch->act, PLR_OUTLAW );
	    if( gch->pcdata->clan && gch->pcdata->clan->clan_type == CLAN_ORDER
		&& gch->pcdata->clan_rank != RANK_EXILED )
	    {
		CLAN_DATA *order = gch->pcdata->clan;
		log_string( "Order %s: forcibly exiled from %s",
			   gch->name, order->name );
		wiznetf( gch, WIZ_CLAN, 0, "%s has been forced from the order of %s.",
			 gch->name, order->name	 );
		talk_channel( gch, "&MINFO: $n has angered $g and has been exiled!",
			      CHANNEL_INFO, "INFO" );
		remove_from_clan( gch );		/* easier this way */
		gch->pcdata->clan	= order;
		gch->pcdata->clan_rank	= RANK_EXILED;
		order->members++;
		add_karma( order, -100 );
	    }
	    else if( !xIS_SET( gch->act, PLR_OUTLAW ) )
		talk_channel( gch, "&MINFO: $n has angered $g and is now an outlaw",
			      CHANNEL_INFO, "INFO" );
	}
    }

    bonus = 1.0;
    xp = 4000 - URANGE( -10, gch->level - victim->level, 6 ) * 950;
    xp = xp - ( xp / 2 ) + ( xp * 500 / get_curr_resil( victim ) );

    if( gch->level < 10 )
	bonus += (float)1 / 2;

    if( xIS_SET( victim->affected_by, AFF_SANCTUARY )
	|| IS_SET( race_table[victim->race].race_abilities, RACE_SANCT ) )
	bonus += (float)1 / 2;
    else if( IS_NPC( victim )
	     && xIS_SET( victim->pIndexData->affected_by, AFF_SANCTUARY ) )
	bonus += (float)1 / 3;

    if( IS_AFFECTED( victim, AFF_ATTACKSHIELD ) )
	bonus += (float)2 / 5;

    if( get_eq_char( victim, WEAR_WIELD_R ) )
	bonus += (float)1 / 4;

    if( get_eq_char( victim, WEAR_WIELD_DOUBLE ) )
	bonus += (float)1 / 3;

    if( get_eq_char( victim, WEAR_WIELD_L ) )
	bonus += (float)1 / 5;

    if( victim->race == gch->race )
	bonus -= (float)1 / 8;

    if( IS_NPC( victim ) )
    {
	if( victim->class >= 0 )
	{
	    if( victim->class < AVAIL_CLASS )
		bonus += (float)1/3;
	    else
		bonus += (float)2/5;
	}

	if( xIS_SET( victim->pIndexData->act, ACT_BURIED ) )
	    bonus += (float)1 / 7;

	if( xIS_SET( victim->pIndexData->affected_by, AFF_WARP_FLESH ) )
	    bonus -= (float)1 / 3;

	if( xIS_SET( victim->pIndexData->affected_by, AFF_MIND_MIST ) )
	    bonus -= (float)1 / 4;

	if( xIS_SET( victim->act, ACT_AGGRESSIVE ) )
	    bonus += (float)1 / 20;

	if( victim->pIndexData->pShop != 0 )
	    bonus -= (float)1 / 4;

	if( victim->spec_fun != 0 && spec_table[victim->spec_fun].prcnt_xp )
	    bonus += (float)spec_table[victim->spec_fun].prcnt_xp / 100;
    }
    else
    {
	if( !xIS_SET( victim->act, PLR_BATTLE ) )
	    bonus = 0.0;
	else
	    bonus *= (float)2;
    }
    if( dam >= 0 )
    {
	dam = UMIN( dam, victim->hit );
	bonus *= (float)dam / victim->max_hit;
    }
    if( gch->class == CLASS_BUILDER )
	bonus /= 5;
    xp = (int)( xp * bonus );

    /*
     * Adjust for popularity of target:
     *	 -1/8 for each target over  'par' ( down to - 50% )
     *	 +1/8 for each target under 'par' (  up to + 25% )
     */
    if( IS_NPC( victim ) )
    {
	level = URANGE( 0, victim->level, MAX_LEVEL - 1 );
	number = UMAX( 1, kill_table[level].number );
	extra = victim->pIndexData->killed - kill_table[level].killed
	    / number;
	xp -= xp * URANGE( -2, extra, 4 ) / 8;
    }

    xp = number_range( xp * 3 / 4, xp * 5 / 4 );
    xp = UMAX( 0, xp );

    if( !IS_NPC( victim ) )
	xp = UMIN( xp, 50000 );

    return xp;
}


char *xp_str( char *outstr, const int xp )
{
    if( xp < 0 )
	return strcpy( outstr, "" );
    if( xp >= 2500 )
	sprintf( outstr, " &g(&c%d&gxp)&n", xp / 100 );
    else
	sprintf( outstr, " &g(&c%d.%2.2d&gxp)&n", xp / 100, xp % 100 );
    return outstr;
}


char *dam_amount( char *outstr, CHAR_DATA *ch, const int dam )
{
    if( !IS_IMMORTAL( ch ) )
	return strcpy( outstr, "" );
    sprintf( outstr, " &r<%d>&n", dam );
    return outstr;
}


void dam_message( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dt,
		  int wpn )
{
    static const char *const him_her[] = { "it", "him", "her" };
    const char *vp;
    const char *attack;
    char *point;
    char buf[MAX_STRING_LENGTH];
    char buf1[256];
    char buf2[256];
    char buf3[256];
    char buf4[256];
    char buf5[256];
    char punct;
    CHAR_DATA *vch;
    int xp;
    int members, member_levels;

    /* All the bulk is in the dam table now. */
    for( xp = 0; dam_table[xp].max_dam >= 0; xp++ )
    {
	if( dam_table[xp].max_dam >= dam )
	    break;
    }
    if( !dam_table[xp].mesg )
    {
	bug( "Unreal bug, no damage message for %d dam.", dam );
	return;
    }
    if( dt >= 0 && dt <= MAX_SKILL
	&& IS_SET( skill_table[dt].skill_type, SKILL_TYPE_FIRE ) )
	vp = dam_table[xp].fire_mesg;
    else if( dt >= 0 && dt <= MAX_SKILL
	     && IS_SET( skill_table[dt].skill_type, SKILL_TYPE_ICE ) )
	vp = dam_table[xp].ice_mesg;
    else
	vp = dam_table[xp].mesg;

    punct = ( dam <= UMAX( ch->level * 3 / 2, 25 ) ) ? '.' : '!';

    if( dt == TYPE_HIT )
    {
	if( ch->race > MAX_RACE )
	{
	    bug( "Dam_message:  %d invalid race", ch->race );
	    ch->race = 0;
	}

	attack = race_table[ch->race].dmg_message;
    }
    else
    {
	if( dt >= 0 && dt < MAX_SKILL )
	    attack = skill_table[dt].noun_damage;
	else if( dt >= TYPE_HIT )
	{
	    xp = dt - TYPE_HIT;
	    attack = flag_string( weapon_flags, &xp );
	}
	else
	{
	    bug( "Dam_message: bad dt %d caused by %s.", dt,
		       ch->name );
	    dt = TYPE_HIT;
	    attack = "hit";
	}
    }

    sprintf( buf1, "Your %s %s %%s&n%c%%s%%s\n\r", attack, vp, punct );
    sprintf( buf2, "%%s&n's %s %s you%c%%s\n\r", attack, vp, punct );
    sprintf( buf3, "%%s&n's %s %s %%s&n%c%%s%%s\n\r", attack, vp, punct );
    sprintf( buf4, "Your %s %s you%c%%s\n\r", attack, vp, punct );
    sprintf( buf5, "%%s&n's %s %s %%s%c%%s%%s\n\r", attack, vp, punct );


    members = 0;
    member_levels = 0;
    if( ch != victim )
    {
	for( vch = ch->in_room->people; vch; vch = vch->next_in_room )
	{
	    if( !IS_AWAKE( vch ) )
		continue;
	    if( is_same_group( vch, ch ) )
	    {
		members++;
		member_levels += vch->level;
	    }
	}

	if( members == 0 || member_levels == 0 )
	{
	    bug( "dam_message: members-%d levels-%d",
		       members, member_levels );
	    members = 1;
	    member_levels = ch->level;
	}
    }

    for( vch = ch->in_room->people; vch; vch = vch->next_in_room )
    {
	char tmpxp[25], tmpdam[25];

	if( !IS_AWAKE( vch ) )
	    continue;
	xp = -1;
	if( ch != victim )
	{
	    if( !IS_NPC( vch ) && is_same_group( vch, ch ) )
	    {
		xp = xp_compute( vch, victim, dam ) * vch->level / member_levels;
		xp += xp * members / 10;
		if( vch == ch )
		    xp += xp / 12;
	    }

	    if( xp > 0 )
	    {
		gain_exp( vch, xp );
		vch->tot_xp += xp;
	    }
	}

	if( ch->fighting == victim && dt != gsn_throw_weapon )
	{
	    if( !vch->desc
		|| ( ( vch == ch || vch == victim ) &&
		     xIS_SET( CH( vch->desc )->act, PLR_BATTLESELF ) )
		|| ( ( vch != ch && vch != victim ) &&
		     xIS_SET( CH( vch->desc )->act, PLR_BATTLEOTHER ) ) )
		continue;
	}

	if( vch == ch )
	{
	    if( ch == victim )
		sprintf( buf, buf4, dam_amount( tmpdam, vch, dam ) );
	    else
		sprintf( buf, buf1, PERS( victim, vch ),
			 xp_str( tmpxp, xp ), dam_amount( tmpdam, vch, dam ) );
	}
	else if( vch == victim )
	    sprintf( buf, buf2, PERS( ch, vch ),
		     dam_amount( tmpdam, vch, dam ) );
	else if( ch == victim )
	    sprintf( buf, buf5, PERS( ch, vch ),
		     him_her[URANGE( 0, ch->sex, 2 )],
		     xp_str( tmpxp, xp ), dam_amount( tmpdam, vch, dam ) );
	else
	    sprintf( buf, buf3, PERS( ch, vch ),
		     PERS( victim, vch ),
		     xp_str( tmpxp, xp ), dam_amount( tmpdam, vch, dam ) );

	point = &buf[0];
	while( *point == '&' )
	    point += 2;
	*point = UPPER( *point );
	send_to_char( buf, vch );
    }

    if( ch->fighting == victim && dt != gsn_throw_weapon )
    {
	if( dam > 0 )
	{
	    ch->num_hits++;
	    ch->tot_dam += dam;
	}
	else
	    ch->num_hits = UMAX( ch->num_hits, 0 );
    }

    return;
}


/*
 * Disarm a creature.
 * Caller must check for successful attack.
 */
bool disarm( CHAR_DATA *ch, CHAR_DATA *victim )
{
    OBJ_DATA *obj;

    if( ( get_size( ch ) - get_size( victim ) )
	< -20 + number_range( -40, 40 ) )
	return FALSE;

    if( !( obj = get_eq_char( victim, WEAR_WIELD_R ) ) )
	if( !( obj = get_eq_char( victim, WEAR_WIELD_L ) ) )
	    if( !( obj = get_eq_char( victim, WEAR_WIELD_DOUBLE ) ) )
		return FALSE;

    if( IS_SET( obj->extra_flags, ITEM_NOREMOVE )
	|| IS_SET( obj->extra_flags, ITEM_NODROP ) )
	return FALSE;

    if( !get_eq_char( ch, WEAR_WIELD_R )
	&& !get_eq_char( ch, WEAR_WIELD_L )
	&& !get_eq_char( ch, WEAR_WIELD_DOUBLE )
	&& ch->class != CLASS_MARTIAL_ARTIST
	&& get_first_class( ch ) != CLASS_MARTIAL_ARTIST
	&& get_second_class( ch ) != CLASS_MARTIAL_ARTIST
	&& number_bits( 3 ) != 0 )
	return FALSE;

    act( "You disarm $N!", ch, NULL, victim, TO_CHAR );
    act( "$n DISARMS you!", ch, NULL, victim, TO_VICT );
    act( "$n DISARMS $N!", ch, NULL, victim, TO_NOTVICT );

    obj_from_char( obj );
    if( IS_NPC( victim ) )
    {
	switch( number_bits( 2 ) )
	{
	case 0:
	    obj_to_room( obj, ch->in_room );
	    break;
	case 1: case 2:
	    act( "$n retrieves the weapon.", victim, NULL, NULL, TO_ROOM );
	    obj_to_char( obj, victim );
	    break;
	default:
	    act( "$n retrieves the weapon.", victim, NULL, NULL, TO_ROOM );
	    obj_to_char( obj, victim );
	    do_wear( victim, obj->name );
	    break;
	}
    }
    else
	obj_to_room( obj, victim->in_room );

    return TRUE;
}



/*
 * Trip a creature.
 * Caller must check for successful attack.
 */
void trip( CHAR_DATA *ch, CHAR_DATA *victim )
{
    if( IS_SET( victim->body_parts, BODY_PART_WINGS )
	|| xIS_SET( ch->affected_by, AFF_FLYING ) )
	return;

    if( victim->wait == 0 )
    {
	act( "$n trips $N and $N goes down!", ch, NULL, victim, TO_ALL );

	WAIT_STATE( ch, 2 * PULSE_VIOLENCE );
	WAIT_STATE( victim, 2 * PULSE_VIOLENCE );
	victim->position = POS_RESTING;
    }

    return;
}


/*
 * At the moment this is for all those trapped in a snare or web (AFF_HOLD)
 * they will struggle to free themselves and in the process hurt themselves,
 * an NPC who breaks free will flee if it is fighting.
 * -- Symposium
 */
void web_update( CHAR_DATA *ch )
{
    int dam;

    if( !IS_AFFECTED( ch, AFF_HOLD ) )
	return;
    if( IS_AFFECTED( ch, AFF_PASS_DOOR ) || IS_AFFECTED( ch, AFF_GHOUL )
	|| power( number_percent( ), 5, 20 - get_curr_str( ch ) ) < 9 )
    {
	affect_strip( ch, gsn_snare );
	affect_strip( ch, gsn_web );
	affect_strip( ch, gsn_strangle );
	xREMOVE_BIT( ch->affected_by, AFF_HOLD );
	if( IS_AFFECTED( ch, AFF_PASS_DOOR ) || IS_AFFECTED( ch, AFF_GHOUL ) )
	    act( "$o bonds pass straight through $m as $e move$%.",
		 ch, NULL, NULL, TO_ALL );
	else
	    act( "$n break$% free of the entanglement.",
		 ch, NULL, NULL, TO_ALL );
	if( IS_NPC( ch ) && xIS_SET( ch->act, ACT_WIMPY )
	    && ch->fighting && ch->hit < ch->max_hit / 4 )
	    do_flee( ch, "" );
    }
    else
    {
	dam = 5 + ( ch->level / 2 ) + ( ch->hit / 35 );
	dam = number_range( dam / 3, dam * 2 / 3 );
	act( "$n struggle$% in the web.", ch, NULL, NULL, TO_ALL );
	damage( ch, ch, dam, gsn_web, WEAR_NONE );
	create_char_event( ch, evn_web_struggle,
			   number_range( PULSE_PER_SECOND * 18,
					 PULSE_PER_SECOND * 27 ) );
    }
    return;
}