/* ************************************************************************
*  File: fight.c , Combat module.                         Part of DIKUMUD *
*  Usage: Combat system and messages.                                     *
*  Copyright (C) 1990, 1991 - see 'license.doc' for complete information. *
************************************************************************* */

#include <stdio.h>
#include <strings.h>
#include <assert.h>

#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "handler.h"
#include "interpreter.h"
#include "db.h"
#include "spells.h"

/* Structures */

struct char_data *combat_list = 0;	    /* head of l-list of fighting chars	*/
struct char_data *combat_next_dude = 0; /* Next dude global trick           */


/* External structures */

extern struct room_data *world;
extern struct zone_data *zone_table;
extern struct message_list fight_messages[MAX_MESSAGES];
extern struct obj_data  *object_list;
extern struct index_data *obj_index;

/* External procedures */

char *fread_string(FILE *f1);
void stop_follower(struct char_data *ch);
void do_flee(struct char_data *ch, char *argument, int cmd);
void hit(struct char_data *ch, struct char_data *victim, int type);
void forget(char *name, struct char_data *ch);
void remember(char *name, struct char_data *ch);
bool is_in_safe(struct char_data *ch, struct char_data *victim);
bool is_first_level(struct char_data *ch, struct char_data *victim);
bool nokill(struct char_data *ch, struct char_data *victim);
bool notwithinsixlevels(struct char_data *ch, struct char_data *victim);
bool isdweeb(struct char_data *ch);

/* Local procedures */

void death_level_handler(struct char_data *ch);
bool to_hit_char(struct char_data *ch,struct char_data *victim);

/* Weapon attack texts */
struct attack_hit_type attack_hit_text[] =
{
  {"hit",   "hits"},             /* TYPE_HIT      */
  {"pound", "pounds"},           /* TYPE_BLUDGEON */
  {"pierce", "pierces"},         /* TYPE_PIERCE   */
  {"slash", "slashes"},          /* TYPE_SLASH    */
  {"whip", "whips"},             /* TYPE_WHIP     */
  {"claw", "claws"},             /* TYPE_CLAW     */
  {"bite", "bites"},             /* TYPE_BITE     */
  {"sting", "stings"},           /* TYPE_STING    */
  {"crush", "crushes"}           /* TYPE_CRUSH    */
};




/* The Fight related routines */

void check_killer(struct char_data *ch, struct char_data *victim)
{
/* This routine sets the player's KILLER flag if he/she attacks another
** player and the victim is not a thief or a killer, and the attacker's
** killer flag is not yet set. It is OK to attack someone with either flag set.
** This routine is disabled in the arena (you're supposed to kill people
** in the arena!).
*/
	char buf[MAX_STRING_LENGTH];

	if ( IS_SET(world[ch->in_room].room_flags,ARENA)) {
		return;
	}

	if (    !IS_NPC(ch) 
	    &&  !IS_NPC(victim)
	    &&  (ch!=victim)
	    &&  !IS_SET(ch->specials.act,PLR_ISKILLER)
	    &&  !IS_TRUSTED(ch)
	    &&  !IS_SET(victim->specials.act,PLR_ISKILLER)
	    &&  !IS_SET(victim->specials.act,PLR_ISTHIEF)
	    &&  !IS_SET(victim->specials.act,PLR_ISDWEEB)
	    ) {

		send_to_char("** You are now an outlaw!! **\n\r",ch);
		SET_BIT(ch->specials.act, PLR_ISKILLER);
		sprintf(buf,"%s assigned killer flag for attacking %s",
			ch->player.name,victim->player.name);
		log(buf);
	}
}

void appear(struct char_data *ch)
{
  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);

  /*
   * Remove wizinvis too.
   */
  ch->specials.wizInvis = FALSE;
  
}



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);
}


void update_pos( struct char_data *victim )
{
	int dam=0;

	/* Handle hit point positions */
	if ((GET_HIT(victim) > 0) && (GET_POS(victim) > POSITION_STUNNED))
		;
	else if (GET_HIT(victim) > 0 )
		GET_POS(victim) = POSITION_STANDING;
	else if (GET_HIT(victim) <= -11)
		GET_POS(victim) = POSITION_DEAD;
	else 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;
}


/* start one char fighting another (yes, it is horrible, I know... )  */
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;
}



/* remove a char from the list of fighting chars */
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("Char fighting not found Error (fight.c, stop_fighting)");
			abort();
		}
		tmp->next_fighting = ch->next_fighting;
	}

	ch->next_fighting = 0;
	ch->specials.fighting = 0;
	if(IS_NPC(ch)) {
		GET_POS(ch) = ch->specials.default_pos;
	} else
		GET_POS(ch) = POSITION_STANDING; /* Default position? */
	if(world[ch->in_room].sector_type==SECT_NO_GROUND) {
		if(IS_AFFECTED(ch,AFF_FLY))
			GET_POS(ch)=POSITION_FLYING;
		else if(IS_AFFECTED(ch,AFF_LEVITATE))
			GET_POS(ch)=POSITION_LEVITATED;
	}
	update_pos(ch);
}



#define MAX_NPC_CORPSE_TIME 5
#define MAX_PC_CORPSE_TIME 10

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

	char *strdup(char *source);
	struct obj_data *create_money( int amount );

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

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

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

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

	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.cost_per_day = 100000;
	if (IS_NPC(ch))
		corpse->obj_flags.timer = MAX_NPC_CORPSE_TIME;
	else
		corpse->obj_flags.timer = MAX_PC_CORPSE_TIME;

	if(!IS_NPC(ch)&&IS_SET(world[ch->in_room].room_flags,ARENA)){
		obj_to_room(corpse, ch->in_room);
		corpse->next = object_list;
		object_list = corpse;
		return;

	} /* create an 'empty' token corpse in the arena */

	corpse->contains = ch->carrying;
	if(GET_GOLD(ch)>0)
	{
		money = create_money(GET_GOLD(ch));
		GET_GOLD(ch)=0;

		obj_to_obj(money,corpse);
	}

	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);

	if(ch->in_room==0 && ch->specials.was_in_room != NOWHERE)
		obj_to_room(corpse,ch->specials.was_in_room);
	else
		obj_to_room(corpse, ch->in_room);
	save_char(ch,NOWHERE);
	save_obj2(ch);
}


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

	if(IS_NPC(ch)) return;

	if ((align = GET_ALIGNMENT(ch)-GET_ALIGNMENT(victim)) > 0) {
		if (align > 650)
			GET_ALIGNMENT(ch) = MIN(1000,GET_ALIGNMENT(ch) + ((align-650) >> 3));
		else
			GET_ALIGNMENT(ch) >>= 1;
	} else {
		if (align < -650)
			GET_ALIGNMENT(ch) = MAX(-1000, GET_ALIGNMENT(ch) + ((align+650) >> 3));
		else
			GET_ALIGNMENT(ch) >>= 1;
	}

	for(i=0;i<MAX_WEAR;i++) {
		if(!(obj=ch->equipment[i]))
			continue;
		if(((IS_OBJ_STAT(obj, ITEM_ANTI_EVIL) && IS_EVIL(ch)) ||
		    (IS_OBJ_STAT(obj, ITEM_ANTI_GOOD) && IS_GOOD(ch)) ||
		    (IS_OBJ_STAT(obj, ITEM_ANTI_NEUTRAL) && IS_NEUTRAL(ch))) &&
		    (ch->in_room != NOWHERE)) {
			act("You are zapped by $p and instantly drop it.",FALSE,ch,obj,0,TO_CHAR);
			act("$n is zapped by $p and instantly drops it.",FALSE,ch,obj,0,TO_ROOM);
			obj=unequip_char(ch,i);
			obj_to_room(obj,ch->in_room);
		}
	}
}



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

	act("Your blood freezes as you hear $n's death cry.", FALSE, ch,0,0,TO_ROOM);
	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;
			act("Your blood freezes as you hear someones death cry.",FALSE,ch,0,0,TO_ROOM);
			ch->in_room = was_in;
		}
	}
}


extern struct index_data *mob_index;

void raw_kill(struct char_data *ch)
{
	char buf[MAX_STRING_LENGTH];

	if (ch->specials.fighting)
		stop_fighting(ch);

	death_cry(ch);
	if(IS_NPC(ch) && ((ch->specials.act & (ACT_SPEC_DIE | ACT_SPEC)) ==
					     (ACT_SPEC_DIE | ACT_SPEC))) {
		if(!mob_index[ch->nr].func) {
			sprintf(buf,"%s %d",ch->player.name,(int)ch->nr);
			log(buf);
			log("No special function for ACT_SPEC_DIE (raw_kill)");
			REMOVE_BIT(ch->specials.act, ACT_SPEC_DIE);
			make_corpse(ch);
		} else 
			(*mob_index[ch->nr].func)(ch,0,-1,"");
	} else
		make_corpse(ch);
	/* Restore PC who died in arena combat */
	if(!IS_NPC(ch)){
		if(IS_SET(world[ch->in_room].room_flags,ARENA)) {
			/* like remove his arena flag */
			if(ch->specials.arena > ARENA_NOTPLAYING){
				GET_HIT(ch) = ch->specials.arena_hits;
				GET_MOVE(ch)= ch->specials.arena_move;
				GET_MANA(ch)= ch->specials.arena_mana;
				ch->specials.arena=ARENA_NOTPLAYING;
				while(ch->affected)
					affect_remove(ch,ch->affected);
				ch->affected=ch->tmp_affected;
				ch->tmp_affected=NULL;
				ch->specials.affected_by=ch->specials.arena_affvector;
			} /* restore his previous stats */
			update_pos(ch);
			send_to_char("\n\r*** The power of the Arena Gods intervene and you are rescued by divine forces!\n\r",ch);
			/* send him to temple */
		/*spell_word_of_recall(20,ch,ch,0); old way now invalid*/
			act("$n is plucked out of space by the Arena Gods!",
				TRUE,ch,0,0,TO_ROOM);
			char_from_room(ch);
			/* Send 'em to the Decision Point */
			char_to_room(ch,real_room(3701),0);
			act("$n is dropped here by a giant, vague hand.",
				TRUE,ch,0,0,TO_ROOM);
			do_look(ch,"",15);
			return;	/* no extract necessary */
		}
	}
	extract_char(ch);
}



void die(struct char_data *ch)
{
	if(!IS_SET(world[ch->in_room].room_flags,ARENA)&&!IS_NPC(ch)) {
		gain_exp(ch, -(GET_EXP(ch)/2));
		death_level_handler(ch);
		raw_kill(ch);
		return;
	}
	raw_kill(ch);
}

void death_level_handler(struct char_data *ch)
{
	int oldlevel, i, j, levels_losing;

	if(!IS_NPC(ch)){
		/* Clear out the outlaw flag */
		REMOVE_BIT(ch->specials.act,PLR_OUTLAW);

		/* time to lose levels if you're a killer/thief */
		if(IS_SET(ch->specials.act, PLR_ISKILLER) ||
			   IS_SET(ch->specials.act, PLR_ISTHIEF)) {

		/* Multiple level loss as requested by Atlos */
		   if(GET_LEVEL(ch) > 15)
			levels_losing=3;
		   else if(GET_LEVEL(ch) > 8)
			levels_losing=2;
		   else
			levels_losing=1;

		   for(j=0;j<levels_losing;j++) {
			oldlevel=GET_LEVEL(ch);
			if(oldlevel!=1) {
				GET_LEVEL(ch) = oldlevel-1;
				GET_EXP(ch) -= GET_EXP(ch)/oldlevel;
				set_title(ch);
				ch->points.max_hit -= 
				  ch->points.max_hit/oldlevel;
				ch->points.max_mana -=
				  ch->points.max_mana/oldlevel;
				ch->points.max_move -=
				  ch->points.max_move/oldlevel;

				/* ch->spells_to_learn = 0; */
				GET_HIT(ch) = hit_limit(ch);
				GET_MANA(ch) = mana_limit(ch);
				GET_MOVE(ch) = move_limit(ch);
				GET_COND(ch, THIRST) = 1;
				GET_COND(ch, FULL ) = 1;
				GET_COND(ch, DRUNK ) = 0;
				
				for(  i=1; i<= MAX_SKILLS; i++ ){
					if( ch->skills[i].learned > 0 &&
					    number(0,3)==0){
						ch->skills[i].learned -=
						  ch->skills[i].learned/
						  oldlevel;
					}
				}
				REMOVE_BIT(ch->specials.act,PLR_ISKILLER);
				REMOVE_BIT(ch->specials.act,PLR_ISTHIEF);
			} /* oldlevel */
		    } /* multi-level loss */
		} /* thief or killer */
	} /* !isnpc */
}



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

	if (!(k=ch->master))
		k = ch;


	if (IS_AFFECTED(k, AFF_GROUP) &&
	   (k->in_room == ch->in_room))
		no_members = 1;
	else
		no_members = 0;

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

	if (no_members >= 1)
		share = MIN(450000/no_members, (GET_EXP(victim)/3)/no_members);
	else
		share = 0;

	if (IS_AFFECTED(k, AFF_GROUP) &&
	   (k->in_room == ch->in_room)) {
		act("You receive your share of experience.", FALSE, k, 0, 0, TO_CHAR);
		gain_exp(k, share);
		change_alignment(k, victim);
	}

	for (f=k->followers; f; f=f->next) {
		if (IS_AFFECTED(f->follower, AFF_GROUP) &&
		   (f->follower->in_room == ch->in_room)) {
			act("You receive your share of experience.", FALSE, f->follower,0,0,TO_CHAR);
			gain_exp(f->follower, share);
			change_alignment(f->follower, victim);
		}
	}
}

char *replace_string(char *str, char *weapon)
{
	static char buf[256];
	char *cp;

	cp = buf;

	for (; *str; str++) {
		if (*str == '#') {
			switch(*(++str)) {
				case 'W' : 
					for (; *weapon; *(cp++) = *(weapon++));
					break;
				default :
					*(cp++) = '#';
					break;
			}
		} else {
			*(cp++) = *str;
		}

		*cp = 0;
	} /* For */

	return(buf);
}



void dam_message(int dam, struct char_data *ch, struct char_data *victim,
                 int w_type)
{
	struct obj_data *wield;
	char *buf;
	int dam_level;

	static struct dam_weapon_type {
		char *to_room;
		char *to_char;
		char *to_victim;
	} dam_weapons[] = {

	 {"$n misses $N with $s #W.",                           /*    0    */
	  "You miss $N with your #W.",
	  "$n misses you with $s #W." },

   {"$n tickles $N with $s #W.",                          /*  1.. 2  */
    "You tickle $N as you #W $M.",
    "$n tickles you as $e #W you." },

   {"$n barely #W $N.",                                   /*  3.. 4  */
    "You barely #W $N.",
    "$n barely #W you."},

	 {"$n #W $N.",                                          /*  5.. 6  */
    "You #W $N.",
    "$n #W you."}, 

	 {"$n #W $N hard.",                                     /*  7..10  */
	  "You #W $N hard.",
    "$n #W you hard."},

	 {"$n #W $N very hard.",                                /* 11..14  */
	  "You #W $N very hard.",
	  "$n #W you very hard."},

	 {"$n #W $N extremely hard.",                          /* 15..20  */
	  "You #W $N extremely hard.",
	  "$n #W you extremely hard."},

	 {"$N staggers from a fearsome #W from $n.",        /* 20..40 */
	  "$N staggers from your fearsome #W.",
	  "You stagger from a fearsome #W from $n."},

	 {"$n massacres $N to small fragments with $s #W.",     /* 40..60  */
	  "You massacre $N to small fragments with your #W.",
	  "$n massacres you to small fragments with $s #W."},

								/* 60..100 */
	 {"$N is enshrouded in a mist of blood after receiving $n's #W.",
	  "$N is enshrouded in a mist of blood after receiving your #W.",
	  "You are enshrouded in a mist of blood after $n's #W."},

	 {"$n nearly rips $N apart from the force of $s #W.",   /* 100+ */
	  "You nearly rip $N apart from the force of your #W.",
	  "$n nearly rips you apart from the force of $s #W."}
	};

	w_type -= TYPE_HIT;   /* Change to base of table with text */

	wield = ch->equipment[WIELD];

	if (dam == 0) {
		dam_level=0;
	} else if (dam <= 2) {
		dam_level=1;
	} else if (dam <= 4) {
		dam_level=2;
	} else if (dam <= 6) {
		dam_level=3;
	} else if (dam <= 10) {
		dam_level=4;
	} else if (dam <= 15) {
		dam_level=5;
	} else if (dam <= 20) {
		dam_level=6;
	} else if (dam <= 40) {
		dam_level=7;
	} else if (dam <= 60) {
		dam_level=8;
	} else if (dam <= 100) {
		dam_level=9;
	} else {
		dam_level=10;
	}
	buf = replace_string(dam_weapons[dam_level].to_room, attack_hit_text[w_type].singular);
	act(buf, FALSE, ch, wield, victim, TO_NOTVICT);
	buf = replace_string(dam_weapons[dam_level].to_char, attack_hit_text[w_type].singular);
	act(buf, FALSE, ch, wield, victim, TO_CHAR);
	buf = replace_string(dam_weapons[dam_level].to_victim, attack_hit_text[w_type].singular);
	act(buf, FALSE, ch, wield, victim, TO_VICT);
}



void damage(struct char_data *ch, struct char_data *victim,
            int dam, int attacktype)
{
	char buf[MAX_STRING_LENGTH];
	struct message_type *messages;
	int i,j,nr,max_hit,exp;
	char buf2[200];

	int hit_limit(struct char_data *ch);

	/* assert(GET_POS(victim) > POSITION_DEAD); */
	if (GET_POS(victim)<=POSITION_DEAD){
		fprintf(stderr,"Fight.c: assert in damage() failed by %s, %s (ch,vict).\n\r",
		   GET_NAME(ch),GET_NAME(victim));
		send_to_char("He's dead already, report this bug.\n\r",ch);
		send_to_char("You're dead. Report bug to gods.\n\r",victim);
		return;
	}

	if (IS_TRUSTED(victim)) /* You can't damage an immortal! */
		dam=0;
		
	if (victim != ch) {
		if(is_in_safe(ch,victim)==TRUE){ return; }
		if(is_first_level(ch,victim)==TRUE){ return; }
		if(nokill(ch,victim)==TRUE){ return; }
		if(notwithinsixlevels(ch,victim)==TRUE){ return; }
		if(isdweeb(ch)==TRUE) { return; }
		check_killer(ch,victim);

		if (IS_NPC(ch)&&IS_AFFECTED(ch, AFF_CHARM) &&
		    !IS_NPC(victim)&&(victim->specials.fighting!=ch)){
			send_to_char("You cannot harm another player!\n\r",ch);
			return;
		}
		if (GET_POS(victim) > POSITION_STUNNED) {
			if (!(victim->specials.fighting)) 
				set_fighting(victim, ch);
			if (IS_NPC(victim) && !IS_TRUSTED(ch)){
                           remember(ch->player.name, victim);
                        }
			GET_POS(victim) = POSITION_FIGHTING;
		}

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

			if (IS_NPC(ch) && IS_NPC(victim) &&
		            victim->master &&
			    !number(0,10) && IS_AFFECTED(victim, AFF_CHARM) &&
			    (victim->master->in_room == ch->in_room)) {
				if (ch->specials.fighting)
					stop_fighting(ch);
				hit(ch, victim->master, TYPE_UNDEFINED);
				return;
			}
		}
	}

	if (victim->master == ch)
		stop_follower(victim);
			
	if (IS_AFFECTED(ch, AFF_INVISIBLE))
		appear(ch);

	if (IS_AFFECTED(victim, AFF_SANCTUARY))
		dam = MIN(dam, 18);  /* Max 18 damage when sanctuary */

	dam=MIN(dam,300);

	dam=MAX(dam,0);

	GET_HIT(victim)-=dam;

	/* No exp for arena fighting */
	if (ch != victim && !IS_SET(world[ch->in_room].room_flags,ARENA))
		gain_exp(ch,GET_LEVEL(victim)*dam);

	update_pos(victim);


	if ((attacktype >= TYPE_HIT) && (attacktype <= TYPE_CRUSH)) {
		if (!ch->equipment[WIELD] && !IS_NPC(ch)) {
			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) {
			nr=dice(1,fight_messages[i].number_of_attacks);
			for(j=1,messages=fight_messages[i].msg;(j<nr)&&(messages);j++)
				messages=messages->next;

			if (IS_TRUSTED(victim)) {
			    if(CAN_SEE(ch,victim)) {
				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) {
				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);
				}
			} else { /* 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);
			}
		}
	}
	}
	switch (GET_POS(victim)) {
		/*
		 * Use send_to_char, because act() doesn't send
		 * message if you are DEAD.
 		*/
		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.", 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 an will slowly die, if not aided.", victim);
			break;
		case POSITION_STUNNED:
			act("$n is stunned, but might regain consciousness again.", TRUE, victim, 0, 0, TO_ROOM);
			send_to_char("You're stunned, but you might regain consciousness again.", victim);
			break;
		case POSITION_DEAD:
			act("$n is dead! R.I.P.", TRUE, victim, 0, 0, TO_ROOM);
              		send_to_char("You are dead!  Sorry...", victim);
			break;

		default:  /* >= POSITION SLEEPING */

			max_hit=hit_limit(victim);

			if (dam > (max_hit/5))
				act("Ouch! That Really did HURT!",FALSE, victim, 0, 0, TO_CHAR);

			if (GET_HIT(victim) < (max_hit/5)) {
			
				act("You wish that your wounds would stop BLEEDING so much!",FALSE,victim,0,0,TO_CHAR);
				if (IS_NPC(victim)) {
					if (IS_SET(victim->specials.act, ACT_WIMPY))
						do_flee(victim, "", 0);
				}
			}
			break;		
	}

	/* WIMPY for PC's */
	if (IS_AFFECTED(victim, AFF_WIMPY) && GET_HIT(victim) > 0 &&
			GET_HIT(victim)<victim->specials.wimpyness) {
		do_flee(victim, "", 0);
	}
	if (!IS_NPC(victim) && !(victim->desc)) {
		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,0);
		}
	}

	if (GET_POS(victim) < POSITION_STUNNED)
		if (ch->specials.fighting == victim)
			stop_fighting(ch);

	if (!AWAKE(victim))
		if (victim->specials.fighting)
			stop_fighting(victim);

	if (GET_POS(victim) == POSITION_DEAD) {
		if (IS_NPC(victim) || victim->desc)
		    /* Cannot get exp in the arena */
		    if(!IS_SET(world[ch->in_room].room_flags,ARENA)) {
			if (IS_AFFECTED(ch, AFF_GROUP)) {
					group_gain(ch, victim);
			} else {
				/* Calculate level-difference bonus */
				exp = GET_EXP(victim)/3;
				if (IS_NPC(ch))
					exp += (exp*MIN(4, (GET_LEVEL(victim) - GET_LEVEL(ch))))>>3;
				else
					exp += (exp*MIN(8, (GET_LEVEL(victim) - GET_LEVEL(ch))))>>3;
				exp = MAX(exp, 1);
				gain_exp(ch, exp);
				change_alignment(ch, victim);
			}
		   } /* if not in arena area */
		if (!IS_NPC(victim) && /* No logging if arena 'death' */
		    !IS_SET(world[ch->in_room].room_flags,ARENA)) {
			sprintf(buf, "%s killed by %s at %s",
				GET_NAME(victim),
				(IS_NPC(ch) ? ch->player.short_descr : GET_NAME(ch)),
				world[victim->in_room].name);
			log(buf);
		}
                if (IS_NPC(ch) /*&& !IS_NPC(victim)*/) {/* Ouch, this isn't */
			forget(victim->player.name,ch);/* a perfect solution*/
                } /* if */                             /* to memory prob... */
		die(victim);
	}
}



void hit(struct char_data *ch, struct char_data *victim, int type)
{

	struct obj_data *wielded = 0;
	struct obj_data *held = 0;
	int w_type;
	int victim_ac, calc_thaco;
	int dam;
	byte diceroll;

	extern byte backstab_mult[];
	extern struct str_app_type str_app[];
	extern struct dex_app_type dex_app[];

	if (ch->in_room != victim->in_room) {
		log("NOT SAME ROOM WHEN FIGHTING!");
		return;
	}

	if (ch->equipment[HOLD])
		held = ch->equipment[HOLD];

	if (ch->equipment[WIELD] &&
	   (ch->equipment[WIELD]->obj_flags.type_flag == ITEM_WEAPON)) {
		wielded = ch->equipment[WIELD];
		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 : w_type = TYPE_CLAW; break;
			case 11 : w_type = TYPE_PIERCE; break;

			default : w_type = TYPE_HIT; break;
		}
	}	else {
		if (IS_NPC(ch) && (ch->specials.attack_type >= TYPE_HIT))
			w_type = ch->specials.attack_type;
		else
			w_type = TYPE_HIT;
	}


	if(!to_hit_char(ch,victim)) {
		if (type == SKILL_BACKSTAB)
			damage(ch, victim, 0, SKILL_BACKSTAB);
		else
			damage(ch, victim, 0, w_type);
	} else {

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

		if (!wielded) {
			if (IS_NPC(ch))
				dam += dice(ch->specials.damnodice, ch->specials.damsizedice);
			else
				dam += number(0,2);  /* Max. 2 dam with bare hands */
		} else {
			dam += dice(wielded->obj_flags.value[1], wielded->obj_flags.value[2]);
		}

		if (GET_POS(victim) < POSITION_FIGHTING)
			dam *= 1+(POSITION_FIGHTING-GET_POS(victim))/3;
		/* Position  sitting  x 1.33 */
		/* Position  resting  x 1.66 */
		/* Position  sleeping x 2.00 */
		/* Position  stunned  x 2.33 */
		/* Position  incap    x 2.66 */
		/* Position  mortally x 3.00 */

		dam = MAX(1, dam);  /* Not less than 0 damage */

		if (type == SKILL_BACKSTAB) {
			dam *= backstab_mult[GET_LEVEL(ch)];
			damage(ch, victim, dam, SKILL_BACKSTAB);
		} else {
                    if (wielded && obj_index[wielded->item_number].func) {
                       if(! (*obj_index[wielded->item_number].func)
                            (wielded, ch, -(dam*1000+w_type) , (char *) victim))
				   damage(ch,victim,dam,w_type);
		    } else
			damage(ch, victim, dam, w_type);
		}
	}
}

void lose_exp_by_flight(struct char_data *ch, struct char_data *was_fighting)
{
	int lose;

	if(!(ch && was_fighting)) {
		log("Bad call to lose_exp_by_flight");
		return;
	}

	if(IS_NPC(ch) || IS_SET(world[ch->in_room].room_flags,ARENA))
		return;

	lose=GET_MAX_HIT(was_fighting)-GET_HIT(was_fighting);
	lose*=GET_LEVEL(was_fighting);

	gain_exp(ch,-lose);
}

/* control the fights going on */
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 { /* Not in same room */
			stop_fighting(ch);
		}
	}
}

/* New generic hitting calculators */
bool to_hit(struct char_data *ch, int vict_ac)
{
	int calc_thaco,diceroll;
	extern int thaco[4][25];
	extern struct str_app_type str_app[];

	/* Calculate the raw armor including magic armor */
	/* The lower AC, the better                      */

	if (!IS_NPC(ch))
		calc_thaco  = thaco[GET_CLASS(ch)-1][GET_LEVEL(ch)];
	else
		/* THAC0 for monsters is set in the HitRoll */
		calc_thaco = 20;

	calc_thaco -= str_app[STRENGTH_APPLY_INDEX(ch)].tohit;
	calc_thaco -= GET_HITROLL(ch);
	calc_thaco -= (int)((GET_INT(ch) - 13)/1.5);    /*Smartness helps!*/ 
	calc_thaco -= (int)((GET_WIS(ch) - 13)/1.5);    /*So does wisdom */
	diceroll = number(1,20);

	return((calc_thaco-diceroll) <= vict_ac);
}

bool to_hit_char(struct char_data *ch,struct char_data *victim)
{
	int victim_ac;
	extern struct dex_app_type dex_app[];

	victim_ac  = GET_AC(victim)/10;

	if (AWAKE(victim))
		victim_ac += dex_app[GET_DEX(victim)].defensive;

	if(IS_EVIL(ch) && IS_AFFECTED(victim,AFF_PROTECT_EVIL))
		victim_ac -= 1;

	victim_ac = MAX(-10, victim_ac);  /* -10 is lowest */

	return(to_hit(ch,victim_ac) || !AWAKE(victim));
}