/
Archipelago/
Archipelago/doc/
Archipelago/lib/misc/
Archipelago/lib/plrobjs/
Archipelago/lib/plrobjs/P-T/
Archipelago/lib/world/mob/
Archipelago/lib/world/obj/
Archipelago/lib/world/shp/
Archipelago/lib/world/wld/
Archipelago/lib/world/zon/
Archipelago/slave/
/* ************************************************************************
*   File: fight.c                                       Part of CircleMUD *
*  Usage: Combat system                                                   *
*                                                                         *
*  All rights reserved.  See license.doc for complete information.        *
*                                                                         *
*  Copyright (C) 1993 by the Trustees of the Johns Hopkins University     *
*  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
************************************************************************ */
/* Archipelago changes by Alastair J. Neil Copyright (C) 1993, 94, 95, 96 */

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

#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "handler.h"
#include "interpreter.h"
#include "db.h"
#include "spells.h"
#include "limits.h"
#include "screen.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 message_list fight_messages[MAX_MESSAGES];
extern struct obj_data *object_list;
extern int	pk_allowed;	/* see config.c */
extern int	auto_save;	/* see config.c */
extern int      pulse;
extern struct dual_list_type attack_hit_text[];
extern struct   index_data *mob_index;
extern struct str_app_type str_app[];
extern struct dex_app_type dex_app[];
/* External procedures */
void poison(struct char_data *ch, struct char_data *victim, struct obj_data *obj);
char	*fread_string(FILE *fl, char *error);
void	stop_follower(struct char_data *ch);
ACMD(do_flee);
void	hit(struct char_data *ch, struct char_data *victim, int type);
void	forget(struct char_data *victim);
void	remember(struct char_data *ch, struct char_data *victim);
void	make_scraps(struct obj_data *obj, int r_num);
void    scrap_item(struct obj_data *obj);
void	loose_level(struct char_data *ch);
bool   is_two_handed(struct char_data *ch, struct obj_data *weapon);
int     spell_lev(struct char_data *caster, int spell);
void    add_event(int plse, int event, int inf1, int inf2, int inf3
	       , int inf4, char *arg, void *subj, void *vict);
void botch_kick(struct char_data *ch, struct char_data *vict);
int stress_die(void);
int get_prof(struct char_data *ch, int *w_type, int type);
void damage_obj_corpse(struct obj_data *corpse, int spell_type, int dam);
void spell_damage_equipment(struct char_data *ch, struct char_data *vict, int spell_no, int dam);
int condition_bonus(struct char_data *ch);
int encumberance_level(struct char_data *ch);
int combat_bonus(struct char_data *ch);
int fatigue_bonus(struct char_data *ch);
int bodylevel_bonus(struct char_data *ch);
int compute_multi_attacks(struct char_data *ch, struct obj_data *weilded, int type);
struct obj_data 	*die(struct char_data *ch);
/* Weapon attack texts */

/* The Fight related routines */
int combat_bonus(struct char_data *ch)
{
  int level=0;
  
  level -= (3*encumberance_level(ch))/4;
  level += fatigue_bonus(ch);
  level += bodylevel_bonus(ch);
  level += condition_bonus(ch);
  
  return(level);
}
int encumberance_level(struct char_data *ch)
{
  int weight_worn=0,i, level=0, items_worn = 0;
  
  for (i=0; i < MAX_WEAR; i++)
    if (ch->equipment[i]){
      weight_worn += ch->equipment[i]->obj_flags.weight;
      items_worn++;
    }
  weight_worn /= 2;
  items_worn /= 3;
  if ((IS_CARRYING_W(ch) + weight_worn) > (4*CAN_CARRY_W(ch))/5)
    level = 8;
  else if ((IS_CARRYING_W(ch) + weight_worn) > (3*CAN_CARRY_W(ch))/5)
    level = 6;
  else if ((IS_CARRYING_W(ch) + weight_worn) > (2*CAN_CARRY_W(ch))/5)
    level = 4;
  else if ((IS_CARRYING_W(ch) + weight_worn) > (CAN_CARRY_W(ch))/5)
    level = 2;
  if ((IS_CARRYING_N(ch) + items_worn) > (4*CAN_CARRY_N(ch))/5)
    level += 4;
  else if ((IS_CARRYING_N(ch) + items_worn) > (3*CAN_CARRY_N(ch))/5)
    level += 3;
  else if ((IS_CARRYING_N(ch) + items_worn) > (2*CAN_CARRY_N(ch))/5)
    level += 2;  
  else if ((IS_CARRYING_N(ch) + items_worn) > (CAN_CARRY_N(ch))/5)
    level += 1;
  
  level /= 2;
  return(level);
}
int condition_bonus(struct char_data *ch)
{
  int level=0;
  
  if (GET_COND(ch,DRUNK) > 19)
    level = -10;
  else   if (GET_COND(ch,DRUNK) > 14)
    level = -7;
  else   if (GET_COND(ch,DRUNK) > 10)
    level = -4;
  else   if (GET_COND(ch,DRUNK) > 5)
    level = -2;
  else   if (GET_COND(ch,DRUNK) > 0)
    level = -1;

  if (GET_COND(ch,THIRST) == 0)
    level -= 1;
  if (GET_COND(ch,FULL) == 0)
    level -= 2;
  
  return(level);
}
int fatigue_bonus(struct char_data *ch)
{

  int max;

  max = GET_MAX_MOVE(ch);

  if (GET_MOVE(ch) > (3*max)/5)
    return(0);
  else if (GET_MOVE(ch) > (2*max)/5)
    return(-1);
  else if (GET_MOVE(ch) > (max)/5)
    return(-3);
  else
    return(-5);
  
}
int bodylevel_bonus(struct char_data *ch)
{

  int max;

  max = GET_MAX_HIT(ch);

  if (GET_HIT(ch) > (3*max)/5)
    return(0);
  else if (GET_HIT(ch) > (2*max)/5)
    return(-1);
  else if (GET_HIT(ch) > (max)/5)
    return(-3);
  else
    return(-5);
  
}
void scrap_item(struct obj_data *obj)
{
    struct obj_data *ot;

    act("$p falls to the ground in scraps!",FALSE,0,obj,0,TO_ROOM);

    if(obj->obj_flags.type_flag == ITEM_CONTAINER &&
       obj->contains){
      
	send_to_room("It's contents fall on the ground.\r\n",obj->in_room,FALSE);
	for(ot = obj->contains;ot;ot = obj->contains){
	  obj_from_obj(ot);
	  obj_to_room(ot,obj->in_room, FALSE);
	}
    }
    make_scraps(obj,obj->in_room);
    extract_obj(obj,0);
}

void	appear(struct char_data *ch)
{
   act("$n slowly fades into existence.", FALSE, ch, 0, 0, TO_ROOM);

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

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



void	load_messages(void)
{
   FILE * f1;
   struct message_type *messages;
   int	i, type;
   char	chk[100];

   if (!(f1 = fopen(MESS_FILE, "r"))) {
      sprintf(buf2, "Error reading combat message file %s", MESS_FILE);
      perror(buf2);
      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) {
	 logg("SYSERR: 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;
       sprintf(buf2, "combat message #%d in file '%s'", i, MESS_FILE);
       
       messages->die_msg.attacker_msg      = fread_string(f1, buf2);
       messages->die_msg.victim_msg        = fread_string(f1, buf2);
       messages->die_msg.room_msg          = fread_string(f1, buf2);
       messages->miss_msg.attacker_msg     = fread_string(f1, buf2);
       messages->miss_msg.victim_msg       = fread_string(f1, buf2);
       messages->miss_msg.room_msg         = fread_string(f1, buf2);
       messages->hit_msg.attacker_msg      = fread_string(f1, buf2);
       messages->hit_msg.victim_msg        = fread_string(f1, buf2);
       messages->hit_msg.room_msg          = fread_string(f1, buf2);
       messages->god_msg.attacker_msg      = fread_string(f1, buf2);
       messages->god_msg.victim_msg        = fread_string(f1, buf2);
       messages->god_msg.room_msg          = fread_string(f1, buf2);
       fscanf(f1, " %s \n", chk);
       
   }

   fclose(f1);
}


void	update_pos( struct char_data *victim )
{

   if ((GET_HIT(victim) > 0) && (GET_POS(victim) > POSITION_STUNNED))
      return;
   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 if (GET_HIT(victim) <= 0)
      GET_POS(victim) = POSITION_STUNNED;
   else
      GET_POS(victim) = POSITION_RESTING;   

}


void	check_killer(struct char_data *ch, struct char_data *vict)
{
   if (!PLR_FLAGGED(vict, PLR_KILLER) && !PLR_FLAGGED(vict, PLR_THIEF)
        && !PLR_FLAGGED(ch, PLR_KILLER) && !IS_NPC(ch) && !IS_NPC(vict) && 
       (ch != vict)) {

/* don't auto set killer flag
      SET_BIT(PLR_FLAGS(ch), PLR_KILLER);
      sprintf(buf, "PC Killer bit set on %s for initiating attack on %s at %s.", 
      GET_NAME(ch), GET_NAME(vict), world[vict->in_room].name); 
      mudlog(buf, BRF, LEVEL_BUILDER, TRUE);
      send_to_char("If you want to be a PLAYER KILLER, so be it...\r\n", ch);  */
   }
}


/* start one char fighting another (yes, it is horrible, I know... )  */
void	set_fighting(struct char_data *ch, struct char_data *vict)
{
   if (ch == vict)
      return;

   assert(!ch->specials.fighting);

   ch->next_fighting = combat_list;
   combat_list = ch;

   ch->specials.fighting = vict;

   if (!pk_allowed)
      check_killer(ch, vict);
}



/* 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) {
	 logg("SYSERR: Char fighting not found Error (fight.c, stop_fighting)");
	 abort();
      }
      tmp->next_fighting = ch->next_fighting;
   }

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



struct obj_data 	*make_corpse(struct char_data *ch)
{
   struct obj_data *corpse, *o;
   struct obj_data *money;
   int	i;
   bool undead = FALSE;
   extern int max_npc_corpse_time, max_pc_corpse_time;


   struct obj_data *create_money(int amount);
   
   
   corpse = read_object(1274, VIRTUAL, 1);
   corpse->obj_flags.value[5] = GET_SIZE(ch);
   corpse->obj_flags.value[4] = GET_LEVEL(ch);   
   if (IS_UNDEAD(ch))
       undead = TRUE;
   corpse->obj_flags.value[2] = GET_IDNUM(ch);
   if (IS_NPC(ch))
     corpse->obj_flags.value[7] = mob_index[ch->nr].virtual;
   corpse->in_room = NOWHERE;
   if (undead)
       sprintf(buf2,"dust pile");
   else
       sprintf(buf2,"corpse %s",GET_NAME(ch));
   corpse->name = str_dup(buf2);
   if (undead)
       sprintf(buf2, "A pile of dust is lying here.");
   else
       sprintf(buf2, "The corpse of %s is lying here.", GET_NAME(ch));
   corpse->description = str_dup(buf2);
   if (undead)
       sprintf(buf2, "a pile of dust");
   else
       sprintf(buf2, "the corpse of %s", GET_NAME(ch));
   corpse->short_description = str_dup(buf2);
   /*   if (!undead){
     sprintf(buf2, "The stench rising from the corpse makes your gorge rise.");
     corpse->action_description = str_dup(buf2);
   }*/
   corpse->contains = ch->inventory;
   if (GET_GOLD(ch) > 0) {
      /* following 'if' clause added to fix gold duplication loophole */
      if (IS_NPC(ch) || (!IS_NPC(ch) && ch->desc)) {
	 money = create_money(GET_GOLD(ch));
	 obj_to_obj(money, corpse);
      }
      GET_GOLD(ch) = 0;
   }

   corpse->obj_flags.wear_flags = ITEM_TAKE;
   corpse->obj_flags.extra_flags = ITEM_NODONATE;
   corpse->obj_flags.value[0] = 0; /* You can't store stuff in a corpse */
   if (undead)
       corpse->obj_flags.value[3] = -2; /* undead corpse identifyer */
   else
       corpse->obj_flags.value[3] = -1; /* corpse identifyer */
   corpse->obj_flags.type_flag = ITEM_CONTAINER;
   corpse->obj_flags.weight = GET_WEIGHT(ch) + IS_CARRYING_W(ch);
   corpse->obj_flags.cost_per_day = 10;
   if (IS_NPC(ch)){
       if (undead)
	   corpse->obj_flags.timer = max_npc_corpse_time/2;
       else
	   corpse->obj_flags.timer = max_npc_corpse_time;
   }
   else
      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->inventory = 0;
   IS_CARRYING_N(ch) = 0;
   IS_CARRYING_W(ch) = 0;

   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, FALSE);
   return(corpse);
}

void	make_scraps(struct obj_data *obj, int r_num)
{
   struct obj_data *scraps;


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

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

   sprintf(buf2, "Scraps from %s are lying here.", obj->short_description);
   scraps->description = str_dup(buf2);

   sprintf(buf2, "scraps from %s", obj->short_description);
   scraps->short_description = str_dup(buf2);


   scraps->obj_flags.type_flag = ITEM_TRASH;
   scraps->obj_flags.wear_flags = ITEM_TAKE;
   scraps->obj_flags.extra_flags = ITEM_NODONATE;
   scraps->obj_flags.value[0] = 0;
   scraps->obj_flags.value[3] = 0;
   scraps->obj_flags.weight = 1;
   scraps->obj_flags.cost_per_day = 100000;
   scraps->obj_flags.timer = 3;

     
   obj_to_room(scraps,r_num, FALSE);
}


/* When ch kills victim */
void	change_alignment(struct char_data *ch, struct char_data *victim)
{
#define GT_LEVEL(ch) (((ch)->player.level > 0) ? (ch)->player.level : 1)
  
  int	align, lev_dif;

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

   if (GET_ALIGNMENT(victim) < 0) {
     if (align > 650) {
       if (lev_dif > 0)
	 GET_ALIGNMENT(ch) =
	   MIN(1000, GET_ALIGNMENT(ch) + GT_LEVEL(victim)*
				 (((align - 650) / 8) +
				  ((1000 - GET_ALIGNMENT(victim))/ 5))
				 / GT_LEVEL(ch));
       else
	 GET_ALIGNMENT(ch) = MIN(1000, GET_ALIGNMENT(ch) + GT_LEVEL(ch)*
				 (((align - 650) / 8) +
				  ((1000 - GET_ALIGNMENT(victim))/ 5))
				 / GT_LEVEL(victim));
       
     }
   }
   else {
     if (lev_dif > 0)
       GET_ALIGNMENT(ch) = MIN(1000, GET_ALIGNMENT(ch) - GT_LEVEL(victim)*
			       (GET_ALIGNMENT(victim) / 8)
				 /GT_LEVEL(ch));
     else
       GET_ALIGNMENT(ch) = MIN(1000, GET_ALIGNMENT(ch) - GT_LEVEL(ch)*
			       (GET_ALIGNMENT(victim) / 8)
			       /GT_LEVEL(victim));
       
   }
   if (align < -650) {
     if (lev_dif > 0)
       GET_ALIGNMENT(ch) = MAX(-1000, GET_ALIGNMENT(ch)
			       + GT_LEVEL(victim)*
			       (((align + 650) / 8)
				+ ((-1000 + GET_ALIGNMENT(victim))
				   / 5))/GT_LEVEL(ch));
     else
       GET_ALIGNMENT(ch) = MAX(-1000, GET_ALIGNMENT(ch)
			       + GT_LEVEL(ch)*
			       (((align + 650) / 8)
				+ ((-1000 + GET_ALIGNMENT(victim))
				   / 5))/GT_LEVEL(victim));
     
   }
   
   else {
     if (lev_dif > 0)
       GET_ALIGNMENT(ch) = MAX(-1000, GET_ALIGNMENT(ch) -
			       GT_LEVEL(victim)*
			       (GET_ALIGNMENT(victim) / 8)
			       / GT_LEVEL(ch));
     else
       GET_ALIGNMENT(ch) = MAX(-1000, GET_ALIGNMENT(ch) -
			       GT_LEVEL(ch)*
			       (GET_ALIGNMENT(victim) / 8)
			       / GT_LEVEL(victim));
   }
   GET_ALIGNMENT(ch) = MIN(1000, GET_ALIGNMENT(ch));
   GET_ALIGNMENT(ch) = MAX(-1000, GET_ALIGNMENT(ch));   
}
   


void	death_cry(struct char_data *ch)
{
   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 < NUM_OF_DIRS; door++) {
      if (CAN_GO(ch, door))	 {
	 ch->in_room = real_room(world[was_in].dir_option[door]->to_room);
	 act("Your blood freezes as you hear someone's death cry.", FALSE, ch, 0, 0, TO_ROOM);
	 ch->in_room = was_in;
      }
   }
}



struct obj_data 	*raw_kill(struct char_data *ch)
{
    struct obj_data *corpse;
    
    if (ch->specials.fighting)
      stop_fighting(ch);
    
    while (ch->affected)
       affect_remove(ch, ch->affected);
    
    death_cry(ch);
    
    corpse = make_corpse(ch);
    extract_char(ch, TRUE);
    return(corpse);
}



struct obj_data 	*die(struct char_data *ch)
{
    struct obj_data *corpse;
    
    gain_exp(ch, -(GET_EXP(ch) / 5),0);
    gain_social_standing(ch, ch->specials.fighting, MODE_DIE);
    GET_COND(ch, FULL) = 24;
    GET_COND(ch, THIRST) = 24;
    GET_COND(ch, DRUNK) = 0;        
    if (!IS_NPC(ch))
	REMOVE_BIT(PLR_FLAGS(ch), PLR_KILLER | PLR_THIEF);
    if (!IS_NPC(ch))
      forget(ch);
    corpse = raw_kill(ch);
    return(corpse);
}



void	group_gain(struct char_data *ch, struct char_data *victim)

{
   int	no_members, share, tot_exp, my_share;
   struct char_data *k;
   struct follow_type *f;

   if (!(k = ch->master))
       k = ch;        /* k is the leader */


   if (IS_AFFECTED(k, AFF_GROUP) && 
       (k->in_room == victim->in_room)){
      no_members = 1;
      tot_exp = GET_LEVEL(k);}
   else{
      no_members = 0;
      tot_exp = 0;}

   for (f = k->followers; f; f = f->next)
      if (IS_AFFECTED(f->follower, AFF_GROUP) && 
          (f->follower->in_room == victim->in_room)){
	 no_members++;
	 tot_exp += GET_LEVEL(f->follower);
      }
   share = GET_EXP(victim);
   if (IS_AFFECTED(k, AFF_GROUP) && 
       (k->in_room == victim->in_room)) {
       my_share = share*GET_LEVEL(k)/tot_exp;
       my_share = MIN(6000*GET_LEVEL(ch),my_share);
       sprintf(buf2, "You receive your share of experience -- %d points.",
	       my_share);
       act(buf2, FALSE, k, 0, 0, TO_CHAR);
       gain_exp(k, my_share,1);
       gain_social_standing(ch, victim, MODE_GROUP_KILL);       
       change_alignment(k, victim);
       if (auto_save) {
	   /* save 25% of the time to avoid lag in big groups */
	   if (!(number(0, 3)))
	       save_char(k, NOWHERE);
       }
   }

   for (f = k->followers; f; f = f->next) {
      if (IS_AFFECTED(f->follower, AFF_GROUP) && 
          (f->follower->in_room == victim->in_room)) {
	  my_share = share*GET_LEVEL(f->follower)/tot_exp;
	  share = MIN(3000*GET_LEVEL(f->follower),share);
	  sprintf(buf2, "You receive your share of experience -- %d points.", my_share);
	 act(buf2, FALSE, f->follower, 0, 0, TO_CHAR);
	 gain_exp(f->follower, my_share,1);
	 gain_social_standing(ch, victim, MODE_GROUP_KILL);       	 
	 change_alignment(f->follower, victim);
	 if (auto_save) {
	    /* save only 25% of the time to avoid lag in big groups */
	    if (!(number(0, 3)))
	       save_char(f->follower, NOWHERE);
	 }
      }
   }
}


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

   cp = buf;

   for (; *str; str++) {
      if (*str == '#') {
	 switch (*(++str)) {
	 case 'W' :
	    for (; *weapon_plural; *(cp++) = *(weapon_plural++)) ;
	    break;
	 case 'w' :
	    for (; *weapon_singular; *(cp++) = *(weapon_singular++)) ;
	    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,int hit_loc)
{
   struct obj_data *wield;
   char	*buf;
   int	msgnum;
   static char *hit_locs1[] = {
       "to the body.",
       "to the arms.",
       "to the legs.",
       "to the head.",
   };

   static char *hit_locs2[] = {
       "in the body.",
       "in the arms.",
       "in the legs.",
       "in the head.",
   };

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

   /* use #w for singular (i.e. "slash") and #W for plural (i.e. "slashes") */

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

      { "$n bruises $N with $s #w ",			/* 1: 1..2  */
      "You bruise $N as you #w $M ",
      "$n bruises you as $e #W you " },

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

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

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

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

      { "$n #W $N extremely hard ", 			/* 6: 15..18  */
      "You #w $N extremely hard ",
      "$n #W you extremely hard " },

      { "$n massacres $N with $s #w ",                  /* 7: 19..25	 */
      "You massacre $N with your #w ",
      "$n massacres you with $s #w " },

      { "$n pulverises $N with $s #w ",                  /* 8: 25..32 */
      "You pulverise $N with your #w ",
      "$n pulverises you with $s #w " },

      { "$n obliterates $N with $s #w ",	       /* 9:  32..45   */
      "You obliterate $N with your #w ",
      "$n obliterates you with $s #w " },

      { "$n annihilates $N with $s #w ",	      /* 10: >45   */
      "You annihilate $N with your #w ",
      "$n annihilates you with $s #w " }
   };   



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

   wield = ch->equipment[WIELD];

   if (dam == 0)	msgnum = 0;
   else if (dam <= 2)	msgnum = 1;
   else if (dam <= 4)	msgnum = 2;
   else if (dam <= 6)	msgnum = 3;
   else if (dam <= 10)	msgnum = 4;
   else if (dam <= 14)	msgnum = 5;
   else if (dam <= 19)	msgnum = 6;
   else if (dam <= 25)	msgnum = 7;
   else	if (dam <= 32)	msgnum = 8;
   else	if (dam <= 45)	msgnum = 9;   
   else                 msgnum = 10;


   /* damage message to onlookers */
   buf = replace_string(dam_weapons[msgnum].to_room,
			attack_hit_text[w_type].singular, attack_hit_text[w_type].plural);
   if (hit_loc >= 0 && hit_loc <= 3)
     if (msgnum != 0)
       strcat(buf,hit_locs2[hit_loc]);
     else
       strcat(buf,hit_locs1[hit_loc]);
   act(buf, FALSE, ch, wield, victim, TO_NOTVICT);
   
   /* damage message to damager */
   
   buf = replace_string(
			dam_weapons[msgnum].to_char,
			attack_hit_text[w_type].singular,
			attack_hit_text[w_type].plural);
   if (hit_loc >= 0 && hit_loc <= 3)
     if (msgnum != 0)
       strcat(buf,hit_locs2[hit_loc]);
     else
       strcat(buf,hit_locs1[hit_loc]);
   if (dam > 0 || !PRF_FLAGGED(ch, PRF_COMPACT))   
     act(buf, FALSE, ch, wield, victim, TO_CHAR);
   

   /* damage message to damagee */
   
   buf = replace_string(
			dam_weapons[msgnum].to_victim,
			attack_hit_text[w_type].singular,
			attack_hit_text[w_type].plural);
   if (hit_loc >= 0 && hit_loc <= 3)
     if (msgnum != 0)
       strcat(buf,hit_locs2[hit_loc]);
     else
       strcat(buf,hit_locs1[hit_loc]);
   if (dam > 0 || !PRF_FLAGGED(victim, PRF_COMPACT))
     act(buf, FALSE, ch, wield,victim, TO_VICT);
   
}


void	damage(struct char_data *ch, struct char_data *victim, int dam,
	       int attacktype,int hit_loc, bool saved)
{ 
  struct message_type *messages;
  struct msg_type *diemessage;
  struct obj_data *obj_tmp;
  int	i, j, nr, exp,where,atcktype,bhead=0,w_type;
  char bufer[100];
  char bufer2[100];
  char *buf;
  ACMD(do_gen_com);
  ACMD(do_wake);
  static struct behead_message_type {
    char	*to_room;
    char	*to_char;
    char	*to_victim;
  } behead_messages = 
    { "$n severs $N's head with $s mighty #w.",
      "You sever $N's head with a mighty #w.",
      "Everything suddenly goes black."};
   
   
    if (GET_POS(victim) <= POSITION_DEAD) {
      logg("SYSERR: Attempt to damage a corpse.");
      return;   /* -je, 7/7/92 */
    }

    if (!IS_NPC(ch) && !pk_allowed && !IS_NPC(victim) && victim != ch){
      act("You don't think attacking $N such a good idea.",
	  FALSE,ch,0,victim,TO_CHAR);
      return;
    }
    assert(GET_POS(victim) > POSITION_DEAD);

    /* You can't damage an immortal! */
    if ((GET_LEVEL(victim) >= LEVEL_BUILDER) && !IS_NPC(victim))
      dam = 0;
    if (GET_POS(victim) == POSITION_SLEEPING){
      if (IS_AFFECTED(victim, AFF_SLEEP))
	affect_from_char(victim, SPELL_SLEEP);
      do_wake(victim,"",0,0);
    }
    if (IS_AFFECTED(ch, AFF_SNEAK)
	&& (GET_SKILL(ch, SKILL_SNEAK) < number(0,31)))
      affect_from_char(ch, SKILL_SNEAK);
   
    if (ch->specials.mount && (GET_SKILL(ch, SKILL_CAVALRY) < number(0,31)))
      if (throw_rider(ch))
	return;
   
    if (victim != ch) {
      if (GET_POS(ch) > POSITION_STUNNED) {
	if (!(ch->specials.fighting))
	  set_fighting(ch, victim);

	if (IS_NPC(ch) && IS_NPC(victim) && victim->master &&
	    ((GET_INT(ch) > 10 && !number(0,10))
	     || (GET_INT(ch) <= 10 && GET_INT(ch) > 7 && !number(0,70))
	     || (GET_INT(ch) <= 7 && GET_INT(ch) > 3 && !number(0,120)) )
	    && CAN_SEE(ch, victim->master)
	    && IS_AFFECTED(victim, AFF_CHARM) && 
	    (victim->master->in_room == ch->in_room)) {
	  if (ch->specials.fighting)
	    stop_fighting(ch);
	  act("$n realises who's the boss.",FALSE,ch,0,0,TO_ROOM);
	  hit(ch, victim->master, TYPE_UNDEFINED);
	  return;
	}
      }

      if (GET_POS(victim) > POSITION_STUNNED) {
	if (!(victim->specials.fighting))
	  set_fighting(victim, ch);
	if (IS_NPC(victim) && IS_SET(victim->specials2.act, MOB_MEMORY) && 
	    !IS_NPC(ch) && (GET_LEVEL(ch) < LEVEL_BUILDER))
	  remember(victim, ch);
      }
    }
   

    if (victim->master == ch)
      stop_follower(victim);

    /*    if (IS_AFFECTED(ch, AFF_INVISIBLE))
      appear(ch);*/

    if (IS_AFFECTED(victim, AFF_SANCTUARY) && attacktype > MAX_SPELL_NO)
      dam = dam / 2;  /* 1/2 physical damage when sanctuary */

    if (!pk_allowed) {
      check_killer(ch, victim);

      if (PLR_FLAGGED(ch, PLR_KILLER))
	dam = 0; 
    }

    dam = MAX(dam, 0);

    w_type = attacktype -TYPE_HIT;
    if (!IS_NPC(ch) && victim != ch  && hit_loc == 3
	&& (attacktype == TYPE_SLASH || attacktype == TYPE_CLEAVE))
      bhead = GET_SKILL(ch,SKILL_BEHEAD)*GET_LEVEL(ch)/MAX(2,GET_LEVEL(victim));
    if (bhead && (number(GET_LEVEL(victim),600) - GET_LEVEL(ch)/2) < bhead/10)
      {
	buf = replace_string(behead_messages.to_char,
			     attack_hit_text[w_type].singular, attack_hit_text[w_type].plural);
	act(buf, FALSE, ch, ch->equipment[WIELD], victim, TO_CHAR);
	buf = replace_string(behead_messages.to_room,
			     attack_hit_text[w_type].singular, attack_hit_text[w_type].plural);
	act(buf, FALSE, ch, ch->equipment[WIELD], victim, TO_NOTVICT);	       
	buf = replace_string(behead_messages.to_victim,
			     attack_hit_text[w_type].singular, attack_hit_text[w_type].plural);
	act(buf, FALSE, ch, ch->equipment[WIELD], victim, TO_VICT);
	GET_HIT(victim) = -100;
	update_pos(victim);
      }
    
    else if (GET_POS(victim) > POSITION_DEAD){
      GET_HIT(victim) -= dam;
      
      if (ch != victim && !IS_NPC(ch))
	gain_exp(ch, dam,0);
      
      update_pos(victim);
      
      if (!ch->equipment[WIELD] && !ch->specials.attack_type)
	atcktype = TYPE_HIT;
      else
	atcktype = attacktype;
      for (i = 0; i < MAX_MESSAGES; i++)
	{
	  if (fight_messages[i].a_type == atcktype){
	    diemessage = &fight_messages[i].msg->die_msg;
	    break;}
	}
       
      if ((attacktype >= TYPE_HIT) && (attacktype <= TYPE_WHIP)) {
	if (GET_POS(victim) == POSITION_DEAD
	    && *diemessage->attacker_msg)
	  {
	    act(diemessage->attacker_msg,
		FALSE, ch, ch->equipment[WIELD], victim, TO_CHAR);
	    act(diemessage->victim_msg,
		FALSE, ch, ch->equipment[WIELD], victim, TO_VICT);
	    act(diemessage->room_msg,
		FALSE, ch, ch->equipment[WIELD], victim, TO_NOTVICT);
	  }
	else {
	  dam_message(dam, ch, victim, attacktype, hit_loc);
	  if (IS_MOB(ch) && MOB_FLAGGED(ch, MOB_POISONOUS)
	      && (attacktype >= TYPE_CLAW )){
	    if (dam && (number(0,10) < MIN(1,GET_LEVEL(ch)/10)))
	      poison(ch, victim, 0);
	  }
	}
	
      }
      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_NPC(victim) && (GET_LEVEL(victim) >= LEVEL_BUILDER)) {
	      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 (!saved) {
	      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 { /* tagert saved */
	      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);
	    }
	  }
	}
      }
   
      /* chance of damaging equipment if blow > 25 points damage) */
      if ((IS_NPC(ch) && dam >= 30) || (!IS_NPC(ch) && dam >= 35))
	if (dam < number(1,250) && ((attacktype >= TYPE_HIT)
				    && (attacktype <= TYPE_WHIP)))
	  {
	    where = number(0,17);
	    if (victim->equipment[where])
	      {
		obj_tmp = unequip_char(victim,where);
		if (IS_SET(obj_tmp->obj_flags.extra_flags,ITEM_FRAGILE))
		  obj_tmp->obj_flags.value[4] += number(1,11);
		else
		  obj_tmp->obj_flags.value[4] += number(0,4);
		if (obj_tmp->obj_flags.value[4] >10)
		  {
		    obj_to_room(obj_tmp,victim->in_room, FALSE);
		    scrap_item(obj_tmp);
		  }
		else if (where != WIELD) {
		  act("$p is crushed.",FALSE,
		      victim,obj_tmp,0,TO_ROOM);
		  act("$p is crushed.",FALSE,
		      victim,obj_tmp,0,TO_CHAR);
		  equip_char(victim,obj_tmp,where);
			   
		}
		else
		  equip_char(victim,obj_tmp,where);
	      }
	  }
    }
    
    /* Use send_to_char -- act() doesn't send message if you are DEAD. */
    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.\r\n", 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.\r\n", victim);
      break;
    case POSITION_STUNNED:
      act("$n is stunned, but will probably regain consciousness again.", TRUE, victim, 0, 0, TO_ROOM);
      send_to_char("You're stunned, but will probably regain consciousness again.\r\n", 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...\r\n", victim);
      break;

    default:  /* >= POSITION SLEEPING */
      if (dam > (GET_MAX_HIT(victim) / 5))
	act("That Really did HURT!", FALSE, victim, 0, 0, TO_CHAR);

      if (GET_HIT(victim) < (GET_MAX_HIT(victim) / 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->specials2.act, MOB_WIMPY))
	    do_flee(victim, "", 0, 0);
	}
      }

      if (!IS_NPC(victim) && WIMP_LEVEL(victim) && victim != ch && 
          GET_HIT(victim) < WIMP_LEVEL(victim)) {
	send_to_char("You panic, and attempt to flee!\r\n", victim);
	if (GET_POS(victim) <= POSITION_STANDING)
	  do_stand(victim, "", 0, 0);	 
	do_flee(victim, "", 0, 0);
      }

      break;
    }
    if (!IS_NPC(victim) && !(victim->desc)) 
      do_flee(victim, "", 0, 0);
    if (!AWAKE(victim))
      if (victim->specials.fighting)
	stop_fighting(victim);

    if (GET_POS(victim) == POSITION_DEAD) {
      if (IS_NPC(victim) || (victim->desc && victim != ch))
	if (IS_AFFECTED(ch, AFF_GROUP)) {
	  group_gain(ch, victim);
	} 
	else {
	  exp = GET_EXP(victim);
	  exp = MIN(exp,3000*GET_LEVEL(ch));
	  sprintf(buf2, "You receive %d experience points.\r\n", exp);
	  send_to_char(buf2, ch);
	  gain_exp(ch, exp,1);
	  gain_social_standing(ch, victim, MODE_KILL);       	  
	  change_alignment(ch, victim);
	  if (auto_save) {
	    /* individuals saved 50% of the time to avoid lag */
	    if (!(number(0, 1)))
	      save_char(ch, NOWHERE);
	  }
	}
      if (!IS_NPC(victim)) {
	if (IS_NPC(ch)){
	  sprintf(bufer,"Ha! %s died too easily!",GET_NAME(victim));
	  sprintf(bufer2,"I need a tougher challenge!");
	  sprintf(buf2, "%s killed by %s at %s", GET_NAME(victim), GET_NAME(ch),
		  world[victim->in_room].name);
	  mudlog(buf2, BRF, LEVEL_BUILDER, TRUE);
	}
      }
      spell_damage_equipment(ch, victim, attacktype, dam);


      if (!IS_NPC(victim))
	if (IS_NPC(ch) &&
	    (GET_LEVEL(ch) > 10 || IS_SET(ch->specials2.act,MOB_CAN_SPEAK))){
	  do_gen_com(ch,bufer,0,5);
	  do_gen_com(ch,bufer2,0,5);
	}
      
      obj_tmp = die(victim);      
      
    }
}

int get_prof(struct char_data *ch, int *w_type, int type)
{
    int prof;
    struct obj_data *wielded = 0;
    if (type == SKILL_DUAL_WIELD) {
      if (ch->equipment[HOLD] &&
	  (ch->equipment[HOLD]->obj_flags.type_flag == ITEM_WEAPON))
	wielded = ch->equipment[HOLD];
    }
    else {
      if (ch->equipment[WIELD] && 
	  (ch->equipment[WIELD]->obj_flags.type_flag == ITEM_WEAPON)) 
	wielded = ch->equipment[WIELD];
    }
    if (wielded) {
      switch (wielded->obj_flags.value[3]) {
      case 0:
      case 1:
	*w_type = TYPE_BLUDGEON;
	if (!IS_NPC(ch)){
	  prof = PROF_CLUB;
	  if (is_two_handed(ch,wielded))
	    prof++;}
	break;
      case 2:
	*w_type = TYPE_PIERCE;
	if (!IS_NPC(ch)){
	  prof = PROF_DAGGER;
	  if (is_two_handed(ch,wielded))
	    prof = PROF_SWORD +1;
	  else if (GET_SIZE(ch) > GET_OBJ_SIZE(wielded) +1)
	    prof = PROF_SWORD;}
	break;
      case 3:
	*w_type = TYPE_SLASH;
	if (!IS_NPC(ch)){      
	  prof = PROF_SWORD;
	  if (is_two_handed(ch,wielded))
	    prof++;}
	break;
      case 4:
	*w_type = TYPE_SMITE;
	if (!IS_NPC(ch)){      
	  prof = PROF_HAMMER;
	  if (is_two_handed(ch,wielded))
	    prof++;	  }
	break;
      case 5:
	*w_type = TYPE_CLEAVE;
	if (!IS_NPC(ch)){      
	  prof = PROF_AXE;
	  if (is_two_handed(ch,wielded))
	    prof++;	  }
	break;
      case 6:
	*w_type = TYPE_NO_BS_PIERCE;
	if (!IS_NPC(ch)){      
	  prof = PROF_SPEAR;
	  if (!is_two_handed(ch,wielded))
	    prof = PROF_DAGGER;}
	break;
      case 7:
	*w_type = TYPE_CLAW;
	if (!IS_NPC(ch))      
	  prof = PROF_CLAW;
	break;
      case 8:
	*w_type = TYPE_BITE;
	break;
      case 9:
	*w_type = TYPE_STING;
	break;
      case 10:
	*w_type = TYPE_CRUSH;
	break;
      case 11:
	*w_type = TYPE_PECK;
	break;
      case 12:
	*w_type = TYPE_BUTT;
	break;
      case 13:
	*w_type = TYPE_KICK;
	break;
      case 14:
	*w_type = TYPE_WHIP;
	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;
    }
    return prof;
}
int compute_multi_attacks(struct char_data *ch, struct obj_data *wielded, int type)
{
  int raw_multi, nattacks=0, weap_multi=0, weap_weight=0, inatt=0;

  if (type == SKILL_BACKSTAB)
    return(1);
  
  raw_multi = GET_LEVEL(ch)/2; /* raw multiple attach percent chance */
  raw_multi = (raw_multi*GET_DEX(ch)*GET_DEX(ch))/255; /* dexterity bonus */
  
  if (wielded) {       
    weap_weight = GET_OBJ_WEIGHT(wielded)/10;
    if (is_two_handed(ch,wielded))
      weap_weight /= 2;
    if (weap_weight >= 3*GET_STR(ch)/4)
      weap_multi = (30 - (40*weap_weight)/GET_STR(ch)) + 30;
    else
      weap_multi = ((40*weap_weight)/GET_STR(ch) - 30) + 30;
    weap_multi = MAX(1,weap_multi);
    weap_multi = MIN(9,weap_multi);       
    raw_multi *= weap_multi;
    raw_multi /= 10.;
  }
  else {
    if (IS_NPC(ch))
      raw_multi += (3*GET_LEVEL(ch))/20;
    else
      raw_multi += GET_SKILL(ch, SKILL_FISTICUFFS);
  }    
  /* ok now determine how many attacks we can have max 6 */
  raw_multi = MIN(149,raw_multi);
  nattacks = 1;
  for (inatt=0;inatt<9;inatt++){
    if (number(0,150) < raw_multi) /*probability of more attacks goes down*/
      nattacks++;  
    raw_multi *= 2;    
    raw_multi /= 3;
  }
  if (type == SKILL_DUAL_WIELD) 
    if (IS_NPC(ch)) 
      nattacks = (nattacks*(GET_LEVEL(ch)/6))/30;
    else
      nattacks = (2*nattacks*GET_SKILL(ch, SKILL_DUAL_WIELD))/60;
  
  
  nattacks = MIN(10,nattacks);
  return(nattacks);
}

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,hit_loc,hit_location,die,num;
  int	victim_ac, thaco, weap_weight,weap_multi;
  int	dam,raw_multi,nattacks,inatt,icnt,damg,prof=0, prof_factor=0;
  int   diceroll=0, bonus=0, damroll=0, str_dam_bon=0, pos_dam_bon=0;
  int   raw_dam=0, dam_stop=0;
  
  
  if (IS_SET(world[ch->in_room].room_flags, PEACEFULL)) 
    {
      act("You feel too peaceful to contemplate violence.",
	  FALSE,ch,0,0,TO_CHAR);
      return;
    }
  
  if (ch->in_room != victim->in_room) {
    logg("SYSERR: NOT SAME ROOM WHEN FIGHTING!");
    return;
  }
  if (GET_POS(ch) < POSITION_SLEEPING) /* can't attack if stunned */
    return;
  
  if (IS_AFFECTED(ch, AFF_HIDE))
    REMOVE_BIT(ch->specials.affected_by, AFF_HIDE);
  
  if (ch->equipment[HOLD])
    held = ch->equipment[HOLD];
  
  prof = get_prof(ch, &w_type, type);
  if (ch->equipment[WIELD] && 
      (ch->equipment[WIELD]->obj_flags.type_flag == ITEM_WEAPON)) 
    wielded = ch->equipment[WIELD];
  if (type == SKILL_DUAL_WIELD)
    wielded = ch->equipment[HOLD];
  /* Calculate the raw armor including magic armor */
  /* The lower AC, the better                      */
  
  thaco = 20;

  bonus = combat_bonus(ch);

  thaco -= str_app[STRENGTH_APPLY_INDEX(ch)].tohit;

  if (!affected_by_spell(ch, SPELL_ENDURANCE))   
    thaco -= bonus;

  if (type == SKILL_DUAL_WIELD)
    thaco -= GET_HITROLL2(ch);
  else
    thaco -= GET_HITROLL(ch);
  
  thaco -= (2*(GET_DEX(ch) - 13))/3;   /* So does dexterity */
  
  if (!IS_NPC(ch) && (GET_LEVEL(ch) < 5))  /* helpt the newbies out */
    thaco -= (GET_LEVEL(ch)*GET_LEVEL(ch))/3;
  
  thaco -= GET_LEVEL(ch)/10;

  thaco = MIN(20, thaco);
  thaco = MAX(-20, thaco);

  
  /* multiple attacks */
  nattacks = compute_multi_attacks(ch, wielded, type);
  
  if (!IS_NPC(ch)){
    if ((type == SKILL_DUAL_WIELD)
	&& (num = number(0,31) > GET_SKILL(ch, SKILL_DUAL_WIELD))){
      if (num == 0)   /* botch */
	if (!number(0,31) && (GET_SKILL(ch, SKILL_DUAL_WIELD) <= 29)) {
	  SET_SKILL(ch, SKILL_DUAL_WIELD, GET_SKILL(ch, SKILL_DUAL_WIELD) +1);
	  send_to_char("You feel more skillful.\r\n",ch);
	  return;
	}
      return;
    }
  } 

  
  /* ok where are we going to hit ?*/
  
  if (ch->in_room != victim->in_room) /* victim may have fled or  */
    return;                         /* otherwise left combat    */
  if (!affected_by_spell(ch, SPELL_ENDURANCE))
    GET_MOVE(ch) -= 1*(encumberance_level(ch) +1);/*lose movement points per blow */
  else
    GET_MOVE(ch) -= 1;
  
  hit_location = number(1,60); 
  if (hit_location <= 60)
    hit_loc = 3;
  if (hit_location <= 56)
    hit_loc = 2;
  if (hit_location <= 40)
    hit_loc = 1;
  if (hit_location <= 30)
    hit_loc = 0;
  
  diceroll = number(0, 50);
  
  if (prof >= PROF_SWORD && prof <= PROF_CLAW)
    num = GET_SKILL(ch,prof);
  else if (!IS_MOB(ch) && !wielded)
    num = GET_SKILL(ch,SKILL_FISTICUFFS);
  else
    num =0;
  
  if(!IS_NPC(ch) && (prof >= PROF_SWORD) && (prof <= PROF_CLAW) && (num < 30)){
    if (!diceroll  && !number(0,3*num*num + 1))
      { 
	send_to_char("You feel more proficient.\r\n",ch);
	SET_SKILL(ch,prof,num + 1);
      }
  }
  else if (!IS_NPC(ch) && !wielded) {
    if (!diceroll  && !number(0, 2*num*num + 1))
      { 
	send_to_char("You feel more proficient.\r\n",ch);
	  SET_SKILL(ch,SKILL_FISTICUFFS,num + 1);
      }
  }
  
  switch (hit_loc){
  case 0:
    victim_ac  = GET_BODY_AC(victim);
    break;    
  case 1:
    victim_ac  = GET_LEGS_AC(victim);
    break;
  case 2:
    victim_ac  = GET_ARMS_AC(victim);
    break;
  case 3:
    victim_ac  = GET_HEAD_AC(victim);
    break;
  default:
    victim_ac  = GET_BODY_AC(victim);
  }
  if (AWAKE(victim))
    victim_ac += dex_app[GET_DEX(victim)].defensive;

  if (!can_see_char(ch,victim))
    victim_ac -= 15;
  
  victim_ac = MAX(-100, victim_ac);
  victim_ac = MIN(100,  victim_ac);  
  
  if (!IS_NPC(ch) && num)
    prof_factor = 50 -  (99*num)/30;
  else if (IS_NPC(ch))
    prof_factor = 0;
  else 
    prof_factor = 100;
  
  prof_factor /= 4;
  
  if ((GET_POS(victim) < POSITION_SLEEPING)
      || IS_AFFECTED(victim, AFF_PARALYSIS))
      diceroll = 50;
  
  
  if ((diceroll < 50) && AWAKE(victim) && (type != SKILL_BACKSTAB) &&
	 (!diceroll ||
	  (diceroll < (thaco + prof_factor + (100 - victim_ac)/4))))
      damage(ch, victim, 0, w_type,hit_loc, 0); /* MISSED */
  else
    {
      str_dam_bon  = str_app[STRENGTH_APPLY_INDEX(ch)].todam;
      dam = str_dam_bon;
      
      if (!affected_by_spell(ch, SPELL_ENDURANCE))
	dam += bonus;
      
      if (type == SKILL_DUAL_WIELD)
	damroll = GET_DAMROLL2(ch);
      else
	damroll = GET_DAMROLL(ch);
      
      dam += damroll;
      
      if (!wielded) {
	if (IS_NPC(ch) && ch->specials.damsizedice){
	  raw_dam = dice(ch->specials.damnodice, ch->specials.damsizedice);
	}
	else 
	    raw_dam = number(0,MIN(1,GET_SKILL(ch, SKILL_FISTICUFFS)/2));
      }
      else{
	damg = MAX(0,wielded->obj_flags.value[4]);
	if (wielded->obj_flags.value[2])
	  raw_dam = (dice(wielded->obj_flags.value[1],
			  wielded->obj_flags.value[2]))*(10-damg)/10;
	raw_dam *= (num + 120);
	raw_dam /= 150;
      }
      dam += raw_dam;
	
      if (GET_POS(victim) < POSITION_STANDING)
	pos_dam_bon = (dam*(POSITION_STANDING - GET_POS(victim)))/3.;
      dam += pos_dam_bon;
      
      switch (hit_loc){
      case 0:
	dam_stop = GET_BODY_STOPPING(ch);
	break;
      case 1:
	dam_stop = GET_LEGS_STOPPING(ch);
	break;
      case 2:
	dam_stop = GET_ARMS_STOPPING(ch);
	break;
      case 3:
	dam_stop = GET_HEAD_STOPPING(ch);
	break;
      default:
	dam_stop = GET_BODY_STOPPING(ch);
      }
      
      if (type == SKILL_BACKSTAB) {
	dam *= MIN(GET_LEVEL(ch) - spell_lev(ch,SKILL_BACKSTAB)
		   + GET_SKILL(ch,SKILL_BACKSTAB)/2,75);
	dam /= 5;
	dam *= GET_DEX(ch);
	dam /= 15;
	die = stress_die();
	dam += die;
	sprintf(buf,"Backstab: die = %d, level = %d dam = %d\r\n",
		die,spell_lev(ch, SKILL_BACKSTAB),dam);
	if (PRF_FLAGGED(ch, PRF_DEBUG))
	  send_to_char(buf,ch);
	if (affected_by_spell(victim, SPELL_ENCASE_IN_ICE)){
	  if (!number(0,3) && (dam > 5)){
	    act("$n's backstab shatters the ice encasing $N!",
		TRUE,ch,0,victim,TO_NOTVICT);
	    act("$n's backstab shatters the ice encasing you!",
		TRUE,ch,0,victim,TO_VICT);
	    act("Your backstab shatters the ice encasing $N.",
		TRUE,ch,0,victim,TO_CHAR);
	    damage(ch,victim,dam/4,SKILL_BACKSTAB,-1,0);
	    affect_from_char(victim, SPELL_ENCASE_IN_ICE);
	  }
	  else {
	    act("$n's backstab glances off the ice encasing $N!",
		TRUE,ch,0,victim,TO_NOTVICT);
	    act("$n's backstab glances off the ice encasing you!",
		TRUE,ch,0,victim,TO_VICT);
	    act("Your backstab glances off the ice encasing $N.",
		TRUE,ch,0,victim,TO_CHAR);
	    if (!(ch->specials.fighting) && (dam > 5))
	      set_fighting(ch, victim);	     
	  }
	}
	else
	  damage(ch,victim,dam,SKILL_BACKSTAB,-1,0);
      }
      else{
	dam -= dam_stop;
	
	dam = MAX(1, dam);  /* Not less than 0 damage */
	
	if (affected_by_spell(victim, SPELL_ENCASE_IN_ICE)){
	  if (!number(0,3) && (dam > 5)){
	    act("$n's blow shatters the ice encasing $N!",
		TRUE,ch,0,victim,TO_NOTVICT);
	    act("$n's blow shatters the ice encasing you!",
		TRUE,ch,0,victim,TO_VICT);
	    act("Your blow shatters the ice encasing $N.",
		TRUE,ch,0,victim,TO_CHAR);
	    dam /= 4;	     
	    damage(ch,victim,dam,w_type,hit_loc,0);
	    affect_from_char(victim, SPELL_ENCASE_IN_ICE);
	  }
	  else {
	    act("$n's blow glances off the ice encasing $N!",
		TRUE,ch,0,victim,TO_NOTVICT);
	    act("$n's blow glances off the ice encasing you!",
		TRUE,ch,0,victim,TO_VICT);
	    act("Your blow glances off the ice encasing $N.",
		TRUE,ch,0,victim,TO_CHAR);
	    if (!(ch->specials.fighting) && (dam > 5))
	      set_fighting(ch, victim);	     
	  }
	}
	else
	  damage(ch,victim,dam,w_type,hit_loc,0);
      }
    }
  nattacks--;
  
  if (nattacks > 0)
    for (icnt=0;icnt<nattacks;icnt++){
      if (type == SKILL_BACKSTAB)
	break;
      if (!victim)
	return;                    
      if (ch->in_room != victim->in_room)/* victim may have fled or */
	return;                    /* otherwise left combat */
      
      GET_MOVE(ch) -= 1;        /* loose a movement point per blow */
      
      hit_location = number(1,60); 
      if (hit_location <= 60)
	hit_loc = 3;
      if (hit_location <= 56)
	hit_loc = 2;
      if (hit_location <= 40)
	hit_loc = 1;
      if (hit_location <= 30)
	hit_loc = 0;
      
      diceroll = number(1, 50);

      if(!IS_NPC(ch) && (prof >= PROF_SWORD) && (prof <= PROF_CLAW) && (num < 30)){
	if (!diceroll  && !number(0,3*num*num + 1))
	  { 
	    send_to_char("You feel more proficient.\r\n",ch);
	    SET_SKILL(ch,prof,num + 1);
	  }
      }
      else if (!IS_NPC(ch) && !wielded) {
	if (!diceroll  && !number(0, 2*num*num + 1))
	  { 
	    send_to_char("You feel more proficient.\r\n",ch);
	    SET_SKILL(ch,SKILL_FISTICUFFS,num + 1);
	  }
      }
      
      switch (hit_loc){
      case 0:
	victim_ac  = GET_BODY_AC(victim);
	break;
      case 1:
	victim_ac  = GET_LEGS_AC(victim);
	break;
      case 2:
	victim_ac  = GET_ARMS_AC(victim);
	break;
      case 3:
	victim_ac  = GET_HEAD_AC(victim);
	break;
      default:
	victim_ac  = GET_BODY_AC(victim);	
      }
      if (AWAKE(victim))
	victim_ac -= dex_app[GET_DEX(victim)].defensive;
      
      victim_ac = MAX(-100, victim_ac);  /* -100 is lowest */
      if (!IS_NPC(ch) && num)
	prof_factor = 50 -  (99*num)/30;
      else if (IS_NPC(ch))
	prof_factor = 0;
      else 
	prof_factor = 100;

      prof_factor /= 4;
      
      if ((((diceroll < 50) && AWAKE(victim)) && 
	   (!diceroll
	    || (diceroll 
		<  (thaco + prof_factor + (100 - victim_ac)/4))))) {
	damage(ch, victim, 0, w_type,hit_loc,0);
      } else {
	
	str_dam_bon  = str_app[STRENGTH_APPLY_INDEX(ch)].todam;
	dam = str_dam_bon;
	
	if (type == SKILL_DUAL_WIELD)
	  damroll = GET_DAMROLL2(ch);
	else
	  damroll = GET_DAMROLL(ch);
	dam += damroll;
	
	if (!wielded) {
	  if (IS_NPC(ch) &&  ch->specials.damsizedice) {
	    raw_dam = dice(ch->specials.damnodice, ch->specials.damsizedice);
	  } else
	    raw_dam = number(0, MIN(1,GET_SKILL(ch, SKILL_FISTICUFFS)/2));
	}
	else{
	  damg = MAX(0,wielded->obj_flags.value[4]);
	  if (wielded->obj_flags.value[2])
	    raw_dam = (dice(wielded->obj_flags.value[1],
			    wielded->obj_flags.value[2]))*(10-damg)/10;
	  raw_dam *= (num + 120);
	  raw_dam /= 150;
	}
	dam += raw_dam;
	if (GET_POS(victim) < POSITION_STANDING)
	  pos_dam_bon = (dam*(POSITION_STANDING - GET_POS(victim)))/3.;
	dam += pos_dam_bon;
	/* 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 */
	  
	switch (hit_loc){
	case 0:
	  dam_stop = GET_BODY_STOPPING(ch);
	  break;
	case 1:
	  dam_stop = GET_LEGS_STOPPING(ch);
	  break;
	case 2:
	  dam_stop = GET_ARMS_STOPPING(ch);
	  break;
	case 3:
	  dam_stop = GET_HEAD_STOPPING(ch);
	  break;
	default:
	  dam_stop = GET_BODY_STOPPING(ch);	  
	}
	dam -= dam_stop;
	dam = MAX(1, dam);  /* Not less than 0 damage */
	add_event(icnt/2, EVENT_COMBAT, dam, w_type, hit_loc,0,0,ch,victim); 
      }
    }
}


/* control the fights going on */
void	perform_violence(void)
{
    struct char_data *ch;
    int i;
    bool found = FALSE;
    struct obj_data *sheath;
   
   for (ch = combat_list; ch; ch = combat_next_dude) {
      combat_next_dude = ch->next_fighting;
      assert(ch->specials.fighting);
      if (IS_NPC(ch) && ch->specials.timer)
	ch->specials.timer--;
      if (AWAKE(ch) && (ch->in_room == ch->specials.fighting->in_room)) {
	if (!ch->equipment[WIELD])
	  for (i=0; i<= MAX_WEAR; i++)
	    if (sheath = ch->equipment[i])
	      if ((GET_ITEM_TYPE(sheath) == ITEM_SCABBARD) &&
		  (sheath->contains))
		found=TRUE;
	if (found)
	  do_draw(ch,"",0,0);
	if (GET_POS(ch) < POSITION_STANDING && IS_NPC(ch))
	  do_stand(ch,"",0,0);
	if (!IS_AFFECTED(ch, AFF_PARALYSIS)
	    || (GET_LEVEL(ch) >= LEVEL_BUILDER)) {
	  hit(ch, ch->specials.fighting, TYPE_UNDEFINED);
	  if (ch->equipment[HOLD]
	      && (ch->equipment[HOLD]->obj_flags.type_flag == ITEM_WEAPON)
	      && ch->specials.fighting)
	    hit(ch, ch->specials.fighting, SKILL_DUAL_WIELD);
	}
      } else { /* Not in same room */
	stop_fighting(ch);
      }
   }
}


void botch_kick(struct char_data *ch, struct char_data *vict)
{
  ACMD(do_say);
  int die;
  die = stress_die();
  
  if(die < 6){
    act("$n kicks out in a very energetic fashion.",FALSE, ch,0,vict,TO_ROOM);
    if (ch->specials.fighting && ch->specials.fighting == vict){
      if (GET_BODY_STOPPING(vict) > 0){
	act("Your kick bounces harmlessly off $N's armour."
	    ,FALSE, ch,0,vict,TO_CHAR);
	act("$n's kick bounces harmlessly off $N's armour."
	    ,FALSE, ch,0,vict,TO_NOTVICT);
	act("$n's kick bounces harmlessly off your armour."
	    ,FALSE, ch,0,vict,TO_VICT);}
      else if ((GET_POS(vict) > POSITION_SLEEPING) &&
	       !IS_AFFECTED(vict, AFF_PARALYSIS)){
	act("$N easily dodges your kick.",FALSE, ch,0,vict,TO_CHAR);
	act("$N easily dodges $n's kick.",FALSE, ch,0,vict,TO_NOTVICT);
	act("You easily dodge $n's kick.",FALSE, ch,0,vict,TO_VICT);
      }
      WAIT_STATE(ch, PULSE_VIOLENCE * 3);       
      return;
    }
    else{
      act("You try to kick $N, but miss by miles.",FALSE, ch,0,vict,TO_CHAR);
      if (IS_NPC(vict) && CAN_SPEAK(vict))
	do_say(vict,"You'd better watch out. You could hurt yourself doing that.",0,0);
      return;
    }   
  }
  else {
    act("$n kicks out in a very energetic fashion.",FALSE, ch,0,vict,TO_ROOM);
    if (ch->specials.fighting && ch->specials.fighting == vict){
      if (GET_BODY_STOPPING(vict) > 0){
	act("Your kick bounces harmlessly off $N's armour."
	    ,FALSE, ch,0,vict,TO_CHAR);
	act("You fall over.",FALSE, ch,0,vict,TO_CHAR);
	act("$n falls flat in $s face.",FALSE, ch,0,vict,TO_ROOM);
	act("$n's kick bounces harmlessly off $N's armour."
	    ,FALSE, ch,0,vict,TO_NOTVICT);
	act("$n's kick bounces harmlessly off your armour."
	    ,FALSE, ch,0,vict,TO_VICT);}
      else{
	if ((GET_POS(vict) > POSITION_SLEEPING) &&
	    !IS_AFFECTED(vict, AFF_PARALYSIS)){
	  act("You easily dodge $n's kick.",FALSE, ch,0,vict,TO_VICT);
	  act("$N easily dodges your kick.",FALSE, ch,0,vict,TO_CHAR);
	  act("$N easily dodges $n's kick.",FALSE, ch,0,vict,TO_NOTVICT);
	}
	act("You fall over.",FALSE, ch,0,vict,TO_CHAR);
	act("$n falls flat in $s face.",FALSE, ch,0,vict,TO_ROOM);  
	
      }
      GET_POS(ch) = POSITION_SITTING;
      WAIT_STATE(ch, PULSE_VIOLENCE * 4);       
      return;
    }
    else{
      act("You try to kick $N, but miss by miles.",FALSE,
	  ch,0,vict,TO_CHAR);
      act("You fall over.",FALSE, ch,0,vict,TO_CHAR);
      act("$n falls flat in $s face.",FALSE, ch,0,vict,TO_ROOM); 	
      if (IS_NPC(vict) && CAN_SPEAK(vict))
	do_say(vict,"Hey!",0,0);
      act("$n is enraged.",TRUE,vict,0,0,TO_ROOM);
      GET_POS(ch) = POSITION_SITTING;
      hit(vict,ch,TYPE_UNDEFINED);
      WAIT_STATE(ch, PULSE_VIOLENCE * 4);
      return;
    }   
  }
  return;
}

int stress_die(void)
{
    int die,bon;
    bon=1;
    while((die = number(0,9)) == 1 && (bon < (1<< 11)))
	bon <<= 1;
    die *= bon;
    return (MAX(0,die));

}