deltamud/deltamud/
deltamud/deltamud/bin/
deltamud/deltamud/cnf/
deltamud/deltamud/lib/
deltamud/deltamud/lib/etc/
deltamud/deltamud/lib/misc/
deltamud/deltamud/lib/plrobjs/
deltamud/deltamud/lib/text/
deltamud/deltamud/lib/text/help/
deltamud/deltamud/lib/world/
deltamud/deltamud/lib/world/trg/
/* ************************************************************************
   *   File: fight.c                                       Part of CircleMUD *
   *  Usage: Combat system                                                   *
   *                                                                         *
   *  All rights reserved.  See license.doc for complete information.        *
   *                                                                         *
   *  Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
   *  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
   ************************************************************************ */

#include "conf.h"
#include "sysdep.h"

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

/* Structures */
extern int arena_preproom;
extern int arena_entrance;
extern struct char_data *arenamaster;
struct char_data *combat_list = NULL;	/* head of l-list of fighting chars */
struct char_data *next_combat_list = NULL;

/* External structures */
extern struct room_data *world;
extern struct message_list fight_messages[MAX_MESSAGES];
extern struct obj_data *object_list;
extern struct index_data *mob_index;	/* LJ autoquest */
extern int pk_allowed;		/* see config.c */
extern int jail_num;		/* see config.c */
extern int bail_multiplier;	/* see config.c */
extern byte pk_victim_min;      /* see config.c */
extern int auto_save;		/* see config.c */
extern int max_exp_gain;	/* see config.c */
extern int max_exp_loss;	/* see config.c */
ACMD(do_use);
void write_aliases(struct char_data *ch);
extern struct str_app_type str_app[];
extern struct dex_app_type dex_app[];

/* External procedures */
char *numdisplay(int);
void match_over(struct char_data *, struct char_data *, char *, int);
void deobserve(struct char_data *);
void clearobservers(struct char_data *);
void inc_matchcount(struct char_data *);
char *fread_action (FILE * fl, int nr);
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 *ch, struct char_data *victim);
void remember (struct char_data *ch, struct char_data *victim);
void diag_char_to_char (struct char_data *i, struct char_data *ch);
int ok_damage_shopkeeper (struct char_data *ch, struct char_data *victim);
int estimate_difficulty(struct char_data *ch, struct char_data *victim);
int exp_to_level(int arg);
ACMD (do_gen_comm);

/* Weapon attack texts */
struct attack_hit_type attack_hit_text[] =
{
  {"hit", "hits"},		/* 0 */
  {"sting", "stings"},
  {"whip", "whips"},
  {"slash", "slashes"},
  {"bite", "bites"},
  {"bludgeon", "bludgeons"},	/* 5 */
  {"crush", "crushes"},
  {"pound", "pounds"},
  {"claw", "claws"},
  {"maul", "mauls"},
  {"thrash", "thrashes"},	/* 10 */
  {"pierce", "pierces"},
  {"blast", "blasts"},
  {"punch", "punches"},
  {"stab", "stabs"}
};

#define IS_WEAPON(type) (((type) >= TYPE_HIT) && ((type) < TYPE_STARVING))

/* The Fight related routines */

void 
appear (struct char_data *ch)
{
  extern bool check_perm_duration(struct char_data *ch, long bitvector);
  if (!check_perm_duration(ch, AFF_INVISIBLE)) {
    if (affected_by_spell (ch, SPELL_INVISIBLE))
      affect_from_char (ch, SPELL_INVISIBLE);
    REMOVE_BIT (AFF_FLAGS (ch), AFF_INVISIBLE);
  }
  if (!check_perm_duration(ch, AFF_HIDE))
    REMOVE_BIT (AFF_FLAGS (ch), AFF_HIDE);

  if (!IS_SET(AFF_FLAGS(ch), AFF_HIDE) && !IS_SET(AFF_FLAGS(ch), AFF_INVISIBLE)) {
    if (GET_LEVEL (ch) < LVL_IMMORT)
      act ("$n slowly fades into existence.", FALSE, ch, 0, 0, TO_ROOM);
    else
      act ("You feel a strange presence as $n appears, seemingly from nowhere.",
	 FALSE, ch, 0, 0, TO_ROOM);
  }
}



void 
load_messages (void)
{
  FILE *fl;
  int i, type=0;
  struct message_type *messages;
  char chk[128];

  if (!(fl = fopen (MESS_FILE, "r")))
    {
      sprintf (buf2, "SYSERR: Error reading combat message file %s", MESS_FILE);
      perror (buf2);
      exit (1);
    }
  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;
    }


  fgets (chk, 128, fl);
  while (!feof (fl) && (*chk == '\n' || *chk == '*'))
    fgets (chk, 128, fl);

  while (*chk == 'M')
    {
      fgets (chk, 128, fl);
      sscanf (chk, " %[^\n]\n", buf);
      type=0;
        for (i=0; i < NUM_ATTACK_TYPES; i++)
          if (is_abbrev(buf, attack_hit_text[i].singular)) {
            type=i+1100;
            break;
          }
        if (!type)
          if ((type = find_skill_num (buf))==-1) {
            for (i=0; i<=12; i++) fgets (chk, 128, fl);
            while (!feof (fl) && (*chk == '\n' || *chk == '*'))
  	      fgets (chk, 128, fl);
            continue;
          }
      for (i = 0; (i < MAX_MESSAGES) && (fight_messages[i].a_type != type) &&
	   (fight_messages[i].a_type); i++);
      if (i >= MAX_MESSAGES)
	{
	  fprintf (logfile, "SYSERR: Too many combat messages.  Increase MAX_MESSAGES and recompile.");
	  exit (1);
	}
      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_action (fl, i);
      messages->die_msg.victim_msg = fread_action (fl, i);
      messages->die_msg.room_msg = fread_action (fl, i);
      messages->miss_msg.attacker_msg = fread_action (fl, i);
      messages->miss_msg.victim_msg = fread_action (fl, i);
      messages->miss_msg.room_msg = fread_action (fl, i);
      messages->hit_msg.attacker_msg = fread_action (fl, i);
      messages->hit_msg.victim_msg = fread_action (fl, i);
      messages->hit_msg.room_msg = fread_action (fl, i);
      messages->god_msg.attacker_msg = fread_action (fl, i);
      messages->god_msg.victim_msg = fread_action (fl, i);
      messages->god_msg.room_msg = fread_action (fl, i);
      fgets (chk, 128, fl);
      while (!feof (fl) && (*chk == '\n' || *chk == '*'))
	fgets (chk, 128, fl);
    }

  fclose (fl);
}

/* add more blood to room (Mulder) */

void increase_blood(int rm)
{
  RM_BLOOD(rm) = MIN(RM_BLOOD(rm) + 1, 10);
}
void increase_snow(int rm) {
 RM_SNOW(rm) = MIN(RM_SNOW(rm) + 1, 10);
}

void 
update_pos (struct char_data *victim)
{
  if ((GET_HIT (victim) > 0) && (GET_POS (victim) > POS_STUNNED))
    return;
  else if (GET_HIT (victim) > 0)
    GET_POS (victim) = POS_STANDING;
  else if (GET_HIT (victim) <= -11)
    GET_POS (victim) = POS_DEAD;
  else if (GET_HIT (victim) <= -6)
    GET_POS (victim) = POS_MORTALLYW;
  else if (GET_HIT (victim) <= -3)
    GET_POS (victim) = POS_INCAP;
  else
    GET_POS (victim) = POS_STUNNED;
}


void 
check_killer (struct char_data *ch, struct char_data *vict)
{

  /* peaceful rooms - but let imps attack */
  if (!PLR_FLAGGED (vict, PLR_KILLER) && !PLR_FLAGGED (vict, PLR_THIEF)
      && !PLR_FLAGGED (ch, PLR_KILLER) && !IS_NPC (ch) && !IS_NPC (vict) &&
      (ch != vict) && IS_JURISDICTED(ch->in_room) && 
      !ROOM_FLAGGED (ch->in_room, ROOM_PEACEFUL))
    {
      char buf[256];

      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);
      if (!PLR_FLAGGED (ch, PLR_THIEF))
	mudlog (buf, BRF, LVL_IMMORT, TRUE);

      SET_BIT (PLR_FLAGS (ch), PLR_KILLER);
      GET_ALIGNMENT (ch) = -1000;

      send_to_char ("This is a jurisdicted area. 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 (!FIGHTING (ch));

  ch->next_fighting = combat_list;
  combat_list = ch;

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

  FIGHTING (ch) = vict;
  GET_POS (ch) = POS_FIGHTING;

  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 *temp;

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

  REMOVE_FROM_LIST (ch, combat_list, next_fighting);
  ch->next_fighting = NULL;
  FIGHTING (ch) = NULL;
  GET_POS (ch) = POS_STANDING;
  update_pos (ch);
}



void 
make_corpse (struct char_data *ch)
{
  struct obj_data *corpse, *o;
  struct obj_data *money;
  int i;
  extern int max_npc_corpse_time, max_pc_corpse_time;

  struct obj_data *create_money (int amount);

  corpse = create_obj ();

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

  sprintf (buf2, "The corpse of %s is lying here.", GET_NAME (ch));
  corpse->description = str_dup (buf2);

  sprintf (buf2, "the corpse of %s", GET_NAME (ch));
  corpse->short_description = str_dup (buf2);

  GET_OBJ_TYPE (corpse) = ITEM_CONTAINER;
  GET_OBJ_WEAR (corpse) = ITEM_WEAR_TAKE;
  GET_OBJ_EXTRA (corpse) = ITEM_NODONATE;
  GET_OBJ_VAL (corpse, 0) = 0;	/* You can't store stuff in a corpse */
  GET_OBJ_VAL (corpse, 3) = 1;	/* corpse identifier */
  GET_OBJ_WEIGHT (corpse) = GET_WEIGHT (ch) + IS_CARRYING_W (ch);
  GET_OBJ_RENT (corpse) = 100000;
  if (IS_NPC (ch))
    GET_OBJ_TIMER (corpse) = max_npc_corpse_time;
  else
    GET_OBJ_TIMER (corpse) = max_pc_corpse_time;

  /* transfer character's inventory to the corpse */
  corpse->contains = ch->carrying;
  for (o = corpse->contains; o != NULL; o = o->next_content)
    o->in_obj = corpse;
  object_list_new_owner (corpse, NULL);

  /* transfer character's equipment to the corpse */
  for (i = 0; i < NUM_WEARS; i++)
    if (GET_EQ (ch, i))
      obj_to_obj (unequip_char (ch, i), corpse);

  /* transfer gold */
  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;
    }
  ch->carrying = NULL;
  IS_CARRYING_N (ch) = 0;
  IS_CARRYING_W (ch) = 0;

  obj_to_room (corpse, ch->in_room);
}


/* When ch kills victim */
void 
change_alignment (struct char_data *ch, struct char_data *victim)
{
  /*
   * new alignment change algorithm: if you kill a monster with alignment A,
   * you move 1/16th of the way to having alignment -A.  Simple and fast.
   */
  if (PRF2_FLAGGED(ch, PRF2_INTANGIBLE)) return;
  GET_ALIGNMENT (ch) += (-GET_ALIGNMENT (victim) - GET_ALIGNMENT (ch)) >> 4;

  if (IS_GOOD (ch))
    REMOVE_BIT (PLR_FLAGS (ch), PLR_THIEF | PLR_KILLER);

}



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

void raw_kill(struct char_data * ch, struct char_data * killer)
{
  void do_extract_char (struct char_data *ch, int type);
  if (FIGHTING (ch))
    stop_fighting (ch);

  while (ch->affected)
    affect_remove (ch, ch->affected);

   if (killer) {
     if (death_mtrigger(ch, killer))
       death_cry(ch);
   } else
       death_cry(ch);

  make_corpse (ch);
  if (GET_LEVEL(ch) < 30 || IS_NPC(ch))
    extract_char (ch);
  else
    do_extract_char (ch, 1);
}


void die(struct char_data * ch, struct char_data * killer)
{
  increase_blood(ch->in_room); /* now add the blood (Mulder) */
  gain_exp (ch, (-(GET_EXP(ch)-exp_to_level(GET_LEVEL(ch)-1))/4));
  //Subtract the difference of the current player exp and how much exp it took to get to their level
  //divided by 4.
  if (!IS_NPC (ch)) {
    REMOVE_BIT (PLR_FLAGS (ch), PLR_KILLER | PLR_THIEF);
    write_aliases(ch);
    GET_COND(ch, FULL) = 0;
    GET_COND(ch, THIRST) = 0;
    GET_COND(ch, DRUNK) = 0;
  }
  raw_kill(ch, killer);
}



void 
perform_group_gain (struct char_data *ch, int base,
		    struct char_data *victim)
{
  if (base > 1)
    {
      sprintf (buf2, "You receive your share of experience -- %s points.\r\n", numdisplay(base));
      send_to_char (buf2, ch);
    }
  else
    send_to_char ("You receive your share of experience -- one measly little point!\r\n", ch);

  gain_exp (ch, base);
  change_alignment (ch, victim);
}


void 
group_gain (struct char_data *ch, struct char_data *victim)
{
  int tot_members, tot_gain;
  struct char_data *k;
  struct follow_type *f;

  if (IS_NPC(ch))
    return;

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

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

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

  if (tot_members<=1)
    tot_gain = MAX(MIN(GET_EXP (victim) * 0.666, max_exp_gain), 1);
  else
    tot_gain = MAX(MIN(GET_EXP (victim) / tot_members, max_exp_gain), 1);

  if (!IS_NPC(victim))
    tot_gain=1;

  if (IS_AFFECTED (k, AFF_GROUP) && k->in_room == ch->in_room)
    perform_group_gain (k, tot_gain, victim);

  for (f = k->followers; f; f = f->next)
    if (IS_AFFECTED (f->follower, AFF_GROUP) && f->follower->in_room == ch->in_room)
      perform_group_gain (f->follower, tot_gain, victim);
}


void 
solo_gain (struct char_data *ch, struct char_data *victim)
{
  int exp;

  exp = MAX(MIN(GET_EXP (victim) * 0.666, max_exp_gain), 1);

  /* Calculate level-difference bonus */
  if (!IS_NPC (victim))
    exp = 1;

  if (IS_NPC (ch))
    return; // Mobs don't get exp.

  if (exp > 1)
    {
      sprintf (buf2, "You receive %s experience points.\r\n", numdisplay(exp));
      send_to_char (buf2, ch);
    }
  else
    send_to_char ("You receive one lousy experience point.\r\n", ch);

  gain_exp (ch, exp);
  change_alignment (ch, victim);
}


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


/* message for doing damage with a weapon */
void 
dam_message (int dam, struct char_data *ch, struct char_data *victim,
	     int w_type)
{
  char *buf;
  int msgnum;

  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 tries to #w $N, but misses.",		/* 0: 0     */
	"You try to #w $N, but miss.",
	"$n tries to #w you, but misses."
    }
    ,

    {
      "$n tickles $N as $e #W $M.",	/* 1: 1..2  */
	"You tickle $N as you #w $M.",
	"$n tickles 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..19  */
	"You #w $N extremely hard.",
	"$n #W you extremely hard."
    }
    ,

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

    {
      "$n OBLITERATES $N with $s deadly #w!!",	/* 8: 24..28   */
	"You OBLITERATE $N with your deadly #w!!",
	"$n OBLITERATES you with $s deadly #w!!"
    }
    ,
    {
      "$n PULVERIZES $N to bits with $s deadly #w!!",	/* 9: 29..33   */
	"You PULVERIZE $N to bits with your deadly #w!!",
	"$n PULVERIZES you to bits with $s deadly #w!!"
    }
    ,
    {
      "$n VAPORIZES $N with $s deadly #w!!",	/*10: 34..38   */
	"You VAPORIZE $N with your deadly #w!!",
	"$n VAPORIZES you with $s deadly #w!!"
    }
    ,
    {
      "$n ANNIHILATES $N to smithereens with $s deadly #w!!", /*11: > 38 */
	"You &RANNIHILATE&Y $N to smithereens with your deadly #w!!",
	"$n &RANNIHILATES&Y you to smithereens with $s deadly #w!!"
    }
  };


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

  if (dam == 0)
    msgnum = 0;
  else if (dam <= 27)
    msgnum = 1;
  else if (dam <= 54)
    msgnum = 2;
  else if (dam <= 81)
    msgnum = 3;
  else if (dam <= 108)
    msgnum = 4;
  else if (dam <= 135)
    msgnum = 5;
  else if (dam <= 162)
    msgnum = 6;
  else if (dam <= 189)
    msgnum = 7;
  else if (dam <= 216)
    msgnum = 8;
  else if (dam <= 243)
    msgnum = 9;
  else if (dam <= 270)
    msgnum = 10;
  else
    msgnum = 11;

  /* damage message to onlookers */
  buf = replace_string (dam_weapons[msgnum].to_room,
	  attack_hit_text[w_type].singular, attack_hit_text[w_type].plural);
  act (buf, FALSE, ch, NULL, victim, TO_NOTVICT);

  /* damage message to damager */
  send_to_char (CCYEL (ch, C_CMP), ch);
  buf = replace_string (dam_weapons[msgnum].to_char,
	  attack_hit_text[w_type].singular, attack_hit_text[w_type].plural);
  act (buf, FALSE, ch, NULL, victim, TO_CHAR);
  send_to_char (CCNRM (ch, C_CMP), ch);

  /* damage message to damagee */
  send_to_char (CCRED (victim, C_CMP), victim);
  buf = replace_string (dam_weapons[msgnum].to_victim,
	  attack_hit_text[w_type].singular, attack_hit_text[w_type].plural);
  act (buf, FALSE, ch, NULL, victim, TO_VICT | TO_SLEEP);
  send_to_char (CCNRM (victim, C_CMP), victim);


}


/*
 * message for doing damage with a spell or skill
 *  C3.0: Also used for weapon damage on miss and death blows
 */
int 
skill_message (int dam, struct char_data *ch, struct char_data *vict,
	       int attacktype)
{
  int i, j, nr;
  struct message_type *msg;

  struct obj_data *weap = GET_EQ (ch, WEAR_WIELD);

  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, msg = fight_messages[i].msg; (j < nr) && msg; j++)
	    msg = msg->next;

	  if (!IS_NPC (vict) && (GET_LEVEL (vict) >= LVL_IMMORT))
	    {
	      act (msg->god_msg.attacker_msg, FALSE, ch, weap, vict, TO_CHAR);
	      act (msg->god_msg.victim_msg, FALSE, ch, weap, vict, TO_VICT);
	      act (msg->god_msg.room_msg, FALSE, ch, weap, vict, TO_NOTVICT);
	    }
	  else if (dam != 0)
	    {
	      if (GET_POS (vict) == POS_DEAD)
		{
		  send_to_char (CCYEL (ch, C_CMP), ch);
		  act (msg->die_msg.attacker_msg, FALSE, ch, weap, vict, TO_CHAR);
		  send_to_char (CCNRM (ch, C_CMP), ch);

		  send_to_char (CCRED (vict, C_CMP), vict);
		  act (msg->die_msg.victim_msg, FALSE, ch, weap, vict, TO_VICT | TO_SLEEP);
		  send_to_char (CCNRM (vict, C_CMP), vict);

		  act (msg->die_msg.room_msg, FALSE, ch, weap, vict, TO_NOTVICT);
		}
	      else
		{
		  send_to_char (CCYEL (ch, C_CMP), ch);
		  act (msg->hit_msg.attacker_msg, FALSE, ch, weap, vict, TO_CHAR);
		  send_to_char (CCNRM (ch, C_CMP), ch);

		  send_to_char (CCRED (vict, C_CMP), vict);
		  act (msg->hit_msg.victim_msg, FALSE, ch, weap, vict, TO_VICT | TO_SLEEP);
		  send_to_char (CCNRM (vict, C_CMP), vict);

		  act (msg->hit_msg.room_msg, FALSE, ch, weap, vict, TO_NOTVICT);
		}
	    }
	  else if (ch != vict)
	    {			/* Dam == 0 */
	      send_to_char (CCYEL (ch, C_CMP), ch);
	      act (msg->miss_msg.attacker_msg, FALSE, ch, weap, vict, TO_CHAR);
	      send_to_char (CCNRM (ch, C_CMP), ch);

	      send_to_char (CCRED (vict, C_CMP), vict);
	      act (msg->miss_msg.victim_msg, FALSE, ch, weap, vict, TO_VICT | TO_SLEEP);
	      send_to_char (CCNRM (vict, C_CMP), vict);

	      act (msg->miss_msg.room_msg, FALSE, ch, weap, vict, TO_NOTVICT);
	    }
	  return 1;
	}
    }
  return 0;
}

#define PHP 5 /* Percent of a player's hp that needs to be lost by ch for him to get +1 POWER */

void adrenaline_rush(struct char_data *ch, sh_int damage) {
  struct affected_type af;

  if (IS_NPC(ch) || GET_SKILL(ch, SKILL_ADRENALINE) < 70 || damage < PHP*GET_MAX_HIT(ch)/100) return;

  af.location = APPLY_POWER;
  af.modifier = damage/(PHP*GET_MAX_HIT(ch)/100);
  af.duration = damage/(PHP*GET_MAX_HIT(ch)/100);
  af.bitvector = 0;
  af.type=SKILL_ADRENALINE + (GET_SKILL(ch, SKILL_BLOODLUST) > 70) + (GET_SKILL(ch, SKILL_CARNALRAGE) > 70); // These skills MUST be in succession to each other for this to work!
  if (af.type==SKILL_CARNALRAGE)
    send_to_char("You feel the &RCarnal &rRage&n build within you!!!\r\n", ch);
  else if (af.type==SKILL_BLOODLUST)
    send_to_char("You &rlust&n for more &RBLOOD&n!!\r\n", ch);
  else if (af.type==SKILL_ADRENALINE)
    send_to_char("You feel a surge of &RADRENALINE&n!\r\n", ch);
  affect_join (ch, &af, TRUE, FALSE, TRUE, FALSE);
}

#define VALID_ATTACKTYPE (attacktype < SELF_DAMAGE)

void do_actual_damage (struct char_data *ch, struct char_data *victim, int dam, int attacktype,
int deathblow) {
  int j, gold_before, gold_after, questdiff;
  struct obj_data *tobj;
  int rip_dam;
  struct char_data *questtarget = NULL;
  ACMD (do_get);
  ACMD (do_split);
  ACMD (do_stand);
  /*  int exp; */
  long local_gold = 0;
  char local_buf[256];
  char mybuf[1024];

  if (GET_POS (victim) <= POS_DEAD)
    {
      log ("SYSERR: Attempt to damage a corpse.");
      die(victim, ch);
      return;			/* -je, 7/7/92 */
    }

  if (IS_SET(PRF2_FLAGS(ch), PRF2_MERCY) && !IS_NPC(ch) && GET_HIT(victim) < 0 && !deathblow && VALID_ATTACKTYPE) {
    act("$N is almost dead, and you decide to have mercy on them.", FALSE, ch, 0, victim, TO_CHAR);
    stop_fighting(ch);
    return;
  }

  if (!(IS_ARENACOMBATANT(ch) && IS_ARENACOMBATANT(victim))){
    if (!pk_allowed && !IS_NPC(ch) && !IS_NPC(victim)
        && (GET_LEVEL(victim) < pk_victim_min)
        && !PLR_FLAGGED (victim, PLR_THIEF)
        && !(ch == victim)){
      send_to_char ("Ack! But he's a newbie!\r\n", ch);
      return;
    }
    if (!pk_allowed && !IS_NPC(ch) && !IS_NPC(victim) 
	&& (GET_LEVEL(ch) < pk_victim_min)
        && !(ch == victim)){

      send_to_char ("Wait till you're level 10 at least before becoming a PLAYER KILLER.\r\n", ch);
      return;
    }
  }

  if (ch->in_room != victim->in_room){
    sprintf(mybuf, "DEBUG: Timing Bug Trigger - %s (ch) and %s (victim) are not in same room.",GET_NAME(ch), GET_NAME(victim));
    mudlog(mybuf, CMP, LVL_GRGOD, TRUE);
    return;
  }

  /* peaceful rooms - but let imps attack */
  if (ch != victim && ROOM_FLAGGED (ch->in_room, ROOM_PEACEFUL) && GET_LEVEL (ch) < LVL_IMPL)
    {
      send_to_char ("This room just has such a peaceful, easy feeling...\r\n", ch);
      return;
    }

  /* shopkeeper protection */
  if (!ok_damage_shopkeeper (ch, victim))
    return;

  /* You can't damage an immortal or a builder */
  if ((!IS_NPC (victim) && (GET_LEVEL (victim) >= LVL_IMMORT)) || ((GET_LEVEL(ch)<LVL_IMMORT || IS_NPC(ch)) && IS_SET(PRF2_FLAGS(victim), PRF2_INTANGIBLE)))
    dam = 0;

  if ((PRF2_FLAGGED(ch, PRF2_INTANGIBLE) && !PRF2_FLAGGED(ch, PRF2_MBUILDING) && GET_LEVEL(victim)<LVL_IMMORT) ||
      (PRF2_FLAGGED(victim, PRF2_INTANGIBLE) && !PRF2_FLAGGED(victim, PRF2_MBUILDING) && GET_LEVEL(ch)<LVL_IMMORT)) {
    stop_fighting(ch);
    stop_fighting(victim);
    return; /* Intangibles can't fight. */
  }

  if (victim != ch)
    {
      /* Start the attacker fighting the victim */
      if (GET_POS (ch) > POS_STUNNED && (FIGHTING (ch) == NULL))
	set_fighting (ch, victim);

      /* Start the victim fighting the attacker */
      if (GET_POS (victim) > POS_STUNNED && (FIGHTING (victim) == NULL))
	{
	  set_fighting (victim, ch);
	  if (MOB_FLAGGED (victim, MOB_MEMORY) && !IS_NPC (ch))
	    remember (victim, ch);
	}
    }

  /* If you attack a pet, it hates your guts */
  if (victim->master == ch)
    stop_follower (victim);

  /* If the attacker is invisible, he becomes visible */
  if (IS_AFFECTED (ch, AFF_INVISIBLE | AFF_HIDE))
    appear (ch);

  /* Cut damage in half if victim has sanct, to a minimum 1 */
  if (IS_AFFECTED (victim, AFF_SANCTUARY) && dam >= 2)
    dam /= 2;

  /* Check for PK if this is not a PK MUD */
  if (!pk_allowed) {
      check_killer (ch, victim);
      /* Uncomment this part ONLY if COMPLETELY NO PKing is allowed
      if (PLR_FLAGGED (ch, PLR_KILLER) && (ch != victim))
	dam = 0;
	*/
  }

  /* Damage multiplier. */
  dam*=dam_multi(ch, victim, attacktype > 0 && attacktype <= MAX_SPELLS ? 1 : 0);

  /* New Skills here - Dodge, Parry, Avoid, Riposte */
  if (attacktype <= TYPE_STAB && attacktype >= TYPE_HIT && GET_POS(ch) > POS_STANDING-1) {
      /* come the more powerful skills... Riposte first */
      if ((number(1,100)*AVOID_FACTOR) <= GET_SKILL(victim, SKILL_RIPOSTE)) {
        
        /* Send out the lovely messages */
        sprintf(buf, "You anticipate $N's attack, avoiding it, and striking back!");
        act(buf, TRUE, victim, 0, ch, TO_CHAR);
        
        sprintf(buf, "$n anticipates your attack, and strikes back at you!");
        act(buf, FALSE, victim, 0, ch, TO_VICT);
        
        sprintf(buf, "$n anticipates $N's ameteur attack, and strikes back expertly.");
        act(buf, FALSE, victim, 0, ch, TO_NOTVICT);
    
        /* How much damage to do on a riposte? */
        tobj = victim->equipment[WEAR_WIELD];
        /* If we aren't wielding a weapon, strike back with minimal
         * barehand damage.
         */
        if(!tobj || GET_OBJ_TYPE(tobj) != ITEM_WEAPON){
          damage(victim, ch, 2, SKILL_RIPOSTE);
          return;
        }
        
        rip_dam = dice(GET_OBJ_VAL(tobj, 1), GET_OBJ_VAL(tobj, 2));
        rip_dam *= 1 + (POS_FIGHTING - GET_POS(ch)) / 3;
      
        damage(victim, ch, rip_dam, SKILL_RIPOSTE);
        return;
        
      } else if((number(1,100)*AVOID_FACTOR) <= GET_SKILL(victim, SKILL_AVOID)) {
          
        /* Send out the lovely messages */
        sprintf(buf, "You avoid $N's attack, tossing $M to the ground.");
        act(buf, TRUE, victim, 0, ch, TO_CHAR);
        
        sprintf(buf, "$n avoids your attack, trips you, sending you to the ground.");
        act(buf, FALSE, victim, 0, ch, TO_VICT);
        
        sprintf(buf, "$n avoids $N's pathetic attack and sends $M sprawling.");
        act(buf, FALSE, victim, 0, ch, TO_NOTVICT);
        
        GET_POS(ch) = POS_SITTING;
        WAIT_STATE(ch, PULSE_VIOLENCE);
      
        return;
        
      } else if((number(1,100)*AVOID_FACTOR) <= GET_SKILL(victim, SKILL_PARRY)) {
        
          /* Send out the lovely messages */
          sprintf(buf, "You parry $N's viscious attack upon your person.");
          act(buf, TRUE, victim, 0, ch, TO_CHAR);
        
          sprintf(buf, "$n spoils your attack with a deft parrying move.");
          act(buf, FALSE, victim, 0, ch, TO_VICT);
        
          sprintf(buf, "$n parries $N's attack with a series of skillful maneuvers.");
          act(buf, FALSE, victim, 0, ch, TO_NOTVICT);
        
          return;

      } else if((number(1,100)*AVOID_FACTOR) <= GET_SKILL(victim, SKILL_DODGE)) {
          
        /* Send out the lovely messages */
        sprintf(buf, "You narrowly dodge $N's masterful attack.");
        act(buf, TRUE, victim, 0, ch, TO_CHAR);
          
        sprintf(buf, "$n narrowly dodges your skillful attack, just avoiding your intended blow.");
        act(buf, FALSE, victim, 0, ch, TO_VICT);
          
        sprintf(buf, "$n narrowly dodges $N's strike.");
        act(buf, FALSE, victim, 0, ch, TO_NOTVICT);
          
        return;
      } 
  }
          
  tobj = NULL;
        
        
  if(!IS_NPC(ch) && !CHECK_WAIT(ch) && GET_POS(ch) < POS_FIGHTING)
    do_stand(ch, "", 0, 0);
        
  if(IS_NPC(ch) && !GET_MOB_WAIT(ch) && GET_POS(ch) < POS_FIGHTING)
    do_stand(ch, "", 0, 0);
        
        
  /* Set the maximum damage per round and subtract the hit points */
  dam = MAX (MIN (dam, 1000), 0);
  GET_HIT (victim) -= dam;
  if (VALID_ATTACKTYPE) adrenaline_rush(victim, dam);

  /* Gain exp for the hit */
//  if (ch != victim)
//    gain_exp (ch, GET_LEVEL (victim) * dam);

  update_pos (victim);

  /*
   * skill_message sends a message from the messages file in lib/misc.
   * dam_message just sends a generic "You hit $n extremely hard.".
   * skill_message is preferable to dam_message because it is more
   * descriptive.
   * 
   * If we are _not_ attacking with a weapon (i.e. a spell), always use
   * skill_message. If we are attacking with a weapon: If this is a miss or a
   * death blow, send a skill_message if one exists; if not, default to a
   * dam_message. Otherwise, always send a dam_message.
   */
  if (attacktype != -1 && VALID_ATTACKTYPE && attacktype!=SKILL_RIPOSTE) {
    if (!IS_WEAPON(attacktype))
      skill_message(dam, ch, victim, attacktype);
    else {
      if ((GET_POS(victim) == POS_DEAD && !deathblow && !IS_SET(PRF2_FLAGS(ch), PRF2_MERCY) &&
          !IS_NPC(ch)) || dam == 0) {
        if (!skill_message(dam, ch, victim, attacktype))
          dam_message(dam, ch, victim, attacktype);
      } else
        dam_message(dam, ch, victim, attacktype);
    }
  }

  if (!IS_NPC(ch) && IS_SET(PRF2_FLAGS(ch), PRF2_MERCY) && GET_HIT(victim) < 0 && !deathblow && VALID_ATTACKTYPE) {
    stop_fighting(ch);
    stop_fighting(victim);
    GET_POS(victim)=POS_STANDING;
    sprintf(buf, "You have mercy on $N, and spare %s life... for now.", HSHR(victim));
    act(buf, TRUE, ch, 0, victim, TO_CHAR);
    
    sprintf(buf, "$n spares your life, thank the gods!");
    act(buf, FALSE, ch, 0, victim, TO_VICT);
  
    sprintf(buf, "$N is about to deliver the death blow, but suddenly spares $n's life!");
    act(buf, FALSE, victim, 0, ch, TO_NOTVICT);
    GET_HIT(victim)=-1; update_pos(victim);
  }

  /* Use send_to_char -- act() doesn't send message if you are DEAD. */
  switch (GET_POS (victim))
    {
    case POS_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 POS_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 POS_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 POS_DEAD:
      if (IS_ARENACOMBATANT(victim)){
	match_over(ch, victim, "(Fatality)", TRUE);
	return ;
      }else{
	act ("$n is dead!  R.I.P.", FALSE, 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) / 4))
	act ("That really did HURT!", FALSE, victim, 0, 0, TO_CHAR);

      if (GET_HIT (victim) < (GET_MAX_HIT (victim) / 4))
	{
	  sprintf (buf2, "%sYou wish that your wounds would stop BLEEDING so much!%s\r\n",
		   CCRED (victim, C_SPR), CCNRM (victim, C_SPR));
	  send_to_char (buf2, victim);
     if (IS_NPC(victim) && (ch != victim) && MOB_FLAGGED(victim, MOB_WIMPY))
	    do_flee (victim, "", 0, 0);
	 }

      if (!IS_NPC (victim) && GET_RETREAT_LEV (victim) && (victim != ch) && GET_HIT(victim) <  GET_RETREAT_LEV(victim) && GET_HIT(victim) > 0)
        {
          send_to_char ("You wimp out, and attempt to retreat!\r\n", victim);
          /* Retreat check is done at spell_recall in spells.c */
          do_use (victim, "retreat", 0, SCMD_RECITE);
        }

      if (!IS_NPC (victim) && GET_RECALL_LEV (victim) && (victim != ch) && GET_HIT(victim) < GET_RECALL_LEV(victim) && GET_HIT(victim) > 0)
        {
          send_to_char ("You wimp out, and attempt to recall!\r\n", victim);
	  /* Recall check is done at spell_recall in spells.c */
	  do_use (victim, "recall", 0, SCMD_RECITE);
        }
      if (!IS_NPC (victim) && GET_WIMP_LEV (victim) && (victim != ch) && GET_HIT(victim) < GET_WIMP_LEV(victim) && GET_HIT(victim) > 0)
	{
	  send_to_char ("You wimp out, and attempt to flee!\r\n", victim);
	  /* Flee check is done at do_flee in act.offensive.c */
	  do_flee (victim, "", 0, 0);
	}
      break;
    }

  /* Help out poor linkless people who are attacked */
  if (!IS_NPC (victim) && !(victim->desc))
    {
      do_flee (victim, "", 0, 0);
      if (!FIGHTING (victim))
	{
	  act ("$n is rescued by divine forces.", FALSE, victim, 0, 0, TO_ROOM);
	  GET_WAS_IN (victim) = victim->in_room;
	  char_from_room (victim);
	  char_to_room (victim, 0);
	}
    }

  /* stop someone from fighting if they're stunned or worse */
  if ((GET_POS (victim) <= POS_STUNNED) && (FIGHTING (victim) != NULL))
    stop_fighting (victim);

  /* Uh oh.  Victim died. */
  if (GET_POS (victim) == POS_DEAD)
    {
      if ((ch != victim) && (IS_NPC (victim) || victim->desc))
	{
	  if (IS_AFFECTED (ch, AFF_GROUP))
	    group_gain (ch, victim);
	  else
	    solo_gain (ch, victim);
	}
      if (PLR_FLAGGED (ch, PLR_QUESTOR) && IS_NPC (victim))
	{			/* LJ autoquest */
	  if (GET_QUESTMOB (ch) == GET_MOB_VNUM (victim))
	    {
	      send_to_char ("You have almost completed your QUEST!\n\r", ch);
	      send_to_char ("Return to the questmaster before your time runs out!\n\r", ch);
	      /* GET_QUESTMOB (ch) = 0; */
	      questtarget = get_char_num(real_mobile(GET_QUESTMOB(ch)));
	      questdiff = estimate_difficulty(ch, questtarget);
	      if (questdiff <= 0)
		questdiff = 1;
	      questdiff = (questdiff/5);
	      GET_QUESTMOB (ch) = -(questdiff);
	    }
	}


      if (!IS_NPC (victim))
	{
	  sprintf (buf2, "%s killed by %s at %s", GET_NAME (victim), GET_NAME (ch),
		   world[victim->in_room].name);
	  mudlog (buf2, BRF, LVL_IMMORT, TRUE);

          if (ch == victim) { /* suicide :( */
	    die(victim, ch);
            return;
          }
	  if ((!pk_allowed) && (PLR_FLAGGED (ch, PLR_KILLER))){
	    /* Oooh, goto JAIL! */

	 sprintf (buf, "&m[&YINFO&m]&n %s was killed by %s (jailed).\r\n",
       GET_NAME (victim), GET_NAME (ch));
	 send_to_all (buf);

	    send_to_char ("Oh now you've really gone and done it!\r\n", ch);
	    REMOVE_BIT(PRF_FLAGS(ch), PRF_SUMMONABLE);
	    SET_BIT(PRF_FLAGS(ch), PRF_NOAUCT);
	    GET_ALIGNMENT (ch) = -1000;

	    j = (int) GET_LEVEL (ch) - (int) GET_LEVEL (victim);
	    if (j == 0)
	      j = 1;
	    else
	      j = abs(j);
	    GET_BAIL_AMT (ch) =  (j* bail_multiplier);

	    char_from_room (ch);
	    char_to_room (ch, real_room(jail_num));
	    act ("$n suddenly appears in the room.", TRUE, ch, 0, 0, TO_ROOM);
	    look_at_room (ch, 0);
	  }else if ((!pk_allowed) && (PLR_FLAGGED (victim, PLR_KILLER) && !IS_NPC(ch))){
	    sprintf (buf,"&m[&YINFO&m]&n %s was killed by %s (defending).\r\n",
		     GET_NAME (victim), GET_NAME (ch));
	    send_to_all (buf);

// this was annoying, changed 8/23/98 --Mulder
	  }else if (IS_NPC(ch) || IS_NPC(victim)){
//
//	    sprintf (buf, "&m[&YINFO&m]&n %s was killed by %s.\r\n", 
//		     GET_NAME (victim), GET_NAME (ch));
//	    send_to_all (buf);
      
	  }else{
	    sprintf (buf, "&m[&YINFO&m]&n %s was killed by %s (offending).\r\n", 
		     GET_NAME (victim), GET_NAME (ch));
	    send_to_all (buf);
	  }

	  if (MOB_FLAGGED (ch, MOB_MEMORY))
	    forget (ch, victim);
	}

      /* Cant determine GET_GOLD on corpse, so do now and store */
      if (IS_NPC (victim))
	{
	  local_gold = GET_GOLD (victim);
	  sprintf (local_buf, "%ld", (long) local_gold);
	}
      die(victim, ch);
      gold_before = gold_after = 0;
      /* If Autoloot enabled, get all corpse */
      if (IS_NPC (victim) && !IS_NPC (ch) 
	  && PRF_FLAGGED (ch, PRF_AUTOLOOT) && !PLR_FLAGGED(ch, PLR_KILLER))
	{
	  gold_before = GET_GOLD(ch);
	  do_get (ch, "all corpse", 0, 0);
	  gold_after = GET_GOLD(ch);
	}

      /* If Autoloot AND AutoSplit AND we got money, split with group */
      if (IS_AFFECTED (ch, AFF_GROUP) && (local_gold > 0) &&
	  PRF_FLAGGED (ch, PRF_AUTOSPLIT) && PRF_FLAGGED (ch, PRF_AUTOLOOT)
	  && !PLR_FLAGGED(ch, PLR_KILLER))
	{
	  if (gold_after > gold_before)
	    do_split (ch, local_buf, 0, 0);
	}
      /* If Autogold enabled, get coins corpse */
      if (IS_NPC (victim) && !IS_NPC (ch) && PRF_FLAGGED (ch, PRF_AUTOGOLD)
	  && !PRF_FLAGGED(ch, PLR_KILLER))
	{
	  gold_before = GET_GOLD(ch);
	  do_get (ch, "coins corpse", 0, 0);
	  gold_after = GET_GOLD(ch);
	}

      /* If Autogold AND AutoSplit AND we got money, split with group */
      if (IS_AFFECTED (ch, AFF_GROUP) && (local_gold > 0) &&
	  PRF_FLAGGED (ch, PRF_AUTOSPLIT) && PRF_FLAGGED (ch, PRF_AUTOGOLD)
	  && !PLR_FLAGGED(ch, PLR_KILLER))
	{
	  if (gold_after > gold_before)
	    do_split (ch, local_buf, 0, 0);
	}
    }
}

void damage (struct char_data *ch, struct char_data *victim, int dam, int attacktype) {
  do_actual_damage (ch, victim, dam, attacktype, 0);
}

void deathblow (struct char_data *ch, struct char_data *victim, int dam, int attacktype) {
  do_actual_damage (ch, victim, dam, attacktype, 1);
}

void hit (struct char_data *ch, struct char_data *victim, int type)
{
  static struct affected_type *af;
  struct obj_data *wielded = GET_EQ (ch, WEAR_WIELD);
  int dam=0, diceroll;

  int backstab_mult (int level);

  /* check if the character has a fight trigger */
  fight_mtrigger(ch);
  fight_otrigger(ch);

  /* Do some sanity checking, in case someone flees, etc. */
  if (ch->in_room != victim->in_room)
    {
      if (FIGHTING (ch) && FIGHTING (ch) == victim)
	stop_fighting (ch);
      return;
    }
  // No hits in a peaceful room! -Storm
  if (ROOM_FLAGGED(ch->in_room, ROOM_PEACEFUL) && GET_LEVEL(ch) < LVL_IMPL) {
    send_to_char("This room just has such a peaceful, easy feeling...\r\n", ch);
    return;
  }

  diceroll = number (0, 100);
  /* decide whether this is a hit or a miss */
  if (diceroll > chance(ch, victim, 0) && AWAKE (victim))
    {
      /* the attacker missed the victim */
      damage (ch, victim, 0, type);
    }
  else
    {
      /* okay, we know the guy has been hit.  now calculate damage. */

      if (wielded)
	{
	  /* Add weapon-based damage if a weapon is being wielded */
	  dam += dice (GET_OBJ_VAL (wielded, 1), GET_OBJ_VAL (wielded, 2));
	}
      else
	{
	  /* If no weapon, add bare hand damage instead */
	  if (IS_NPC (ch))
	    {
	      dam += dice (ch->mob_specials.damnodice, ch->mob_specials.damsizedice);
	    }
	  else
	    {
	      dam += number (0, 2);	/* Max 2 bare hand damage for players */
	    }
	}

      /*
       * Include a damage multiplier if victim isn't ready to fight:
       *
       * Position sitting  1.33 x normal
       * Position resting  1.66 x normal
       * Position sleeping 2.00 x normal
       * Position stunned  2.33 x normal
       * Position incap    2.66 x normal
       * Position mortally 3.00 x normal
       *
       * Note, this is a hack because it depends on the particular
       * values of the POSITION_XXX constants.
       */
      if (GET_POS (victim) < POS_FIGHTING)
	dam *= 1 + (POS_FIGHTING - GET_POS (victim)) / 3;

      /* at least 1 hp damage min per hit */
      dam = MAX (1, dam);

      if ((type == SKILL_BACKSTAB))
	{
	  dam *= backstab_mult (GET_LEVEL (ch));
	  damage (ch, victim, dam, SKILL_BACKSTAB);
	}
      else
	damage (ch, victim, dam, type);
      if (AFF_FLAGGED(ch, AFF_R_CHARGED))
         for (af = ch->affected; af; af = af->next)
           if (af->type==SPELL_REDIRECT_CHARGE && af->bitvector==AFF_R_CHARGED) {
	     act ("You momentarily run your finger against $N's skin and a charge of electricity jumps from your body into theirs!\r\n$N CRISPS AND FRIES!!", FALSE, ch, NULL, victim, TO_CHAR);
	     act ("$n touches you and you find it somewhat... &KELECTRIFYING&n!\r\nYour skin chars and crisps!", FALSE, ch, NULL, victim, TO_VICT);
	     act ("You momentarily see a flash of light and $N FRIES to a CRISP!", FALSE, ch, NULL, victim, TO_NOTVICT);
             damage(ch, victim, af->modifier, TYPE_UNDEFINED);
             affect_remove(ch, af);
             break;
           }
    }
   /* check if the victim has a hitprcnt trigger */
   hitprcnt_mtrigger(victim);
}



/* control the fights going on.  Called every 2 seconds from comm.c. */
void 
perform_violence (void)
{
  struct char_data *ch;
  extern struct index_data *mob_index;
  int apr, prob, percent;
  int condition;
  int j;

  for (ch = combat_list; ch; ch = next_combat_list)
    {
      next_combat_list = ch->next_fighting;
      apr = 0;

      if (FIGHTING (ch) == NULL || ch->in_room != FIGHTING (ch)->in_room)
	{
	  stop_fighting (ch);
	  continue;
	}

      if (IS_NPC (ch))
	{
	  if (GET_MOB_WAIT (ch) > 0)
	    {
	      GET_MOB_WAIT (ch) -= PULSE_VIOLENCE;
	      continue;
	    }
	  GET_MOB_WAIT (ch) = 0;
	  if (GET_POS (ch) < POS_FIGHTING)
	    {
	      GET_POS (ch) = POS_FIGHTING;
	      act ("$n scrambles to $s feet!", TRUE, ch, 0, 0, TO_ROOM);
	    }
         if ((GET_POS (FIGHTING(ch)) < POS_SLEEPING) && (MOB_FLAGGED(ch, MOB_MERCY))) {
         stop_fighting(FIGHTING(ch));
         stop_fighting(ch);
          }
	}

      if (GET_POS (ch) < POS_FIGHTING)
	{
	  send_to_char ("You can't fight while sitting!!\r\n", ch);
	  continue;
	}
      if (!IS_NPC (ch) && !PRF2_FLAGGED(ch, PRF2_DISPMOB))
	diag_char_to_char (FIGHTING (ch), ch);

      if (GET_CLASS(ch) == CLASS_WARRIOR || GET_CLASS(ch) == CLASS_THIEF){
	prob = GET_SKILL(ch, SKILL_SECOND_ATTACK);
	percent = number((int)(prob * 0.85), 101) 
	  - (GET_LEVEL(ch)/number(10, 20));
	if (percent <= 0)
	  percent = 1;
	if (prob > percent){
	  apr++;
	  prob = GET_SKILL(ch, SKILL_THIRD_ATTACK);
	  percent = number((int)(prob * 0.9), 151) 
	    - (GET_LEVEL(ch)/number(10, 30));
	  if (prob > percent && GET_CLASS(ch) == CLASS_WARRIOR)
	    apr++;
       }
     }

    if (IS_NPC(ch)){
      if (MOB_FLAGGED(ch, MOB_DBLATTACK)){
	percent = number(50, 101) 
	  - (GET_LEVEL(ch)/number(10, 20));
	if (percent <= 0)
	  percent = 1;
	if (number(1,100) > percent)
	  apr++;
      }
    }

      
    // increment apr by one for every attack they are supposed to get,
    // for the multiple attack skills, you should make sure they only
    // get a subsequent attack if they properly got the previous one.
    // For instance, you only get third attack if you are getting a
    // second attack.  This doesn't need to be skill based, you can
    // easily make it based upon class/level... see the second example
    // below.
    //
    //   if (AFF_FLAGGED(ch, AFF_HASTE))
    //     apr += number(0, 2);
    //    
    //   if (GET_CLASS(ch) == CLASS_WARRIOR && GET_LEVEL(ch) >= 10)
    //     apr++;
    //
    // If apr is negative they get no attacks, if apr is 0 they get
    // one attack.  APR has a range of -1 to 4, giving a minimum of
    // no attacks, to a maximum of 4.  See the below line for changing
    // that (eg., MAX(-1, MIN(apr, 6)) means a max of 6).
    

    apr = MAX(-1, MIN(apr, 4));
 
    if (apr >= 0) {
      for (; apr >= 0 && FIGHTING(ch); apr--) {
//        hit(ch, FIGHTING(ch), TYPE_UNDEFINED);
        hit(ch, FIGHTING(ch), GET_ATTACKTYPE(ch));
        if (FIGHTING(ch)) {
          condition = 20-(((GET_DEX(FIGHTING(ch))-12)*5)/3);
          MAX(MIN(condition,30),10); // Cap it to minimum of 10 and maximum of 30.
          if (!IS_NPC(FIGHTING(ch))) {
            for (j = 0; j < NUM_WEARS; j++) {
              if (FIGHTING(ch)->equipment[j]) {
                if (number(1, 100) <= condition) {
                  if (FIGHTING(ch)->equipment[j]->obj_flags.total_slots != 0) {
                    FIGHTING(ch)->equipment[j]->obj_flags.curr_slots =
                    FIGHTING(ch)->equipment[j]->obj_flags.curr_slots--;
                    sprintf(buf, "%s just got DAMAGED during the combat!\r\n",
                    FIGHTING(ch)->equipment[j]->short_description);
                    send_to_char(buf, FIGHTING(ch));
                  }
                }
                if ((FIGHTING(ch)->equipment[j]->obj_flags.curr_slots == 0) && (FIGHTING(ch)->equipment[j]->obj_flags.total_slots != 0)) {
                  sprintf(buf, "%s crumbles to dust as it wears out!\r\n",
                  FIGHTING(ch)->equipment[j]->short_description);
                  send_to_char(buf, FIGHTING(ch));
                  extract_obj(FIGHTING(ch)->equipment[j]);
                }
              }
            }
          }
        }
      }
      if ((MOB_FLAGGED(ch, MOB_SPEC) || MOB_FLAGGED(ch, MOB_CASTER))
          && mob_index[GET_MOB_RNUM(ch)].func != NULL)
        (mob_index[GET_MOB_RNUM(ch)].func) (ch, ch, 0, "");
    }
  }
}