/***************************************************************************
 *  File: fight.c , Combat module.                         Part of DIKUMUD *
 *  Usage: Combat system and messages.                                     *
 *  Copyright (C) 1990, 1991 - see 'license.doc' for complete information. *
 *                                                                         *
 *  Copyright (C) 1992, 1993 Michael Chastain, Michael Quan, Mitchell Tse  *
 *  Performance optimization and bug fixes by MERC Industries.             *
 *  You can use our stuff in any way you like whatsoever so long as this   *
 *  copyright notice remains intact.  If you like it please drop a line    *
 *  to mec@garnet.berkeley.edu.                                            *
 *                                                                         *
 *  This is free software and you are benefitting.  We hope that you       *
 *  share your changes too.  What goes around, comes around.               *
 ***************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>

#include "structs.h"
#include "mob.h"
#include "obj.h"
#include "utils.h"
#include "handler.h"
#include "interp.h"
#include "db.h"
#include "spells.h"

struct char_data *combat_list       = NULL;
struct char_data *combat_next_dude  = NULL;

extern struct room_data *world;
extern struct message_list fight_messages[MAX_MESSAGES];
extern struct obj_data  *object_list;

void stop_follower(struct char_data *ch);
void hit(struct char_data *ch, struct char_data *victim, int type);
bool is_in_safe(struct char_data *ch, struct char_data *victim);
bool is_first_level(struct char_data *ch, struct char_data *victim);

void dam_message(int dam, struct char_data *ch, struct char_data *victim,
		 int w_type);
void group_gain( struct char_data *ch, struct char_data *victim );
bool check_parry( struct char_data *ch, struct char_data *victim );
bool check_dodge( struct char_data *ch, struct char_data *victim );
void disarm( struct char_data *ch, struct char_data *victim );
void trip( struct char_data *ch, struct char_data *victim );

/*
 * Control the fights going on.
 * Called periodically by the main loop.
 */
void perform_violence( void )
{
    struct char_data *ch;

    for (ch = combat_list; ch; ch = combat_next_dude )
    {
	combat_next_dude = ch->next_fighting;
	assert( ch->specials.fighting );

	if ( AWAKE(ch) && ch->in_room == ch->specials.fighting->in_room )
	    hit( ch, ch->specials.fighting, TYPE_UNDEFINED );
	else
	    stop_fighting( ch );
    }
}



/*
 * Do one group of attacks.
 */
void hit( struct char_data *ch, struct char_data *victim, int type )
{
    int     chance;

    /*
     * First attack.
     */
    one_hit( ch, victim, type );

    /*
     * Second attack.
     */
    chance  = IS_NPC(ch) ? 2 * GET_LEVEL(ch)
                         : ch->skills[SKILL_SECOND_ATTACK].learned * 2 / 5;
    if ( number(1, 100) < chance )
	one_hit( ch, victim, type );

    /*
     * Third attack.
     */
    chance  = IS_NPC(ch) ? GET_LEVEL(ch)
			 : ch->skills[SKILL_THIRD_ATTACK].learned / 5;
    if ( number(1, 100) < chance )
	one_hit( ch, victim, type );

    /*
     * Fourth attack.
     */
    chance  = IS_NPC(ch) ? GET_LEVEL(ch) * 2 / 3 : 0;
    if ( number(1, 100) < chance )
	one_hit( ch, victim, type );
}



/*
 * Hit one guy once.
 */
void one_hit( struct char_data *ch, struct char_data *victim, int type )
{
    struct obj_data *wielded;
    int w_type;
    int victim_ac, calc_thaco;
    int dam;
    byte diceroll;

    extern int thaco[4][36];
    extern byte backstab_mult[];
    extern struct str_app_type str_app[];
    extern struct dex_app_type dex_app[];

    /*
     * Can't beat a dead char!
     */
    if ( GET_POS(victim) == POSITION_DEAD )
	return;

    /*
     * This happens when people flee et cetera during multi attacks.
     */
    if ( ch->in_room != victim->in_room )
	return;
      
    /*
     * Figure out the type of damage message.
     */
    wielded = ch->equipment[WIELD];
    w_type  = TYPE_HIT;
    if ( wielded && wielded->obj_flags.type_flag == ITEM_WEAPON )
    {
	switch ( wielded->obj_flags.value[3] )
	{
	case 0: case 1: case 2:             w_type = TYPE_WHIP;     break;
	case 3:                             w_type = TYPE_SLASH;    break;
	case 4: case 5: case 6:             w_type = TYPE_CRUSH;    break;
	case 7:                             w_type = TYPE_BLUDGEON; break;
	case 8: case 9: case 10: case 11:   w_type = TYPE_PIERCE;   break;
	}
    }
    if ( type == SKILL_BACKSTAB )
	w_type  = SKILL_BACKSTAB;

    /*
     * Calculate to-hit-armor-class-0 versus armor class.
     * thaco for monsters is set in hitroll.
     */
    if ( !IS_NPC(ch) )
	calc_thaco  = thaco[(int) GET_CLASS(ch)-1][(int) GET_LEVEL(ch)];
    else
	calc_thaco  = 20;
    
    calc_thaco  -= str_app[STRENGTH_APPLY_INDEX(ch)].tohit;
    calc_thaco  -= GET_HITROLL(ch);

    victim_ac   = GET_AC(victim)/10;
    if ( AWAKE(victim) )
	victim_ac += dex_app[GET_DEX(victim)].defensive;
    victim_ac   = MAX( -10, victim_ac );

    /*
     * The moment of excitement!
     */
    diceroll    = number(1, 20);

    if ( diceroll < 20 && AWAKE(victim)
    &&  (diceroll == 1 || diceroll < calc_thaco - victim_ac) )
    {
	/* Miss. */
	damage( ch, victim, 0, w_type );
	return;
    }

    /*
     * Hit.
     * Calc damage.
     */
    if ( wielded )
	dam = dice( wielded->obj_flags.value[1], wielded->obj_flags.value[2] );
    else if ( IS_NPC(ch) )
	dam = dice( ch->specials.damnodice, ch->specials.damsizedice );
    else
	dam = number( 0, 2 );

    /*
     * Bonuses.
     */
    dam += str_app[STRENGTH_APPLY_INDEX(ch)].todam;
    dam += GET_DAMROLL(ch);

    /*
     * Bonus for hitting weak opponents.
     * Bonus for backstabbing.
     */
    if ( GET_POS(victim) < POSITION_FIGHTING )
	dam *= 1 + ( 2 * ( POSITION_FIGHTING - GET_POS(victim) ) ) / 3;

    if ( type == SKILL_BACKSTAB )
      {
	dam *= backstab_mult[(int) GET_LEVEL(ch)];
	type = TYPE_UNDEFINED;    /* Prevents multi backstabbing/combat */
	                          /*                            -Kahn   */
      }

    if ( dam < 1 )
	dam = 1;

    damage( ch, victim, dam, w_type );
    return;
}



/*
 * Inflict damage from a hit.
 */
void damage( struct char_data *ch, struct char_data *victim,
	    int dam, int attacktype )
{
    struct message_type *messages;
    int i, j, nr, max_hit;

    int hit_limit(struct char_data *ch);

    if ( GET_POS(victim) == POSITION_DEAD )
	return;

    /*
     * Can't hurt god, but he likes to see the messages.
     */
    if ( GET_LEVEL(victim) >= 32 && !IS_NPC(victim) )
	dam = 0;

    /*
     * Certain attacks are forbidden.
     */
    if ( victim != ch )
    {
	if ( is_in_safe( ch, victim ) )
	    return;
	if ( is_first_level( ch, victim ) )
	    return;
	check_killer( ch, victim );
    }

    /*
     * An eye for an eye, a tooth for a tooth, a life for a life.
     */
    if ( GET_POS(victim) > POSITION_STUNNED && ch != victim )
    {
	if ( !victim->specials.fighting )
	    set_fighting( victim, ch );
	GET_POS(victim) = POSITION_FIGHTING;
    }

    if ( GET_POS(ch) > POSITION_STUNNED && ch != victim )
    {
	if ( !ch->specials.fighting )
	    set_fighting( ch, victim );

	/*
	 * If victim is charmed, ch might attack victim's master.
	 */
	if ( IS_NPC(ch) && IS_NPC(victim) && IS_AFFECTED(victim, AFF_CHARM)
	&& victim->master != NULL && victim->master->in_room == ch->in_room
	&& number(0,9) == 0 )
	{
	    if ( ch->specials.fighting )
		stop_fighting(ch);
	    hit( ch, victim->master, TYPE_UNDEFINED );
	    return;
	}
    }

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

    if ( IS_AFFECTED( ch, AFF_CHARM ) )
	stop_follower( ch );
	    
    /*
     * Inviso attacks.  Not.
     */
    if ( IS_AFFECTED(ch, AFF_INVISIBLE) )
    {
	act( "$n slowly fades into existence.", FALSE, ch, 0, 0, TO_ROOM );

	if ( affected_by_spell( ch, SPELL_INVISIBLE ) )
	    affect_from_char( ch, SPELL_INVISIBLE );

	REMOVE_BIT( ch->specials.affected_by, AFF_INVISIBLE );
	ch->specials.wizInvis = FALSE;
    }

    /*
     * Damage modifiers.
     */
    if ( IS_AFFECTED(victim, AFF_SANCTUARY) )
	dam /= 2;

    if ( IS_AFFECTED(victim, AFF_PROTECT_EVIL) && GET_ALIGNMENT(ch) < -350 )
	dam /= 2;

    if ( dam < 0 )
	dam = 0;

    /*
     * Check for parry, mob disarm, and trip.
     * Print a suitable damage message.
     */
    if ( attacktype >= TYPE_HIT && attacktype < TYPE_SUFFERING )
    {
	if ( IS_NPC(ch) && number( 1, 100 ) < GET_LEVEL(ch) / 2 )
	    disarm( ch, victim );
	if ( IS_NPC(ch) && number( 1, 100 ) < GET_LEVEL(ch) / 2 )
	    trip( ch, victim );
	if ( check_parry( ch, victim ) )
	    return;
	if ( check_dodge( ch, victim ) )
	    return;
	if ( ch->equipment[WIELD] == NULL )
	    dam_message(dam, ch, victim, TYPE_HIT);
	else
	    dam_message(dam, ch, victim, attacktype);
    }
    else
    {
	for ( i = 0; i < MAX_MESSAGES; i++ )
	{
	    if ( fight_messages[i].a_type != attacktype )
		continue;

	    nr  = dice( 1, fight_messages[i].number_of_attacks );
	    j   = 1;
	    for ( messages = fight_messages[i].msg; j < nr && messages; j++ )
		messages = messages->next;

	    if ( !IS_NPC(victim) && GET_LEVEL(victim) > 31)
	    {
		act( messages->god_msg.attacker_msg,
		    FALSE, ch, ch->equipment[WIELD], victim, TO_CHAR );
		act( messages->god_msg.victim_msg,
		    FALSE, ch, ch->equipment[WIELD], victim, TO_VICT );
		act( messages->god_msg.room_msg,
		    FALSE, ch, ch->equipment[WIELD], victim, TO_NOTVICT );
	    }
	    else if ( dam == 0 )
	    {
		act( messages->miss_msg.attacker_msg,
		    FALSE, ch, ch->equipment[WIELD], victim, TO_CHAR );
		act( messages->miss_msg.victim_msg,
		    FALSE, ch, ch->equipment[WIELD], victim, TO_VICT );
		act( messages->miss_msg.room_msg,
		    FALSE, ch, ch->equipment[WIELD], victim, TO_NOTVICT );
	    }
	    else if ( GET_POS(victim) == POSITION_DEAD )
	    {
		act( messages->die_msg.attacker_msg,
		    FALSE, ch, ch->equipment[WIELD], victim, TO_CHAR );
		act( messages->die_msg.victim_msg,
		    FALSE, ch, ch->equipment[WIELD], victim, TO_VICT );
		act( messages->die_msg.room_msg,
		    FALSE, ch, ch->equipment[WIELD], victim, TO_NOTVICT );
	    }
	    else
	    {
		act( messages->hit_msg.attacker_msg,
		    FALSE, ch, ch->equipment[WIELD], victim, TO_CHAR );
		act( messages->hit_msg.victim_msg,
		    FALSE, ch, ch->equipment[WIELD], victim, TO_VICT );
		act( messages->hit_msg.room_msg,
		    FALSE, ch, ch->equipment[WIELD], victim, TO_NOTVICT );
	    }
	}
    }

    /*
     * Hurt the victim.
     * Reward the perpetrator.
     * Life is hard.
     */
    GET_HIT(victim) -= dam;
    update_pos( victim );
    if ( ch != victim )
	gain_exp( ch, GET_LEVEL(victim) * dam );

    /*
     * Inform the victim of his new state.
     * Use send_to_char, because act() doesn't send to DEAD people.
     */
    switch( GET_POS(victim) )
    {
    case POSITION_MORTALLYW:
	act( "$n is mortally wounded, and will die soon, if not aided.",
	    TRUE, victim, 0, 0, TO_ROOM );
	send_to_char( 
	    "You are mortally wounded, and will die soon, if not aided.\n\r",
	    victim );
	break;

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

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

    case POSITION_DEAD:
	act( "$n is DEAD!!", TRUE, victim, 0, 0, TO_ROOM );
	send_to_char( "You have been KILLED!!\n\r\n\r", victim );
	break;

    default:
	max_hit = hit_limit( victim );
	if ( dam > max_hit / 5 )
	    send_to_char( "That really did HURT!\n\r", victim );

	/*
	 * Wimp out?
	 */
	if ( GET_HIT(victim) < max_hit/5 )
	{
	    send_to_char( "You wish you would stop BLEEDING so much!\n\r",
		victim );
	    if ( IS_NPC(victim) )
	    {
		if ( IS_SET(victim->specials.act, ACT_WIMPY) )
		{
		    do_flee( victim, "", 0 );
		    return;
		}
	    }
	    else
	    {
		if ( IS_AFFECTED(victim, AFF_WIMPY) )
		{
		    do_flee( victim, "", 0 );
		    return;
		}
	    }
	}
	break;
    }

    /*
     * Take care of link dead people.
     */
    if ( !IS_NPC(victim) && victim->desc == NULL )
    {
	do_flee( victim, "", 0 );
	if ( !victim->specials.fighting )
	{
	    act( "$n is rescued by divine forces.",
		FALSE, victim, 0, 0, TO_ROOM );
	    victim->specials.was_in_room = victim->in_room;
	    char_from_room(victim);
	    char_to_room(victim, 0);
	}
	return;
    }

    /*
     * Sleep spells.
     */
    if ( !AWAKE(victim) )
    {
	if ( victim->specials.fighting )
	    stop_fighting( victim );
    }

    /*
     * Payoff for killing things.
     */
    if ( GET_POS(victim) == POSITION_DEAD )
    {
	if ( IS_NPC(victim) || victim->desc != NULL )
	    group_gain( ch, victim );

	if ( !IS_NPC(victim) )
	{
	    sprintf( log_buf, "%s killed by %s at %d",
		GET_NAME(victim),
		(IS_NPC(ch) ? ch->player.short_descr : GET_NAME(ch)),
		world[victim->in_room].number );
	    log( log_buf );

	    if ( GET_EXP(victim) > exp_table[(int) GET_LEVEL(victim)] )
	    {
		GET_EXP(victim) = ( GET_EXP(victim)
		                + exp_table[(int) GET_LEVEL(victim)] ) / 2;
	    }
	}
	raw_kill( victim );
	return;
    }

    return;
}



/*
 * See if an attack justifies a KILLER flag.
 */
void check_killer( struct char_data *ch, struct char_data *victim )
{
    /*
     * No attack in safe room anyways.
     */
    if (IS_SET( world[ch->in_room].room_flags, SAFE) )
	return;
    
    /*
     * NPC's are fair game.
     * So are killers and thieves.
     */
    if ( IS_NPC(victim) )
	return;
    if ( IS_SET(victim->specials.affected_by, AFF_KILLER) )
	return;
    if ( IS_SET(victim->specials.affected_by, AFF_THIEF) )
	return;

    /*
     * Charm-o-rama.
     */
    if ( IS_SET(ch->specials.affected_by, AFF_CHARM) )
    {
	if ( ch->master == NULL )
	{
	    sprintf( log_buf, "Check_killer: %s bad AFF_CHARM",
		IS_NPC(ch) ? ch->player.short_descr : GET_NAME(ch) );
	    log( log_buf );
	    affect_from_char( ch, SPELL_CHARM_PERSON );
	    REMOVE_BIT( ch->specials.affected_by, AFF_CHARM );
	    return;
	}

	send_to_char( "*** You are now a KILLER!! ***\n\r", ch->master );
	SET_BIT(ch->master->specials.affected_by, AFF_KILLER);
	stop_follower(ch);
	return;
    }

    /*
     * NPC's are cool of course (as long as not charmed).
     * Hitting yourself is cool too (bleeding).
     * And current killers stay as they are.
     */
    if ( IS_NPC(ch) || ch == victim )
	return;

    if ( IS_SET(ch->specials.affected_by, AFF_KILLER) )
	return;

    send_to_char( "*** You are now a KILLER!! ***\n\r", ch );
    SET_BIT(ch->specials.affected_by, AFF_KILLER);
    return;
}

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

    if ( victim->equipment[WIELD] == NULL )
	return FALSE;

    if ( ch->equipment[WIELD] == NULL && number ( 1, 101 ) >= 50 )
        return FALSE;

    if ( IS_NPC(victim) )
	chance	= MIN( 60, 2 * GET_LEVEL(ch) );
    else
	chance	= victim->skills[SKILL_PARRY].learned / 2;

    percent = number(1, 101) - (GET_LEVEL(victim) - GET_LEVEL(ch));
    if ( percent >= chance )
	return FALSE;

    act( "$n parries $N's attack.", FALSE, victim, NULL, ch, TO_NOTVICT );
    act( "$n parries your attack.", FALSE, victim, NULL, ch, TO_VICT );
    act( "You parry $N's attack.",  FALSE, victim, NULL, ch, TO_CHAR );
    return TRUE;
}

/*
 * Check for dodge.
 */
bool check_dodge( struct char_data *ch, struct char_data *victim )
{
    int percent;
    int chance;

    if ( IS_NPC(victim) )
        chance  = MIN( 60, 2 * GET_LEVEL(ch) );
    else
        chance  = victim->skills[SKILL_DODGE].learned / 2;

    percent = number(1, 101) - (GET_LEVEL(victim) - GET_LEVEL(ch));
    if ( percent >= chance )
        return FALSE;

    act( "$n dodges $N's attack.", FALSE, victim, NULL, ch, TO_NOTVICT );
    act( "$n dodges your attack.", FALSE, victim, NULL, ch, TO_VICT );
    act( "You dodge $N's attack.", FALSE, victim, NULL, ch, TO_CHAR );
    return TRUE;
}


/*
 * Load fighting messages into memory.
 */
void load_messages(void)
{
    FILE *f1;
    int i,type;
    struct message_type *messages;
    char chk[100];

    if (!(f1 = fopen(MESS_FILE, "r"))){
	perror("read messages");
	exit(0);
    }

    for (i = 0; i < MAX_MESSAGES; i++)
    { 
	fight_messages[i].a_type = 0;
	fight_messages[i].number_of_attacks=0;
	fight_messages[i].msg = 0;
    }

    fscanf(f1, " %s \n", chk);

    while(*chk == 'M')
    {
	fscanf(f1," %d\n", &type);
	for (i = 0; (i < MAX_MESSAGES) && (fight_messages[i].a_type!=type) &&
	    (fight_messages[i].a_type); i++);
	if(i>=MAX_MESSAGES){
	    log("Too many combat messages.");
	    exit(0);
	}

	CREATE(messages,struct message_type,1);
	fight_messages[i].number_of_attacks++;
	fight_messages[i].a_type=type;
	messages->next=fight_messages[i].msg;
	fight_messages[i].msg=messages;

	messages->die_msg.attacker_msg      = fread_string(f1);
	messages->die_msg.victim_msg        = fread_string(f1);
	messages->die_msg.room_msg          = fread_string(f1);
	messages->miss_msg.attacker_msg     = fread_string(f1);
	messages->miss_msg.victim_msg       = fread_string(f1);
	messages->miss_msg.room_msg         = fread_string(f1);
	messages->hit_msg.attacker_msg      = fread_string(f1);
	messages->hit_msg.victim_msg        = fread_string(f1);
	messages->hit_msg.room_msg          = fread_string(f1);
	messages->god_msg.attacker_msg      = fread_string(f1);
	messages->god_msg.victim_msg        = fread_string(f1);
	messages->god_msg.room_msg          = fread_string(f1);
	fscanf(f1, " %s \n", chk);
    }

    fclose(f1);
}



/*
 * Set position of a victim.
 */
void update_pos( struct char_data *victim )
{
    if ( GET_HIT(victim) > 0 )
    {
    	if ( GET_POS(victim) <= POSITION_STUNNED )
	    GET_POS(victim) = POSITION_STANDING;
	return;
    }

    if ( IS_NPC(victim) || GET_HIT(victim) <= -11 )
    {
	GET_POS(victim) = POSITION_DEAD;
	return;
    }

         if ( GET_HIT(victim) <= -6 ) GET_POS(victim) = POSITION_MORTALLYW;
    else if ( GET_HIT(victim) <= -3 ) GET_POS(victim) = POSITION_INCAP;
    else                              GET_POS(victim) = POSITION_STUNNED;

    return;
}



/*
 * Start fights.
 */
void set_fighting(struct char_data *ch, struct char_data *vict)
{
    assert(!ch->specials.fighting);

    ch->next_fighting = combat_list;
    combat_list = ch;

    if(IS_AFFECTED(ch,AFF_SLEEP))
	affect_from_char(ch,SPELL_SLEEP);

    ch->specials.fighting = vict;
    GET_POS(ch) = POSITION_FIGHTING;

    return;
}



/*
 * Stop fights.
 */
void stop_fighting(struct char_data *ch)
{
    struct char_data *tmp;

    assert(ch->specials.fighting);

    if (ch == combat_next_dude)
	combat_next_dude = ch->next_fighting;

    if (combat_list == ch)
       combat_list = ch->next_fighting;
    else
    {
	for (tmp = combat_list; tmp && (tmp->next_fighting != ch); 
	    tmp = tmp->next_fighting)
	    ;
	if (!tmp)
	{
	    log( "Stop_fighting: char not found" );
	    abort();
	}
	tmp->next_fighting = ch->next_fighting;
    }

    ch->next_fighting = 0;
    ch->specials.fighting = 0;
    GET_POS(ch) = POSITION_STANDING;
    update_pos(ch);

    return;
}



#define MAX_NPC_CORPSE_TIME 3
#define MAX_PC_CORPSE_TIME 30

void make_corpse(struct char_data *ch)
{
    struct obj_data *corpse, *o;
    struct obj_data *money; 
    char buf[MAX_STRING_LENGTH];
    int i;

    CREATE(corpse, struct obj_data, 1);
    clear_object(corpse);

    corpse->item_number	= NOWHERE;
    corpse->in_room	= NOWHERE;
    corpse->name	= str_dup("corpse");

    sprintf( buf, "Corpse of %s is lying here.", 
      (IS_NPC(ch) ? ch->player.short_descr : GET_NAME(ch)));
    corpse->description = str_dup(buf);

    sprintf( buf, "Corpse of %s",
      (IS_NPC(ch) ? ch->player.short_descr : GET_NAME(ch)));
    corpse->short_description = str_dup(buf);

    corpse->contains = ch->carrying;
    if ( IS_NPC(ch) && GET_GOLD(ch) > 0 )
    {
	money = create_money(GET_GOLD(ch));
	GET_GOLD(ch)=0;
	obj_to_obj(money, corpse);
    }

    corpse->obj_flags.type_flag    = ITEM_CONTAINER;
    corpse->obj_flags.wear_flags   = ITEM_TAKE;
    corpse->obj_flags.value[0]     = 0; /* You can't store stuff in a corpse */
    corpse->obj_flags.value[3]     = 1; /* corpse identifyer */
    corpse->obj_flags.weight       = GET_WEIGHT(ch)+IS_CARRYING_W(ch);
    corpse->obj_flags.eq_level     = 0;
    if (IS_NPC(ch))
    {
	corpse->obj_flags.cost_per_day = 100000;
	corpse->obj_flags.timer = MAX_NPC_CORPSE_TIME;
    }
    else
    {
	corpse->obj_flags.cost_per_day = 200000;
	corpse->obj_flags.timer = MAX_PC_CORPSE_TIME;
    }

    for ( i = 0; i < MAX_WEAR; i++ )
    {
	if ( ch->equipment[i] )
	    obj_to_obj( unequip_char(ch, i), corpse );
    }

    ch->carrying	= 0;
    IS_CARRYING_N(ch)	= 0;
    IS_CARRYING_W(ch)	= 0;

    corpse->next	= object_list;
    object_list		= corpse;

    for ( o = corpse->contains; o; o->in_obj = corpse, o = o->next_content )
	;

    object_list_new_owner( corpse, 0 );
    obj_to_room( corpse, ch->in_room );

    return;
}


/* When ch kills victim */
void change_alignment(struct char_data *ch, struct char_data *victim)
{
    int align;

    align   = GET_ALIGNMENT(ch) - GET_ALIGNMENT(victim);

    if ( align > 650 )
	GET_ALIGNMENT(ch) += (align - 650) / 4;
    else if ( align < -650 )
	GET_ALIGNMENT(ch) += (align + 650) / 4;
    else
	GET_ALIGNMENT(ch) /= 2;

    GET_ALIGNMENT(ch) = MIN( 1000, MAX( -1000, GET_ALIGNMENT(ch) ) );
}



void death_cry(struct char_data *ch)
{
    int door, was_in;
    char *message;

    act( "Your blood freezes as you hear $n's death cry.",
	FALSE, ch, 0, 0, TO_ROOM );

    if ( IS_NPC(ch) )
	message = "You hear something's death cry.";
    else
	message = "You hear someone's death cry.";

    was_in = ch->in_room;
    for ( door = 0; door <= 5; door++ )
    {
	if (CAN_GO(ch, door))
	{
	    ch->in_room = world[was_in].dir_option[door]->to_room;
	    if ( ch->in_room == was_in )
		continue;
	    act( message, FALSE, ch, 0, 0, TO_ROOM );
	    ch->in_room = was_in;
	}
    }
}



void raw_kill( struct char_data *ch )
{
    if ( ch->specials.fighting )
	stop_fighting( ch );

    death_cry( ch );
    make_corpse( ch );

    if ( IS_NPC(ch) )
    {
	extract_char( ch, TRUE );
	return;
    }

    extract_char( ch, FALSE );
    ch->specials.affected_by    = 0;
    GET_POS(ch)                 = POSITION_RESTING;
    while ( ch->affected )
	affect_remove( ch, ch->affected );

    if ( GET_HIT(ch) <= 0 )
	GET_HIT(ch) = 1;
    if ( GET_MOVE(ch) <= 0 )
	GET_MOVE(ch) = 1;
    if ( GET_MANA(ch) <= 0 )
	GET_MANA(ch) = 1;
    save_char_obj( ch );
}



void group_gain( struct char_data *ch, struct char_data *victim )
{
    char buf[256];
    int no_members, share;
    int totallevels;
    struct char_data *k;
    struct follow_type *f;

    /*
     * Monsters don't get kill xp's.
     * Dying of mortal wounds doesn't give xp to anyone!
     */
    if ( IS_NPC(ch) || ch == victim )
	return;
    
    if ( ( k = ch->master ) == NULL )
	k = ch;

    no_members  = 0;
    totallevels = 0;

    if ( IS_AFFECTED(k, AFF_GROUP) && k->in_room == ch->in_room )
    {
	no_members  += 1;
	totallevels += GET_LEVEL(k);
    }

    for ( f = k->followers; f; f = f->next )
    {
	if ( IS_AFFECTED(k, AFF_GROUP) && f->follower->in_room == ch->in_room )
	{
	    no_members  += 1;
	    totallevels += GET_LEVEL(f->follower);
	}
    }

    if ( no_members == 0 )
    {
	no_members  = 1;
	totallevels = GET_LEVEL(ch);
    }

    share   = GET_EXP(victim);
    share   += share * (no_members - 1) / 10;

    /*
     * Kludgy loop to get k in at end.
     */
    for ( f = k->followers; ; f = f->next )
    {
	struct char_data *  tmp_ch;
	int                 tmp_share;

	tmp_ch  = (f == NULL) ? k : f->follower;

	if ( tmp_ch->in_room != ch->in_room )
	    goto LContinue;

	if ( !IS_AFFECTED(tmp_ch, AFF_GROUP) && tmp_ch != ch )
	    goto LContinue;
	    
	if ( GET_LEVEL(tmp_ch) - GET_LEVEL(k) >= 6 )
	{
	    act( "You are too high for this group.  You gain no experience.",
		FALSE, tmp_ch, 0, 0, TO_CHAR );
	    goto LContinue;
	}

	if ( GET_LEVEL(tmp_ch) - GET_LEVEL(k) <= -6 )
	{
	    act( "You are too low for this group.  You gain no experience.",
		FALSE, tmp_ch, 0, 0, TO_CHAR );
	    goto LContinue;
	}

	tmp_share   = MIN( 250000, GET_LEVEL(tmp_ch) * share / totallevels );
	sprintf( buf, "You receive %d exps of %d total.\n\r",
	    tmp_share, share );
	send_to_char( buf, tmp_ch );
	gain_exp( tmp_ch, tmp_share );
	change_alignment( tmp_ch, victim );

 LContinue:
	if ( f == NULL )
	    break;
    }
}




void dam_message( int dam, struct char_data *ch, struct char_data *victim,
		 int w_type )
{
    static char *attack_table[] =
    {
	"hit", "pound", "pierce", "slash", "whip", "claw",
	"bite", "sting", "crush"
    };

    char buf1[256], buf2[256], buf3[256];
    char *vs, *vp;
    char *attack;
    char punct;

	 if ( dam ==  0 ) { vs  = "miss";           vp  = "misses";         }
    else if ( dam <=  4 ) { vs  = "hit";            vp  = "hits";           }
    else if ( dam <=  6 ) { vs  = "injure";         vp  = "injures";        }
    else if ( dam <=  8 ) { vs  = "wound";          vp  = "wounds";         }
    else if ( dam <= 11 ) { vs  = "decimate";       vp  = "decimates";      }
    else if ( dam <= 14 ) { vs  = "devastate";      vp  = "devastates";     }
    else if ( dam <= 17 ) { vs  = "maim";           vp  = "maims";          }
    else if ( dam <= 21 ) { vs  = "MUTILATE";       vp  = "MUTILATES";      }
    else if ( dam <= 25 ) { vs  = "DISMEMBER";      vp  = "DISMEMBERS";     }
    else if ( dam <= 29 ) { vs  = "DISEMBOWEL";     vp  = "DISEMBOWELS";    }
    else if ( dam <= 33 ) { vs  = "MASSACRE";       vp  = "MASSACRES";      }
    else                  { vs  = "*** DEMOLISH ***";
			    vp  = "*** DEMOLISHES ***";                     }

    w_type  -= TYPE_HIT;
    if ( w_type >= sizeof(attack_table)/sizeof(attack_table[0]) )
    {
	log( "Dam_message: bad w_type" );
	w_type  = 0;
    }
    punct   = (dam <= 8) ? '.' : '!';

    if ( w_type == 0 )
    {
	sprintf( buf1, "$n %s $N%c", vp, punct );
	sprintf( buf2, "You %s $N%c", vs, punct );
	sprintf( buf3, "$n %s you%c", vp, punct );
    }
    else
    {
	attack  = attack_table[w_type];
	sprintf( buf1, "$n's %s %s $N%c", attack, vp, punct );
	sprintf( buf2, "Your %s %s $N%c", attack, vp, punct );
	sprintf( buf3, "$n's %s %s you%c", attack, vp, punct );
    }

    act( buf1, FALSE, ch, NULL, victim, TO_NOTVICT );
    act( buf2, FALSE, ch, NULL, victim, TO_CHAR );
    act( buf3, FALSE, ch, NULL, victim, TO_VICT );
}



/*
 * Disarm a creature.
 * Caller must check for successful attack.
 */
void disarm( struct char_data *ch, struct char_data *victim )
{
    struct obj_data *obj;

    if ( victim->equipment[WIELD] == NULL )
	return;

    if ( ch->equipment[WIELD] == NULL && number ( 1, 101 ) >= 50 )
      return;

    act( "$n disarms you and sends your weapon flying!",
	FALSE, ch, NULL, victim, TO_VICT );
    act( "You disarm $N and send $S weapon flying!",
	FALSE, ch, NULL, victim, TO_CHAR );
    act( "$n disarms $N and sends $S weapon flying!",
	FALSE, ch, NULL, victim, TO_NOTVICT );

    obj = unequip_char( victim, WIELD );
    obj_to_room( obj, victim->in_room );
    return;
}



/*
 * Trip a creature.
 * Caller must check for successful attack.
 */
void trip( struct char_data *ch, struct char_data *victim )
{
    act( "$n trips you and you go down!",
        FALSE, ch, NULL, victim, TO_VICT );
    act( "You trip $N and $N goes down!",
        FALSE, ch, NULL, victim, TO_CHAR );
    act( "$n trips $N and $N goes down!",
        FALSE, ch, NULL, victim, TO_NOTVICT );

    damage( ch, victim, 1, SKILL_TRIP );
    WAIT_STATE( ch, PULSE_VIOLENCE*2 );
    WAIT_STATE( victim, PULSE_VIOLENCE*3 );
    GET_POS(victim) = POSITION_SITTING;

    return;
}