/* ************************************************************************
*  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 <string.h>
#include <assert.h>
#include <malloc.h>
#include <stdlib.h>

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

#define NTAUNT 14

static char *taunt[]={
  "HOORAY!! I just killed %s!",
  "HA HA %s! You stink!",
  "I am happy to report that %s is dead!",
  "Long live %s! HA, HA, HA!!",
  "HA! Looks like %s just kicked the bucket!",
  "Bid a fond farewell to poor old dead %s!",
  "My sympathies to the %s family!",
  "Our friend %s has bitten the proverbial bullet!",
  "Looks like a quick trip to the Temple for %s!",
  "I sure hope %s likes reading that menu!",
  "My old pal %s just bought the farm!",
  "Funeral services for %s will be held at the city dump!",
  "Farewell to dear old %s, who won't be missed!",
  "Die %s, die!"
};

/* 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 index_data *mob_index;
extern struct weapon_spell_list wsplist[];
extern int pcdeaths,mobdeaths;
extern struct room_data *world;
extern struct message_list fight_messages[MAX_MESSAGES];
extern struct obj_data  *object_list;

/* 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 rescuesub(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     */
  {"blast", "blasts"},
  {"splat", "splats"},
  {"spank", "spanks"},           /* TYPE_STING    */
  {"crush", "crushes"},          /* TYPE_CRUSH    */
  {"burn" , "burns"}
};


/* The Fight related routines */

void weapon_effects(struct char_data *ch, struct char_data *vict,
 struct obj_data *weapon);

void appear(struct char_data *ch)
{
  act("$n slowly fade 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);
}

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

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

 if(!(ch->specials.fighting)) return;
 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;
 GET_POS(ch) = POSITION_STANDING;
 update_pos(ch);
}

#define MAX_NPC_CORPSE_TIME 3
#define MAX_PC_CORPSE_TIME 48
#define MAX_POOF_TIME 8

void make_corpse(struct char_data *ch)
{
 struct obj_data *corpse, *o, *xo;
 struct obj_data *money; 
 char buf[MAX_STRING_LENGTH];
 int i;
#ifdef NEEDS_STRDUP
 char *strdup(char *source);
#endif
 struct obj_data *create_money( int amount );
 struct extra_descr_data *new_descr;

 if(!IS_NPC(ch)){
  if(GET_LEVEL(ch) >= (IMO>>4)){
   i=number(0,MAX_WEAR-1);
   CREATE(corpse, struct obj_data, 1);
   clear_object(corpse);
   corpse->obj_flags.wear_flags=1;
   corpse->obj_flags.weight=1000;
   corpse->name = strdup("gravestone");
   corpse->in_room = NOWHERE;
   corpse->item_number = NOWHERE;
   sprintf(buf,"A Gravestone for %s",GET_NAME(ch));
   corpse->short_description = strdup(buf);
   sprintf(buf,"%s's gravestone is here.",GET_NAME(ch));
   corpse->description = strdup(buf);
   CREATE(new_descr, struct extra_descr_data, 1);
   new_descr->keyword = strdup("gravestone");
   sprintf(buf,"It reads: Here lies %s, poor soul.\n\r",GET_NAME(ch));
   new_descr->description = strdup(buf);
   new_descr->next = corpse->ex_description;
   corpse->ex_description = new_descr;
   corpse->next = object_list;
   object_list = corpse;
   obj_to_room(corpse, ch->in_room);
  }
  return;
 }
/*
   Old corpse system here.
*/
 CREATE(corpse, struct obj_data, 1);
 clear_object(corpse);
 corpse->item_number = NOWHERE;
 corpse->in_room = NOWHERE;
 sprintf(buf,"corpse %s",GET_NAME(ch));
 corpse->name = strdup(buf);
 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->contains = ch->carrying;
 if((GET_GOLD(ch)>0)&&(IS_NPC(ch)||ch->desc)) {
  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.extra_flags = ITEM_NO_PUT_IN;
 corpse->obj_flags.value[0] = 0; /* You can't store stuff in a corpse */
 corpse->obj_flags.value[2] = mob_index[ch->nr].virtual;
 corpse->obj_flags.value[3] = 1; /* corpse identifyer */
 corpse->obj_flags.weight = GET_WEIGHT(ch)+IS_CARRYING_W(ch);
 if (IS_NPC(ch))
  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 (xo=ch->equipment[i]){
   obj_to_obj(unequip_char(ch, i), corpse);
   if(IS_SET(xo->obj_flags.extra_flags,ITEM_POOF)){
     SET_BIT(xo->obj_flags.extra_flags,ITEM_POOFSOON);
     if(GET_ITEM_TYPE(xo) == ITEM_WEAPON)
       xo->obj_flags.timer=24*number(1,7);
     else if(GET_ITEM_TYPE(xo) == ITEM_BOMB) {
       xo->obj_flags.timer = number(6,24);
     } else {
       xo->obj_flags.timer=MAX_POOF_TIME;
     }
   }
  }
 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);
 for(o=corpse->contains;o;o=xo){
  xo=o->next_content;
  if(GET_ITEM_TYPE(o)==ITEM_BOMB){
   obj_from_obj(o);
   obj_to_room(o,ch->in_room);
   act("An explosive device rolls out of the corpse.",FALSE,ch,0,0,TO_ROOM);
  }
 }
}


/* When ch kills victim */
void change_alignment(struct char_data *ch, struct char_data *victim)
{
 int al;
 
 if(IS_AFFECTED(ch,AFF_HOLDALIGN))
  return;
 al=(7*GET_ALIGNMENT(ch)-GET_ALIGNMENT(victim))/8;
 if(al < -1000) al = -1000;
 if(al >  1000) al =  1000;
 GET_ALIGNMENT(ch)=al;
}

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

 act("Your blood freezes as you hear $ns 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;
  }
 }
}

void raw_kill(struct char_data *ch)
{
 int i,j,n;
 struct char_data *tmp;

 if (ch->specials.fighting)
  stop_fighting(ch);
 death_cry(ch);
 if(IS_NPC(ch))
   mobdeaths++;
 else
   pcdeaths++;
 if(IS_SET(ch->specials.act,ACT_TRIBBLE)){
   n = ch->nr;
   j=number(0,10)>>2;
   for(i=0;i<j;i++){
     tmp = read_mobile(n,0);
     if(tmp)
       char_to_room(tmp,ch->in_room);
   }
 } else {
   make_corpse(ch);
 }
 if(IS_NPC(ch))
   extract_char(ch);
 else{
   ch->points.gold>>=1;
   do_rent(ch,0,0);
 }
}
void die(struct char_data *ch)
{
 int pen;
 struct affected_type *hjp;

 if(!IS_NPC(ch)){
   gain_exp(ch, -(int)(GET_EXP(ch)>>1));
   GET_META(ch) /= 2;
   pen = 1 + ch->points.max_hit/50;
   if (pen < 0)
     return;
   ch->points.max_hit -= pen;
   pen = 1 + ch->points.max_mana/50;
   if (pen < 0)
     return;
   ch->points.max_mana -= pen;
   pen = 1 + ch->points.max_move/50;
   if (pen < 0)
     return;
   ch->points.max_move -= pen;
   for(hjp = ch->affected; hjp; hjp = hjp->next)
     affect_remove(ch, hjp );
 }
 raw_kill(ch);
}
void group_gain(struct char_data *ch, struct char_data *victim, int levsum)
{
 char buf[256];
 int share, shareunit, t;
 struct char_data *k;
 struct follow_type *f;

 if(!levsum)
  return;
 shareunit = GET_EXP(victim)/levsum;
 if (!(k=ch->master))
  k=ch;
 if (IS_AFFECTED(k, AFF_GROUP) && (k->in_room == ch->in_room)) {
  if(levsum > 0){
   t = GET_LEVEL(k);
   share = shareunit * t;
  } else
   share = 0;
  sprintf(buf, "You receive your share of experience, which is worth %d.",
   share);
  act(buf, 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)){
   if(levsum > 0){
    t = GET_LEVEL(f->follower);
    share = shareunit * t;
   } else
    share = 0;
   sprintf(buf,"You receive your share of experience, which is worth %d.",
    share);
   act(buf,FALSE,f->follower,0,0,TO_CHAR);
   gain_exp(f->follower,share);
   change_alignment(f->follower, victim);
  }
 }
}

char *rep_string(char *str, char *weapon, int dam)
{
 static char buf[256];
 static char tmp[32];
 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 */
 sprintf(tmp," (%d Dam).",dam);
 strcat(buf,tmp);
 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 t;

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

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

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

   {"$n barely #W $N",
    "You barely #W $N",
    "$n barely #W you"}, /* 2 */

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

  {"$n #W $N hard",
   "You #W $N hard",
    "$n #W you hard"},

  {"$n #W $N very hard",
   "You #W $N very hard",
   "$n #W you very hard"},  /* 5 */

  {"$n #W $N extremely hard",
   "You #W $N extremely hard",
   "$n #W you extremely hard"},

  {"$n MASSACRES $N with $s #W",
   "You MASSACRE $N with your #W",
   "$n MASSACRES you with $s #W"}, /* 7 */

  {"$n PULVERIZES $N with $s #W",
   "You PULVERIZE $N with your #W",
   "$n PULVERIZES you with $s #W"},

  {"$n ANNIHILATES $N with $s #W",
   "You ANNIHILATE $N with your #W",
   "$n ANNIHILATES you with $s #W"},

  {"$n OBLITERATES $N with $s #W",
   "You OBLITERATE $N with your #W",
   "$n OBLITERATES you with $s #W"},  /* 10 */

  {"$n DISINTEGRATES $N with $s #W",
   "You DISINTEGRATE $N with your #W",
   "$n DISINTEGRATES you with $s #W"},

  {"$n BIFURCATES $N with $s #W",
   "You BIFURCATE $N with your #W",
   "$n BIFURCATES you with $s #W"},   /* 12 */

  {"$n VARIEGATES $N with $s #W",
   "You VARIEGATE $N with your #W",
   "$n VARIEGATES you with $s #W"},

  {"$n LAMINATES $N with $s #W",
   "You LAMINATE $N with your #W",
   "$n LAMINATES you with $s #W"},    /* 14 */

  {"$n MASTICATES $N with $s #W",
   "You MASTICATE $N with your #W",
   "$n MASTICATES you with $s #W"},

  {"$n REGURGITATES $N with $s #W",
   "You REGURGITATE $N with your #W",
   "$n REGURGITATES you with $s #W"}
 };
 w_type -= TYPE_HIT;   /* Change to base of table with text */
 wield = ch->equipment[WIELD];
 if (dam == 0) {
  t=0;
 } else if (dam <= 2) {
  t=1;
 } else if (dam <= 4) {
  t=2;
 } else if (dam <= 8) {
  t=3;
 } else if (dam <= 12) {
  t=4;
 } else if (dam <= 18) {
  t=5;
 } else if (dam <= 26) {
  t=6;
 } else if (dam <= 36) {
  t=7;
 } else if (dam <= 48) {
  t=8;
 } else if (dam <= 64) {
  t=9;
 } else if (dam <= 99) {
  t=10;
 } else if (dam <= 399) {
  t=11;
 } else if (dam <= 1599) {
  t=12;
 } else if (dam <= 4999) {
  t=13;
 } else if (dam <= 19999) {
  t=14;
 } else if (dam <= 99999) {
  t=15;
 } else {
  t=16;
 }
 buf=rep_string(dam_weapons[t].to_room,attack_hit_text[w_type].singular,dam);
 act(buf,FALSE,ch,wield,victim,TO_NOTVICT);
 buf=rep_string(dam_weapons[t].to_char,attack_hit_text[w_type].singular,dam);
 act(buf,FALSE,ch,wield,victim,TO_CHAR);
 buf=rep_string(dam_weapons[t].to_victim,attack_hit_text[w_type].singular,dam);
 act(buf,FALSE,ch,wield,victim,TO_VICT);
}

int getlevelsum(struct char_data *ch)
{
  struct char_data *ldr;
  struct follow_type *f;
  int room,t,levsum=0;

  room = ch->in_room;
  ldr = (ch->master) ? ch->master : ch;
  if(IS_AFFECTED(ldr,AFF_GROUP) && (ldr->in_room == room)){
    t=GET_LEVEL(ldr);
    levsum=t;
  }
  for(f=ldr->followers; f; f=f->next)
    if(IS_AFFECTED(f->follower,AFF_GROUP) && (f->follower->in_room == room)){
      t=GET_LEVEL(f->follower);
      levsum+=t;
    }
  return(levsum);
}
void damage(struct char_data *ch, struct char_data *victim,
            int dam, int attacktype)
{
 struct char_data *tmp;
 struct affected_type *aff;
 char buf[MAX_STRING_LENGTH];
 struct message_type *messages;
 int i,j,k,nr,max_hit,exp,levdam;
 extern int nokillflag;
 int hit_limit(struct char_data *ch);
 bool lootflag;
 
 if(!victim)
   return;
 if(IS_NPC(ch)==IS_NPC(victim)){
   if(!IS_NPC(ch)){
     if(nokillflag && !IS_SET(world[ch->in_room].room_flags,PK_ROOM)) return;
   } else {
     SET_BIT(ch->specials.act,ACT_SMART);
   }
 }
 if(IS_SET(world[ch->in_room].room_flags,LAWFUL))
   return;
 if(GET_POS(victim) <= POSITION_DEAD){
   stop_fighting(ch);
   return;
 }
 if (ch->in_room != victim->in_room) {
  if(ch->specials.fighting == victim)
   stop_fighting(ch);
  return;
 }
 if(IS_AFFECTED(ch, AFF_CHARM) && (ch->master==victim)){
    send_to_char("Hurt your master?\n\r",ch);
    return;
 }
 if((GET_LEVEL(victim)>=IMO) && !IS_NPC(victim))
  dam=0;
 if (victim != ch) {
  if (GET_POS(victim) > POSITION_STUNNED) {
   if (!(victim->specials.fighting))
    set_fighting(victim, ch);
   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,6) && 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(IS_AFFECTED(ch,AFF_CHARM) && IS_NPC(ch) && !IS_NPC(victim) && ch->master){
   stop_fighting(ch);
   affect_from_char(ch,SPELL_CHARM_PERSON);
   if(IS_SET(ch->specials.affected_by,AFF_CHARM))
     REMOVE_BIT(ch->specials.affected_by,AFF_CHARM | AFF_GROUP);
   if(ch->in_room == ch->master->in_room)
    hit(ch,ch->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 >>= 1;
 if(attacktype < 100){
  if((attacktype < MAXSPELL) || (attacktype >= SPELL_MAGIC_MISSILE_WPN)){
    if(GET_INT(ch) > 75)
      dam=dam+(dam*(GET_INT(ch)-75))/50;
    if(k=victim->specials.magres){
      if(k > 100)
        k=100;
      if(dam < 1000000){
        dam -= (k*dam)/100;
      } else {
        dam -= k*(dam/100);
      }
    }
  }
 }
 dam=MIN(GET_HIT(victim)+13,dam);
 if(dam < 0)
   dam=0;
 GET_HIT(victim)-=dam;
 if(IS_NPC(victim) && (ch != victim))
  gain_exp(ch,dam);
 update_pos(victim);
 if ((attacktype >= TYPE_HIT) && (attacktype < TYPE_SHOOT)) {
  if (!ch->equipment[WIELD]) {
   dam_message(dam, ch, victim, TYPE_HIT);
  } else {
   dam_message(dam, ch, victim, attacktype);
  }
 } else if(attacktype != TYPE_SHOOT) {
 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) >= IMO)) {
    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)) {
  case POSITION_MORTALLYW:
   act("$n has been mortally wounded, and begins to stink.", TRUE, victim, 0, 0, TO_ROOM);
   act("You are mortally wounded, and begin to stink.", FALSE, victim, 0, 0, TO_CHAR);
   break;
  case POSITION_INCAP:
   act("$n is incapacitated and will slowly die, if not aided.", TRUE, victim, 0, 0, TO_ROOM);
   act("You are incapacitated an will slowly die, if not aided.", FALSE, victim, 0, 0, TO_CHAR);
   break;
  case POSITION_STUNNED:
   act("$n is stunned, but could regain consciousness again.", TRUE, victim, 0, 0, TO_ROOM);
   act("You're stunned, but could regain consciousness again.", FALSE, victim, 0, 0, TO_CHAR);
   break;
  case POSITION_DEAD:
   act("$n is dead! R.I.P.", TRUE, victim, 0, 0, TO_ROOM);
   act("You are dead!  Sorry...", FALSE, victim, 0, 0, TO_CHAR);
   break;

  default:  /* >= POSITION SLEEPING */
   max_hit=hit_limit(victim);
   if (dam > (max_hit/5))
    act("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 that much!",
      FALSE,victim,0,0,TO_CHAR);
   if(GET_HIT(victim) < (max_hit/5))
    if(IS_NPC(victim))
     if(IS_SET(victim->specials.act, ACT_WIMPY)){
       if(GET_POS(victim) == POSITION_SITTING)
        do_stand(victim, "", 0);
       do_flee(victim, "", 0);
     } else {
      for(tmp=world[victim->in_room].people;tmp;tmp=tmp->next_in_room)
        if((tmp!=victim)&&(IS_NPC(tmp))&&
           (tmp->specials.fighting==victim->specials.fighting)&&
           (IS_SET(tmp->specials.act, ACT_HELPER))&&
           (GET_HIT(tmp) > (GET_HIT(victim)<<1))&&
           (GET_HIT(tmp) > 0)&&
           (GET_HIT(victim) > 0)){
          rescuesub(tmp,victim);
          break;
        }
     }
   break;  
 }
 if(!IS_NPC(victim) && !(victim->desc)) {
  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);
   GET_MANA(victim) = GET_MOVE(victim) = 0;
  }
 }
 if (GET_POS(victim) < POSITION_MORTALLYW)
  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)){
   if (IS_AFFECTED(ch, AFF_GROUP)) {
     group_gain(ch,victim,getlevelsum(ch));
   } else {
    exp = GET_EXP(victim);
    gain_exp(ch,exp);
    sprintf(buf,"You get %d experience point(s).\n\r",exp);
    send_to_char(buf,ch);
    change_alignment(ch,victim);
   }
   if(!IS_NPC(ch))
     (ch->points.kills)++;
  }
  if (!IS_NPC(victim)) {
   (victim->points.deaths)++;
   sprintf(buf, "%s killed (%d) by %s at %s",
    GET_NAME(victim), attacktype,
    (IS_NPC(ch) ? ch->player.short_descr : GET_NAME(ch)),
    world[victim->in_room].name);
   log(buf);
   if(IS_NPC(ch)){
    sprintf(buf,taunt[number(0,NTAUNT)],GET_NAME(victim));
    do_shout(ch,buf,0);
   }
  }
  lootflag = IS_NPC(victim) && !IS_NPC(ch) &&
             IS_SET(ch->specials.act, PLR_AUTOLOOT);
  die(victim);
  if(lootflag){
    struct obj_data *o;

    if(o = get_obj_in_list_vis(ch,"corpse",world[ch->in_room].contents))
      do_get(ch,"all corpse",0);
  }
 }
}

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,lo,hi,i;
 int off,def,loff,ldef;
 int bam,dam,sum,ff,n;
 char buf[256];
 extern int mischance,hitchance;

 if (ch->in_room != victim->in_room) {
  if(ch->specials.fighting == victim)
   stop_fighting(ch);
  return;
 }
 lo=WIELD;
 if((type==TYPE_UNDEFINED)&&(held=ch->equipment[HOLD])&&
    (held->obj_flags.type_flag == ITEM_WEAPON))
  hi=HOLD;
 else
  hi=WIELD;
 for(i=lo;i<=hi;i++){
  if (ch->equipment[i] &&
     (ch->equipment[i]->obj_flags.type_flag == ITEM_WEAPON)) {
   wielded = ch->equipment[i];
   switch (wielded->obj_flags.value[3]) {
    case 2  : w_type = TYPE_WHIP; break;
    case 3  : w_type = TYPE_SLASH; break;
    case 4  : w_type = TYPE_BLAST; break;
    case 5  : w_type = TYPE_SPLAT; break;
    case 6  : w_type = TYPE_SPANK; break;
    case 7  : w_type = TYPE_BLUDGEON; break;
    case 8  : w_type = TYPE_CRUSH; break;
    case 9  : w_type = TYPE_BURN; 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;
  }
  n=number(0,99);
  if(n < mischance){
    sum=(-1);
  } else if(n > hitchance){
    sum=1;
  } else {
    loff = GET_LEVEL(ch); ldef = GET_LEVEL(victim);
    off = loff + GET_STR(ch) + GET_DEX(ch) + GET_HITROLL(ch);
    def = ldef + GET_STR(victim) + GET_DEX(victim) + GET_AC(victim);
    sum = 1 + number(0,loff) + off - def;
  }
  if (AWAKE(victim) && (sum < 0)){
   if (type == SKILL_BACKSTAB)
    damage(ch,victim,0,SKILL_BACKSTAB);
   else
    damage(ch,victim,0,w_type);
  } else {
   dam = GET_DAMROLL(ch) + GET_STR(ch) - GET_CON(victim);
   if(dam < 1) dam=1;
   if (IS_NPC(ch))
    dam += dice(ch->specials.damnodice, ch->specials.damsizedice);
   else
    dam += 1;
   if(wielded){
    dam += dice(wielded->obj_flags.value[1],wielded->obj_flags.value[2]);
    if(IS_SET(wielded->obj_flags.extra_flags,ITEM_SFX)){
      ff=(ch->specials.fighting != NULL);
      weapon_effects(ch,victim,wielded);
      if(ch->specials.fighting){
        if(ch->specials.fighting->in_room != ch->in_room) return;
      } else {
        if(ff) return;
      }
    }
   }
   if (GET_POS(victim) < POSITION_FIGHTING)
    dam *= 1+(POSITION_FIGHTING-GET_POS(victim))/3;
   dam = MAX(1, dam);  /* Not less than 0 damage */
   if (type == SKILL_BACKSTAB) {
    bam = 2+(GET_LEVEL(ch)/10);
    dam *= bam;
    damage(ch, victim, dam, SKILL_BACKSTAB);
   } else
    damage(ch, victim, dam, w_type);
  }
 }
}

/* 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);
  }
 }
}
void weapon_effects(struct char_data *ch, struct char_data *vict,
 struct obj_data *weapon)
{
  int n,w,l,s;

  n=weapon->obj_flags.value[0];
  if(!wsplist[n].spellfun) return;
  w=GET_WIS(ch);
  if(number(wsplist[n].lo,wsplist[n].hi) < w){
    s=wsplist[n].shift;
    l=(s) ? wsplist[n].lev+(w>>s) : wsplist[n].lev;
    (*wsplist[n].spellfun)(l,ch,vict,weapon);
  }
}