AwakeMUD-0.6Beta/doc/
AwakeMUD-0.6Beta/lib/
AwakeMUD-0.6Beta/lib/etc/
AwakeMUD-0.6Beta/lib/etc/pfiles/
AwakeMUD-0.6Beta/lib/fixer_data/
AwakeMUD-0.6Beta/lib/misc/
AwakeMUD-0.6Beta/lib/plrobjs/
AwakeMUD-0.6Beta/lib/plrobjs/A-E/
AwakeMUD-0.6Beta/lib/plrobjs/F-J/
AwakeMUD-0.6Beta/lib/plrobjs/K-O/
AwakeMUD-0.6Beta/lib/plrobjs/U-Z/
AwakeMUD-0.6Beta/lib/plrspells/A-E/
AwakeMUD-0.6Beta/lib/plrspells/F-J/
AwakeMUD-0.6Beta/lib/plrtext/A-E/
AwakeMUD-0.6Beta/lib/world/
AwakeMUD-0.6Beta/lib/world/mob/
AwakeMUD-0.6Beta/lib/world/obj/
AwakeMUD-0.6Beta/lib/world/qst/
AwakeMUD-0.6Beta/lib/world/shp/
AwakeMUD-0.6Beta/lib/world/wld/
AwakeMUD-0.6Beta/lib/world/zon/
/*************************************************************************
*   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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

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

/* Structures */
struct char_data *combat_list = NULL;   /* head of l-list of fighting chars */
struct char_data *next_combat_list = NULL;

/* External structures */
extern struct char_data *character_list;
extern struct obj_data *obj_proto;
extern struct room_data *world;
extern struct message_list fight_messages[MAX_MESSAGES];
extern int pk_allowed;          /* see config.c */
extern int auto_save;           /* see config.c */
extern int max_exp_gain;        /* see config.c */
extern int top_of_world;
extern int material_ratings[];
extern char *dirs[];
extern int rev_dir[];

int find_sight(struct char_data *ch);
void damage_door(struct char_data *ch, int room, int dir, int power, int type);
void damage_obj(struct char_data *ch, struct obj_data *obj, int power, int type);

/* External procedures */
char *fread_action(FILE * fl, int nr);
char *fread_string(FILE * fl, char *error);
void stop_follower(struct char_data * ch);
ACMD(do_flee);
ACMD(do_action);
void hit(struct char_data * ch, struct char_data * victim, int type);
void docwagon(struct char_data *ch);
void range_combat(struct char_data *ch, char target[MAX_INPUT_LENGTH],
                  struct obj_data *weapon, int range, int dir);
void roll_individual_initiative(struct char_data *ch);
void ranged_response(struct char_data *ch, struct char_data *vict);
int find_weapon_range(struct char_data *ch, struct obj_data *weapon);
void weapon_scatter(struct char_data *ch, struct char_data *victim,
                    struct obj_data *weapon);
void explode(struct char_data *ch, struct obj_data *weapon, int room);
void target_explode(struct char_data *ch, struct obj_data *weapon,
                    int room, int mode);
void forget(struct char_data * ch, struct char_data * victim);
void remember(struct char_data * ch, struct char_data * victim);
int ok_damage_shopkeeper(struct char_data * ch, struct char_data * victim);
extern int success_test(int number, int target);
extern int resisted_test(int num_for_ch, int tar_for_ch, int num_for_vict,
int tar_for_vict);
extern int modify_target(struct char_data *ch);
extern int is_allergic(struct char_data *ch, int type);
extern int skill_web (struct char_data *ch, int skillnumber);
extern bool mob_magic(struct char_data * mob);
extern int reverse_web(struct char_data *, int &, int &);
extern bool attempt_reload(struct char_data *mob, int pos);
extern void Crash_rentsave(struct char_data *ch, int cost);
extern void switch_weapons(struct char_data *mob, int pos);
extern void hunt_victim(struct char_data * ch);
extern void matrix_fight(struct char_data *ch, struct char_data *victim);
extern void check_quest_kill(struct char_data *ch, struct char_data *victim);
extern void check_quest_destroy(struct char_data *ch, struct obj_data *obj);
extern void purge_spell_q(struct char_data *ch);
extern bool cycle_spell_q(struct char_data *ch);
extern int return_general(int);
extern struct index_data *obj_index;
extern struct index_data *mob_index;
extern struct zone_data *zone_table;
extern const struct command_info cmd_info[];
extern void perform_tell(struct char_data *, struct char_data *, char *);
/* Weapon attack texts */
struct attack_hit_type attack_hit_text[] =
{
  {"hit", "hits", "hit"},               /* 0 */
  {"sting", "stings", "sting"},
  {"whip", "whips", "whip"},
  {"slash", "slashes", "slash"},
  {"bite", "bites", "bite"},
  {"bludgeon", "bludgeons", "bludgeon"},        /* 5 */
  {"crush", "crushes", "crush"},
  {"pound", "pounds", "pound"},
  {"claw", "claws", "claw"},
  {"maul", "mauls", "maul"},
  {"thrash", "thrashes", "thrash"},     /* 10 */
  {"pierce", "pierces", "pierce"},
  {"punch", "punches", "punch"},
  {"stab", "stabs", "stab"},
  {"shock", "shocks", "shock"},
  {"shuriken", "shurikens", "shuriken"}, /* 15 */
  {"pierce", "pierces", "pierce"},
  {"pierce", "pierces", "pierce"},
  {"grenade", "grenades", "grenade"},
  {"grenade", "grenades", "grenade"},
  {"rocket", "rockets", "rocket"},  /* 20 */
  {"shoot", "shoots", "shot"},
  {"blast", "blasts", "blast"},
  {"shoot", "shoots", "shot"},
  {"blast", "blasts", "blast"},
  {"blast", "blasts", "burst fire"},    /* 25 */
  {"blast", "blasts", "blast"},
  {"bifurcate", "bifurcates", "BIFURCATION"}
};
int cmd_slp;

void set_attacking(struct char_data *ch, struct char_data *vict, const char *file, int line)
{
  //ch->char_specials.fightList.ADD(vict);
  //vict->char_specials.defendList.ADD(ch);
}

/* The Fight related routines */

void appear(struct char_data * ch)
{
  if (affected_by_spell(ch, SPELL_INVISIBILITY) == 1)
    affect_from_char(ch, SPELL_INVISIBILITY);

  if (affected_by_spell(ch, SPELL_IMPROVED_INVIS) == 1)
    affect_from_char(ch, SPELL_IMPROVED_INVIS);

  REMOVE_BIT(AFF_FLAGS(ch), AFF_IMP_INVIS | AFF_INVISIBLE | AFF_HIDE);

  if (GET_LEVEL(ch) < LVL_LEGEND)
    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;
  struct message_type *messages;
  char chk[128];

  if (!(fl = fopen(MESS_FILE, "r"))) {
    sprintf(buf2, "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, " %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) {
      fprintf(stderr, "Too many combat messages.  Increase MAX_MESSAGES and recompile.");
      exit(1);
    }
    messages = new message_type;
    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);
  cmd_slp = find_command("slap");
}

void update_pos(struct char_data * victim)
{
  if (IS_IC(victim) && GET_MENTAL(victim) < -100) {
    GET_MENTAL(victim) = GET_MAX_MENTAL(victim);
    GET_POS(victim) = POS_STANDING;
    return;
  }

  if ((GET_MENTAL(victim) < 100) && (GET_PHYSICAL(victim) >= 100)) {
    GET_POS(victim) = POS_STUNNED;
    GET_INIT_ROLL(victim) = 0;
    return;
  }

  if ((GET_PHYSICAL(victim) >= 100) && (GET_POS(victim) > POS_STUNNED))
    return;
  else if (GET_PHYSICAL(victim) >= 100) {
    GET_POS(victim) = POS_STANDING;
    return;
  } else if ((int)(GET_PHYSICAL(victim) / 100) <= -GET_BOD(victim))
    GET_POS(victim) = POS_DEAD;
  else
    GET_POS(victim) = POS_MORTALLYW;

  GET_INIT_ROLL(victim) = 0;
}

void check_killer(struct char_data * ch, struct char_data * vict)
{
  if (!IS_NPC(ch) &&
      !IS_NPC(vict) &&
      !PLR_FLAGGED(vict, PLR_KILLER) &&
      !PLR_FLAGGED(vict, PLR_THIEF) &&
      ( !PRF_FLAGGED(ch, PRF_PKER)  || !PRF_FLAGGED(vict, PRF_PKER) ) &&
      !PLR_FLAGGED(ch, PLR_KILLER) &&
      (ch != vict))
  {
    char buf[256];

    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, ch, LOG_MISCLOG, TRUE);
    send_to_char("If you want to be a PLAYER KILLER, so be it...\r\n", ch);
  }
  else if ( (IS_NPC(ch) && ch->desc != NULL && ch->desc->original != NULL) )
  {
    if (!PLR_FLAGGED(vict, PLR_KILLER) &&
      !PLR_FLAGGED(vict, PLR_THIEF) && (!PRF_FLAGGED(ch->desc->original, PRF_PKER)) ||
      !PRF_FLAGGED(vict, PRF_PKER) && !PLR_FLAGGED(ch->desc->original, PLR_KILLER)
      && (ch->desc->original != vict))
    {
      if ( IS_NPC(vict) && vict->desc == NULL )
        return;
      else if ( IS_NPC(vict) && vict->desc != NULL && vict->desc->original != NULL )
      {
	    if (!PLR_FLAGGED(vict->desc->original, PLR_KILLER) &&
	    !PLR_FLAGGED(vict->desc->original, PLR_THIEF) && (!PRF_FLAGGED(ch->desc->original, PRF_PKER) ||
	    !PRF_FLAGGED(vict->desc->original, PRF_PKER)) && !PLR_FLAGGED(ch->desc->original, PLR_KILLER) && (GET_LEVEL(ch->desc->original) < LVL_LEGEND)
	    && (ch->desc->original != vict->desc->original))
	    ;
	    else
	    return;
      }

      char buf[256];

      if(GET_LEVEL(ch->desc->original) > LVL_LEGEND)
      	return;
      SET_BIT(PLR_FLAGS(ch->desc->original), PLR_KILLER);
      sprintf(buf, "PC Killer bit set on %s (while in %s) for initiating attack on %s at %s.",
              GET_NAME(ch->desc->original), ch->player.short_descr, GET_NAME(vict), world[vict->in_room].name);
      mudlog(buf, ch, LOG_MISCLOG, 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(!FIGHTING(ch));

  ch->next_fighting = combat_list;
  combat_list = ch;

  if (IS_AFFECTED(ch, AFF_SLEEP))
    REMOVE_BIT(AFF_FLAGS(ch), AFF_SLEEP);

  roll_individual_initiative(ch);
  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);
  purge_spell_q(ch);
  GET_INIT_ROLL(ch) = 0;
}

void make_corpse(struct char_data * ch)
{
  extern int max_npc_corpse_time, max_pc_corpse_time;
  struct obj_data *create_nuyen(int amount);
  struct obj_data *create_credstick(struct char_data *ch, int amount);

  struct obj_data *corpse, *o, *obj;
  struct obj_data *money;
  int i, nuyen, credits, counter;

  corpse = create_obj();

  corpse->item_number = NOTHING;
  corpse->in_room = NOWHERE;
  sprintf(buf2, "corpse %s", ch->player.name);
  corpse->name = str_dup(buf2);

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

  sprintf(buf2, "^rthe corpse of %s^n", 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 | ITEM_CORPSE);
  GET_OBJ_VAL(corpse, 0) = 0;   /* You can't store stuff in a corpse */
  GET_OBJ_VAL(corpse, 4) = 1;   /* corpse identifier */
  GET_OBJ_WEIGHT(corpse) = MAX(100, GET_WEIGHT(ch)) + IS_CARRYING_W(ch);
  if (IS_NPC(ch)) {
    GET_OBJ_TIMER(corpse) = max_npc_corpse_time;
    GET_OBJ_VAL(corpse, 4) = 0;
    GET_OBJ_VAL(corpse, 5) = ch->nr;
  } else {
    GET_OBJ_TIMER(corpse) = max_pc_corpse_time;
    GET_OBJ_VAL(corpse, 4) = 1;
    GET_OBJ_VAL(corpse, 5) = GET_IDNUM(ch);
    /* make 'em bullet proof...(anti-twink measure) */
    GET_OBJ_BARRIER(corpse) = 75;
  }

  /* transfer character's inventory to the corpse */
  for (o = ch->carrying; o; o = obj) {
    obj = o->next_content;
    obj_from_char(o);
    obj_to_obj(o, corpse);
  }

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

  /* transfer nuyen & credstick */
  if (IS_NPC(ch)) {
    nuyen = (int)(GET_NUYEN(ch) / 10);
    nuyen = number(GET_NUYEN(ch) - nuyen, GET_NUYEN(ch) + nuyen);
    credits = (int)(GET_BANK(ch) / 10);
    credits = number(GET_BANK(ch) - credits, GET_BANK(ch) + credits);
  } else {
    nuyen = GET_NUYEN(ch);
    credits = 0;
  }

  if (IS_NPC(ch) && (nuyen > 0 || credits > 0))
    if (from_ip_zone(GET_MOB_VNUM(ch))) {
      nuyen = 0;
      credits = 0;
    }

  if (nuyen > 0) {
    /* following 'if' clause added to fix gold duplication loophole */
    if (IS_NPC(ch) || (!IS_NPC(ch) && ch->desc)) {
      money = create_nuyen(nuyen);
      obj_to_obj(money, corpse);
    }
    GET_NUYEN(ch) = 0;
  }

  if (credits > 0) {
    money = create_credstick(ch, credits);
    obj_to_obj(money, corpse);
  }

  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.
   */
  GET_ALIGNMENT(ch) += (-GET_ALIGNMENT(victim) - GET_ALIGNMENT(ch)) >> 4;
}

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

  act("$n cries out $s last breath as $e dies!", 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("Somewhere close, 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 obj_data *bio, *obj, *o;
  int i;

  if (FIGHTING(ch))
    stop_fighting(ch);

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

  if (IS_ASTRAL(ch)) {
    act("$n vanishes.", FALSE, ch, 0, 0, TO_ROOM);
    for (i = 0; i < NUM_WEARS; i++)
      if (GET_EQ(ch, i))
        extract_obj(GET_EQ(ch, i));
    for (obj = ch->carrying; obj; obj = o) {
      o = obj->next_content;
      extract_obj(obj);
    }
  } else {
    death_cry(ch);

    if (!(IS_SPIRIT(ch) && GET_MOB_VNUM(ch) >= 25 && GET_MOB_VNUM(ch) <= 42))
      make_corpse(ch);

    if (!IS_NPC(ch)) {
      for (bio = ch->bioware; bio; bio = bio->next_content)
        if (!GET_OBJ_VAL(bio, 2)) {
          GET_OBJ_VAL(bio, 5) = 12;
          GET_OBJ_VAL(bio, 6) = 0;
        } else if (GET_OBJ_VAL(bio, 2) == 4) {
          if (GET_OBJ_VAL(bio, 5) > 0)
            for (i = 0; i < MAX_OBJ_AFFECT; i++)
              affect_modify(ch, bio->affected[i].location, bio->affected[i].modifier,
                                bio->obj_flags.bitvector, FALSE);
          GET_OBJ_VAL(bio, 5) = 0;
        }
      Crash_rentsave(ch, NOWHERE);
      SET_BIT(PLR_FLAGS(ch), PLR_JUST_DIED);
    }
  }

  extract_char(ch);
}

void death_penalty(struct char_data *ch, int is_death_trap)
{
  //if (!IS_NPC(ch)
  //    && ch->real_abils.bod > 1
  //    && !PLR_FLAGGED(ch, PLR_NEWBIE)
  //    && !success_test(MAX((int)(ch->real_abils.ess / 100), 1),
  //       7 - (int)(GET_ESS(ch) / 100)))
  //{
  //  GET_REP(ch) -= ch->real_abils.bod;
  //  ch->real_abils.bod--;
  //  GET_INDEX(ch) -= 100;
  //}

  /*  Body test for attribute loss */

  int attribute = 0;
  int did_loss = 0;

  if(!IS_NPC(ch)
     && !PLR_FLAGGED(ch, PLR_NEWBIE)
     && ch->real_abils.bod > 1
     && GET_REP(ch) > 15
     && !(success_test(GET_BOD(ch),4) >= 1 ))
  {
	did_loss = 1;
	attribute = dice(1,6);
	switch(attribute)
	{
	   case 1:
	   	ch->real_abils.bod--;
	   	GET_INDEX(ch) -= 100;
	   	GET_REP(ch) -= ch->real_abils.bod;
	   	break;
	   case 2:
	   	ch->real_abils.str--;
	   	GET_REP(ch) -= ch->real_abils.str;
	   	break;
	   case 3:
	   	ch->real_abils.qui--;
	   	GET_REP(ch) -= ch->real_abils.qui;
	   	break;
	   case 4:
	   	ch->real_abils.intel--;
	   	GET_REP(ch) -= ch->real_abils.intel;
	   	break;
	   case 5:
	   	ch->real_abils.wil--;
	   	GET_REP(ch) -= ch->real_abils.wil;
	   	break;
	   case 6:
	   	ch->real_abils.rea--;
	   	GET_REP(ch) -= ch->real_abils.rea;
	   	break;
	}
  }

  /* Second checkl for magic loss */
  //*
  if(!IS_NPC(ch)
     && ch->real_abils.mag > 0
     && !PLR_FLAGGED(ch, PLR_NEWBIE)
     && GET_REP(ch) > 15
     && !success_test(2,GET_MAG(ch)))
  {
	 did_loss = 1;
	 //GET_REP(ch) -= ch->real_abils.mag;
	 ch->real_abils.mag -= 100;
	 ch->real_abils.rmag -= 100;
  }
  //*/
  if(did_loss == 0)
  	gain_exp(ch, -(ch->real_abils.bod * 100));
}

void die(struct char_data * ch)
{
  if (!IS_NPC(ch))
  {
    death_penalty(ch, 0);
    REMOVE_BIT(PLR_FLAGS(ch), PLR_WANTED);
  }

  raw_kill(ch);
}

/*
 * Lets the player give up and die if they're at 0 or less
 * physical points.
 */
ACMD(do_die)
{
    char buf[100];

    /* If they're still okay... */
    if ( GET_PHYSICAL(ch) > 0 )
    {
	send_to_char("Your mother would be so sad.. :(\n\r",ch);
	return;
    }

    send_to_char("You give up the will to live..\n\r",ch);

    /* log it */
    sprintf(buf,"%s gave up the will to live. {%s (%d)}",
	GET_NAME(ch), world[ch->in_room].name, world[ch->in_room].number );
    mudlog(buf, ch, LOG_DEATHLOG, TRUE);

    /* Now we just kill them, MuHahAhAhahhaAHhaAHaA!!...or something */
    die(ch);

    return;
}

int calc_karma(struct char_data *ch, struct char_data *vict)
{
  int base = 0, i, counter;
  struct spell_data *spell;
  char rbuf[MAX_STRING_LENGTH];

  if (!vict)
    return 0;

  base = (int)((1.5 * (vict->real_abils.bod + GET_BALLISTIC(vict) + GET_IMPACT(vict))) +
          (vict->real_abils.qui + (int)(vict->real_abils.mag / 100) +
          vict->real_abils.str + vict->real_abils.wil + vict->real_abils.rea));

  for (i = MAX_SPELLS + 1; i <= MAX_SKILLS; i++)
    if (GET_SKILL(vict, i) > 0) {
      if (i == SKILL_SORCERY && GET_MAG(vict) > 0)
        base += (int)(1.15 * GET_SKILL(vict, i));
      else if (return_general(i) == SKILL_FIREARMS ||
           return_general(i) == SKILL_UNARMED_COMBAT ||
           return_general(i) == SKILL_ARMED_COMBAT ||
           i == SKILL_KILL_HANDS)
        base += (int)(1.4 * GET_SKILL(vict, i));
      else if (i > SKILL_PERCEPTION)
        base += (int)(1.10 * GET_SKILL(vict, i));
    }

  if (MOB_FLAGGED(vict, MOB_GUARD))
    base = (int)(base * 1.1);
  if (MOB_FLAGGED(vict, MOB_NOBLIND))
    base = (int)(base * 1.05);
  if (MOB_FLAGGED(vict, MOB_IMMEXPLODE))
    base = (int)(base * 1.05);
  if (IS_NPC(vict) && GET_RACE(vict) == CLASS_SPIRIT)
    base = (int)(base * 1.15);

  if (ch && !IS_NPC(ch)) {
    if (PLR_FLAGGED(ch, PLR_NEWBIE))
      base -= (int)(GET_REP(ch) / 5);
    else
      base -= (int)(GET_REP(ch) / 2);
  }

  if (IS_NPC(vict))
    base += GET_KARMA(vict);
  else if (ch)
    base += (int)(GET_REP(ch) / 4);

  //now to randomize it a bit
  base += (!ch ? 0 : (!number(0,2) ? number(0,5) :
		      0 - number(0,5)));

  base = (ch ? MIN(max_exp_gain, base) : base);
  base = MAX(base, 1);

  if (ch && !IS_NPC(ch) && GET_LEVEL(ch) < LVL_LEGEND && IS_NPC(vict))
    if (from_ip_zone(GET_MOB_VNUM(vict)))
      base = 0;
//if(ch && base  > 10 && !PLR_FLAGGED(ch,PLR_NEWBIE))
//  	base = 10;
  return base;
}

void perform_group_gain(struct char_data * ch, int base, struct char_data * victim)
{
  int share;

  share = MIN(max_exp_gain, MAX(1, base));
  if (!IS_NPC(ch))
    share = MIN(base, (PLR_FLAGGED(ch, PLR_NEWBIE) ? 20 : GET_REP(ch) * 2));

  /* psuedo-fix of the group with a newbie to get more exp exploit */
  if ( !PLR_FLAGGED(ch, PLR_NEWBIE) )
    share /= 2;

  share = gain_exp(ch, share);

  send_to_char(ch, "You receive your share of %0.2f karma.\r\n", ((float)share / 100));

  change_alignment(ch, victim);
}

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

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

  if (IS_AFFECTED(k, AFF_GROUP) && (k->in_room == ch->in_room))
    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++;

  base = calc_karma(k, victim);

  if (tot_members >= 1)
    base = MAX(1, (base / tot_members));
  else base = 0;

  if (IS_AFFECTED(k, AFF_GROUP) && k->in_room == ch->in_room)
    perform_group_gain(k, base, 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, base, victim);
}

char *replace_string(char *str, char *weapon_singular, char *weapon_plural,
                     char *weapon_different)
{
  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;
      case 'd':
        for (; *weapon_different; *(cp++) = *(weapon_different++));
        break;
      default:
        *(cp++) = '#';
        break;
      }
    } else
      *(cp++) = *str;

    *cp = 0;
  }                             /* For */

  return (buf);
}

#define SENDOK(ch) ((ch)->desc && AWAKE(ch) && !(PLR_FLAGGED((ch), PLR_WRITING) || PLR_FLAGGED((ch), PLR_EDITING) || PLR_FLAGGED((ch), PLR_MAILING) || PLR_FLAGGED((ch), PLR_CUSTOMIZE)) && (STATE(ch->desc) != CON_SPELL_CREATE))

/* 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;
  struct char_data *vict, *witness;

  static struct dam_weapon_type {
    char *to_room;
    char *to_char;
    char *to_victim;
  }
    dam_weapons[] = {
    {
      "$n tries to #w $N, but misses.", /* 0: 0     */
      "You try to #w $N, but miss.",
      "$n tries to #w you, but misses."
    },

    {
      "$n grazes $N as $e #W $M.",      /* 1: 1..2  */
      "You graze $N as you #w $M.",
      "$n grazes 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 #d.", /* 7: 19..23 */
      "You massacre $N to small fragments with your #d.",
      "$n massacres you to small fragments with $s #d."
    },

    {
      "$n demolishes $N with $s deadly #d!",    /* 8: > 23   */
      "You demolish $N with your deadly #d!",
      "$n demolishes you with $s deadly #d!"
    },

    {
      "$n pulverizes $N with $s incredibly powerful #d!!",
      "You pulverize $N with your incredibly powerful #d!!",
      "$n pulverizes you with $s incredibly powerful #d!!",
    },

    {
      "$n sublimates $N with an ultimate #d!!!",
      "You sublimate $N with an ultimate #d!!!",
      "$n sublimates you with an ultimate #d!!!",
    }
  };


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

  if (dam < 0)
    msgnum = 0;
  else if (dam == 0) {
     switch(number(0,1)) {
       case 0: msgnum = 1; break;
       case 1: msgnum = 2; break;
       default: msgnum = 0; break;
     }
  } else if (dam <= 1)
    msgnum = 3;
  else if (dam <= 3)
    msgnum = 5;
  else if (dam <= 6)
    msgnum = 7;
  else if (dam <= 10)
    msgnum = 8;
  else if (dam <= 13)
    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,
          attack_hit_text[w_type].different);
  for (witness = world[victim->in_room].people; witness; witness = witness->next_in_room)
    if (witness != ch && witness != victim && !PRF_FLAGGED(witness, PRF_FIGHTGAG) && SENDOK(witness))
      perform_act(buf, ch, NULL, victim, witness);
  if (ch->in_room != victim->in_room)
    for (witness = world[ch->in_room].people; witness; witness = witness->next_in_room)
      if (witness != ch && witness != victim && !PRF_FLAGGED(witness, PRF_FIGHTGAG) && SENDOK(witness))
        perform_act(buf, ch, NULL, victim, witness);

  /* damage message to damager */
  strcpy(buf1, "^y");
  buf = replace_string(dam_weapons[msgnum].to_char,
          attack_hit_text[w_type].singular, attack_hit_text[w_type].plural,
          attack_hit_text[w_type].different);
  strcat(buf1, buf);
  act(buf1, FALSE, ch, NULL, victim, TO_CHAR);

  /* damage message to damagee */
  strcpy(buf1, "^r");
  buf = replace_string(dam_weapons[msgnum].to_victim,
          attack_hit_text[w_type].singular, attack_hit_text[w_type].plural,
          attack_hit_text[w_type].different);
  strcat(buf1, buf);
  act(buf1, FALSE, ch, NULL, victim, TO_VICT | TO_SLEEP);
}
#undef SENDOK

/*
 * 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 = ch->equipment[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_LEGEND) && dam < 1) {
        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)) {  /* 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;
}

int find_orig_dir(struct char_data *ch, struct char_data *victim)
{
  struct char_data *vict;
  int nextroom, dir, dist, room;

  for (dir = 0; dir < (NUM_OF_DIRS - 1); dir++) {
    room = victim->in_room;

    if (CAN_GO2(room, dir))
      nextroom = EXIT2(room, dir)->to_room;
    else nextroom = NOWHERE;

    for (dist = 1; nextroom != NOWHERE && dist <= 4; dist++) {
      for (vict = world[nextroom].people; vict; vict = vict->next_in_room)
        if (vict == ch)
          return (rev_dir[dir]);

      room = nextroom;
      if (CAN_GO2(room, dir))
        nextroom = EXIT2(room, dir)->to_room;
      else nextroom = NOWHERE;
    }
  }
  return -1;
}

void weapon_scatter(struct char_data *ch, struct char_data *victim, struct obj_data *weapon)
{
  struct char_data *vict;
  char ammo_type[20];
  int damage_total, power, total = 0, door = 0, dir[3], i;

  if (!ch || !victim || !weapon || ch->in_room == victim->in_room)
    return;

  dir[1] = find_orig_dir(ch, victim);
  if (dir[1] >= NORTH && dir[1] <= NORTHWEST) {
    if (dir[1] == NORTH)
      dir[0] = NORTHWEST;
    else dir[0] = dir[1] - 1;
    if (dir[1] == NORTHWEST)
      dir[2] = NORTH;
    else dir[2] = dir[1] + 1;
  } else dir[0] = dir[2] = -1;

  for (vict = world[victim->in_room].people; vict; vict = vict->next_in_room)
    if (vict != victim && !IS_ASTRAL(vict) && GET_POS(vict) > POS_SLEEPING)
      total++;

  for (i = 0; i < 3; i++)
    if (dir[i] != -1 && EXIT(victim, dir[i]) &&
        IS_SET(EXIT(victim, dir[i])->exit_info, EX_CLOSED))
      door += 2;

  switch(GET_OBJ_VAL(weapon, 3)) {
    case TYPE_SHOTGUN:
      sprintf(ammo_type, "horde of pellets");
      break;
    case TYPE_MACHINE_GUN:
      sprintf(ammo_type, "stream of bullets");
      break;
    case TYPE_CANNON:
      sprintf(ammo_type, "shell");
      break;
    case TYPE_ROCKET:
      sprintf(ammo_type, "rocket");
      break;
    default:
      sprintf(ammo_type, "bullet");
      break;
  }

  i = number(1, MAX(20, total + door + 2));
  if (i <= total) { // hits a victim
    for (vict = world[victim->in_room].people; vict; vict = vict->next_in_room)
      if (vict != victim && !IS_ASTRAL(vict) && GET_POS(vict) > POS_SLEEPING &&
          !number(0, total - 1))
        break;

    if (vict && (IS_NPC(vict) || (!IS_NPC(vict) && vict->desc))) {
      sprintf(buf, "A %s flies in from nowhere, hitting you!", ammo_type);
      act(buf, FALSE, vict, 0, 0, TO_CHAR);
      sprintf(buf, "A %s hums into the room and hits $n!", ammo_type);
      act(buf, FALSE, vict, 0, 0, TO_ROOM);
      power = MAX(GET_OBJ_VAL(weapon, 0) - GET_BALLISTIC(vict) - 3, 2);
      damage_total = MAX(1, GET_OBJ_VAL(weapon, 1));
      damage_total = convert_damage(stage((2 - success_test(GET_BOD(vict), power +
                                    modify_target(vict))), damage_total));
      damage(ch, vict, damage_total, TYPE_SCATTERING, PHYSICAL);
      return;
    }
  } else if (i > (MAX(20, total + door + 2) - door)) { // hits a door
    for (i = 0; i < 3; i++)
      if (dir[i] != -1 && EXIT(victim, dir[i]) && !number(0, door - 1) &&
          IS_SET(EXIT(victim, dir[i])->exit_info, EX_CLOSED))
        break;

    if (i < 3) {
      sprintf(buf, "A %s hums into the room and hits the %s!", ammo_type,
              fname(EXIT(victim, dir[i])->keyword));
      act(buf, FALSE, victim, 0, 0, TO_ROOM);
      act(buf, FALSE, victim, 0, 0, TO_CHAR);
      damage_door(NULL, victim->in_room, dir[i], GET_OBJ_VAL(weapon, 1) * 2 +
                  (int)(GET_OBJ_VAL(weapon, 0) / 6), DAMOBJ_PROJECTILE);
      return;
    }
  }

  // if it's reached this point, it's harmless
  sprintf(buf, "A %s hums harmlessly through the room.", ammo_type);
  act(buf, FALSE, victim, 0, 0, TO_ROOM);
  act(buf, FALSE, victim, 0, 0, TO_CHAR);
}

void damage_equip(struct char_data *ch, struct char_data *victim, int power,
                  int attacktype)
{
  int i = number(0, 39);

  if (i >= WEAR_PATCH || !GET_EQ(victim, i))
    return;

  if (attacktype >= TYPE_PISTOL && attacktype <= TYPE_BIFURCATE) {
    if (i >= WEAR_EYES)
      return;
    damage_obj(ch, GET_EQ(victim, i), power, DAMOBJ_PROJECTILE);
  } switch (attacktype) {
    case TYPE_PIERCE: case TYPE_STAB: case TYPE_SHURIKEN:
      if (i >= WEAR_EYES)
        return;
      damage_obj(ch, GET_EQ(victim, i), power, DAMOBJ_PIERCE);
      break;
    case TYPE_STING: case TYPE_SLASH: case TYPE_CLAW:
    case TYPE_THRASH: case TYPE_ARROW: case TYPE_THROWING_KNIFE:
      if (i >= WEAR_EYES)
        return;
      damage_obj(ch, GET_EQ(victim, i), power, DAMOBJ_SLASH);
      break;
    case TYPE_HIT: case TYPE_BLUDGEON: case TYPE_POUND:
    case TYPE_MAUL: case TYPE_PUNCH:
      damage_obj(ch, GET_EQ(victim, i), power, DAMOBJ_CRUSH);
      break;
  }
}

float power_multiplier(int type, int material)
{
  switch (type) {
    case DAMOBJ_ACID:
      switch (material) {
        case 6: case 7: case 10: case 11: return 1.2;
        case 13:                          return 2.0;
      }
      break;
    case DAMOBJ_AIR:
      switch (material) {
        case 2:                                    return 2.0;
        case 5: case 8: case 14: case 15: case 16: return 0.4;
        default:                                   return 0.8;
      }
      break;
    case DAMOBJ_EARTH:
      switch (material) {
        case 2:    return 2.0;
        default:   return 0.9;
      }
      break;
    case DAMOBJ_FIRE:
      switch (material) {
        case 0: case 1: case 3: return 1.5;
        case 4: case 6: case 7: return 1.1;
        default:                return 0.8;
      }
      break;
    case DAMOBJ_ICE:
      switch (material) {
        case 3: case 4: return 1.2;
        default:        return 0.9;
      }
      break;
    case DAMOBJ_LIGHTNING:
      switch (material) {
        case 8:                return 2.0;
        case 10: case 11:      return 5.0;
        default:               return 0.7;
      }
      break;
    case DAMOBJ_WATER:
      switch (material) {
        case 10: case 11: return 3.0;
        default:          return 0.5;
      }
      break;
    case DAMOBJ_EXPLODE:
      switch (material) {
        case 0: case 1: case 3: case 4: case 13: return 1.3;
        case 2:                                  return 3.0;
        default:                                 return 1.1;
      }
      break;
    case DAMOBJ_PROJECTILE:
      switch (material) {
        case 2:           return 3.0;
        case 10: case 11: return 1.1;
        default:          return 0.8;
      }
      break;
    case DAMOBJ_CRUSH:
      switch (material) {
        case 1: case 6: case 7:            return 1.4;
        case 2:                            return 4.0;
        case 5: case 14: case 15: case 16: return 1.0;
        default:                           return 0.7;
      }
      break;
    case DAMOBJ_SLASH:
      switch (material) {
        case 0: case 3: case 4: case 13:   return 1.4;
        case 5: case 14: case 15: case 16: return 0.3;
        default:                           return 0.6;
      }
      break;
    case DAMOBJ_PIERCE:
      switch (material) {
        case 0: case 3: case 4: case 13:           return 1.0;
        case 5: case 8: case 14: case 15: case 16: return 0.1;
        default:                                   return 0.3;
      }
      break;
  }
  return 1.0;
}

void damage_door(struct char_data *ch, int room, int dir, int power, int type)
{
  if (room < 0 || room > top_of_world || dir < NORTH || dir > UP ||
      !world[room].dir_option[dir] || !world[room].dir_option[dir]->keyword ||
      !IS_SET(world[room].dir_option[dir]->exit_info, EX_CLOSED))
    return;

  int modifier, dam, target, rating, half, opposite, rev, ok = 0;

  opposite = world[room].dir_option[dir]->to_room;
  rev = rev_dir[dir];
  if (opposite > -1 && world[opposite].dir_option[rev] &&
      world[opposite].dir_option[rev]->to_room == room)
    ok = TRUE;

  if (IS_SET(type, DAMOBJ_MANIPULATION)) {
    rating = world[room].dir_option[dir]->barrier;
    modifier = 2;
    REMOVE_BIT(type, DAMOBJ_MANIPULATION);
  } else {
    rating = world[room].dir_option[dir]->barrier << 1;
    modifier = 4;
  }

  half = MAX(1, rating >> 1);

  modifier -= power >> 2;

  if (success_test(2, MAX(2, material_ratings[world[room].dir_option[dir]->material] + modifier)))
    power = (int)(power * power_multiplier(type, world[room].dir_option[dir]->material));

  if (power < half) {
    sprintf(buf, "The %s remains undamaged.\r\n", fname(world[room].dir_option[dir]->keyword));
    send_to_room(buf, room);
    if (ch && ch->in_room != room)
      send_to_char(buf, ch);
    return;
  } else if (power < rating) {
    sprintf(buf, "The %s has been slightly damaged.\r\n",
            fname(world[room].dir_option[dir]->keyword));
    send_to_room(buf, room);
    if (ch && ch->in_room != room)
      send_to_char(buf, ch);
    world[room].dir_option[dir]->condition--;
  } else {
    sprintf(buf, "The %s has been damaged!\r\n", fname(world[room].dir_option[dir]->keyword));
    send_to_room(buf, room);
    if (ch && ch->in_room != room)
      send_to_char(buf, ch);
    world[room].dir_option[dir]->condition -= 1 + (power - rating) / half;
  }

  if (ok)
    world[opposite].dir_option[rev]->condition = world[room].dir_option[dir]->condition;

  if (world[room].dir_option[dir]->condition <= 0) {
    sprintf(buf, "The %s has been destroyed!\r\n", fname(world[room].dir_option[dir]->keyword));
    send_to_room(buf, room);
    if (ch && ch->in_room != room)
      send_to_char(buf, ch);
    REMOVE_BIT(world[room].dir_option[dir]->exit_info, EX_CLOSED);
    REMOVE_BIT(world[room].dir_option[dir]->exit_info, EX_LOCKED);
    SET_BIT(world[room].dir_option[dir]->exit_info, EX_DESTROYED);
    if (ok) {
      sprintf(buf, "The %s is destroyed from the other side!\r\n",
              fname(world[room].dir_option[dir]->keyword));
      send_to_room(buf, opposite);
      REMOVE_BIT(world[opposite].dir_option[rev]->exit_info, EX_CLOSED);
      REMOVE_BIT(world[opposite].dir_option[rev]->exit_info, EX_LOCKED);
      SET_BIT(world[opposite].dir_option[rev]->exit_info, EX_DESTROYED);
    }
  }
}

// damage_obj does what its name says, it figures out effects of successes
// damage, applies that damage to that object, and sends messages to the player
void damage_obj(struct char_data *ch, struct obj_data *obj, int power, int type)
{
  if (!obj)
    return;
  if (power <= 0)
    return;

  int success, modifier, dam, target, rating, half;
  struct char_data *vict = (obj->worn_by ? obj->worn_by : obj->carried_by);
  struct obj_data *temp, *next;

// PC corpses are indestructable by normal means
  if ( IS_OBJ_STAT(obj, ITEM_CORPSE) && GET_OBJ_VAL(obj, 4) == 1 )
  {
    if ( ch != NULL )
      send_to_char("Nuh uh fuck nut.\n\r",ch);
    return;
  }
  if (IS_SET(type, DAMOBJ_MANIPULATION)) {
    rating = GET_OBJ_BARRIER(obj);
    modifier = 2;
    REMOVE_BIT(type, DAMOBJ_MANIPULATION);
  } else {
    rating = GET_OBJ_BARRIER(obj) << 1;
    modifier = 4;
  }

  half = MAX(1, rating >> 1);

  modifier -= power >> 2;

  success = success_test(2, MAX(2, material_ratings[GET_OBJ_MATERIAL(obj)] + modifier));

  if (success > 0) {
    switch (type) {
      case DAMOBJ_ACID:
        if (GET_OBJ_TYPE(obj) == ITEM_ARMOR && success == 2)
          switch (number(0, 4)) {
            case 0:
              GET_OBJ_VAL(obj, 0) = MAX(GET_OBJ_VAL(obj, 0) - 1, 0);
              GET_OBJ_VAL(obj, 1) = MAX(GET_OBJ_VAL(obj, 1) - 1, 0);
              break;
            case 1: case 2:
              GET_OBJ_VAL(obj, 0) = MAX(GET_OBJ_VAL(obj, 0) - 1, 0);
              break;
            case 3: case 4:
              GET_OBJ_VAL(obj, 1) = MAX(GET_OBJ_VAL(obj, 1) - 1, 0);
              break;
          }
        break;
      case DAMOBJ_FIRE: case DAMOBJ_EXPLODE:
        if (GET_OBJ_TYPE(obj) == ITEM_GUN_CLIP && success == 2 && vict) {
          act("$p ignites, spraying bullets about!", FALSE, vict, obj, 0, TO_CHAR);
          act("One of $n's clips ignites, spraying bullets about!",
              FALSE, vict, obj, 0, TO_ROOM);
          target = (int)(GET_OBJ_VAL(obj, 0) / 4);
          switch (GET_OBJ_VAL(obj, 1) + 300) {
            case TYPE_ROCKET:
              dam = DEADLY; break;
            case TYPE_GRENADE_LAUNCHER: case TYPE_CANNON:
              dam = SERIOUS; break;
            case TYPE_SHOTGUN: case TYPE_MACHINE_GUN: case TYPE_RIFLE:
              dam = MODERATE; break;
            default:
              dam = LIGHT;
          }
          dam = convert_damage(stage(-success_test(GET_BOD(vict) + GET_DEFENSE(vict),
                               target + modify_target(vict)), dam));
          damage(vict, vict, dam, TYPE_SCATTERING, TRUE);
          extract_obj(obj);
          return;
        }
        if (GET_OBJ_TYPE(obj) == ITEM_WEAPON && success == 2 &&
            GET_OBJ_VAL(obj, 3) == TYPE_HAND_GRENADE) {
          if (vict) {
            act("$p is set off by the fire!", FALSE, vict, obj, 0, TO_CHAR);
            act("A $p carried by $n is set off by the fire!", FALSE, vict, obj, 0, TO_ROOM);
            explode(NULL, obj, obj->in_room);
          } else if (obj->in_room != NOWHERE) {
            sprintf(buf, "%s is set off by the flames!", CAP(obj->short_description));
            send_to_room(buf, obj->in_room);
            explode(NULL, obj, obj->in_room);
          }
          return;
        }
        break;
    }
    power = (int)(power * power_multiplier(type, GET_OBJ_MATERIAL(obj)));
  }

  if (power < half) {
    if (ch)
      send_to_char(ch, "%s remains undamaged.\r\n", CAP(obj->short_description));
    return;
  } else if (power < rating) {
    if (ch)
      send_to_char(ch, "%s has been slightly damaged.\r\n", CAP(obj->short_description));
    if (vict && vict != ch)
      send_to_char(vict, "%s has been slightly damaged.\r\n", CAP(obj->short_description));
    GET_OBJ_CONDITION(obj)--;
  } else {
    if (ch)
      send_to_char(ch, "%s has been damaged!\r\n", CAP(obj->short_description));
    if (vict && vict != ch)
      send_to_char(vict, "%s has been damaged!\r\n", CAP(obj->short_description));
    GET_OBJ_CONDITION(obj) -= 1 + (power - rating) / half;
  }

  // if the end result is that the object's condition rating is 0 or less
  // it's destroyed -- a good reason to keep objects in good repair
  if (GET_OBJ_CONDITION(obj) <= 0) {
    if (ch)
      send_to_char(ch, "%s has been destroyed!\r\n", CAP(obj->short_description));
    if (vict && vict != ch)
      send_to_char(vict, "%s has been destroyed!\r\n", CAP(obj->short_description));
    if (ch && !IS_NPC(ch) && GET_QUEST(ch))
      check_quest_destroy(ch, obj);
    else if (ch && AFF_FLAGGED(ch, AFF_GROUP) && ch->master &&
             !IS_NPC(ch->master) && GET_QUEST(ch->master))
      check_quest_destroy(ch->master, obj);
    for (temp = obj->contains; temp; temp = next) {
      next = temp->next_content;
      obj_from_obj(temp);
      if (IS_OBJ_STAT(obj, ITEM_CORPSE) && !GET_OBJ_VAL(obj, 4) &&
          GET_OBJ_TYPE(temp) != ITEM_MONEY)
        extract_obj(temp);
      else if (vict)
        obj_to_char(temp, vict);
      else if (obj->in_room != NOWHERE)
        obj_to_room(temp, obj->in_room);
      else extract_obj(temp);
    }
    extract_obj(obj);
  }
}

#define DOCWAGON_ROOM   2127

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

  switch (SECT(ch->in_room)) {
    case SECT_INSIDE:
      sprintf(buf,"A DocWagon employee suddenly appears, teleports %s's body to\r\nsafety, and dissolves into thin air.", GET_NAME(ch));
      act(buf,FALSE, ch, 0, 0, TO_ROOM);
      break;
    case SECT_WATER_SWIM: case SECT_WATER_NOSWIM:
      sprintf(buf,"A DocWagon armored speedboat arrives, loading %s's body on\r\nboard before leaving.", GET_NAME(ch));
      act(buf, FALSE, ch, 0, 0, TO_ROOM);
      break;
    case SECT_UNDERWATER:
      sprintf(buf,"A DocWagon submarine silently arrives and loads %s's body into the\r\ndecompression chamber.", GET_NAME(ch));
      act(buf, FALSE, ch, 0, 0, TO_ROOM);
      break;
    default:
      sprintf(buf,"A DocWagon helicopter flies in, taking %s's body to safety.", GET_NAME(ch));
      act(buf, FALSE, ch, 0, 0, TO_ROOM);
      break;
  }

  mudlog(buf, ch, LOG_DEATHLOG, TRUE);
}

void docwagon(struct char_data *ch)
{
  int i, creds;
  struct obj_data *docwagon = NULL, *obj, *temp;

  if (IS_NPC(ch))
    return;

  for (obj = ch->carrying; obj && !docwagon; obj = obj->next_content) {
    if (GET_OBJ_TYPE(obj) == ITEM_DOCWAGON && GET_OBJ_VAL(obj, 1) == GET_IDNUM(ch))
      docwagon = obj;
    else for (temp = obj->contains; temp && !docwagon; temp = temp->next_content)
      if (GET_OBJ_TYPE(temp) == ITEM_DOCWAGON && GET_OBJ_VAL(temp, 1) == GET_IDNUM(ch))
        docwagon = temp;
  }

  for (i = 0; (i < NUM_WEARS && !docwagon); i++)
    if ((obj = GET_EQ(ch, i))) {
      if (GET_OBJ_TYPE(obj) == ITEM_DOCWAGON && GET_OBJ_VAL(obj, 1) == GET_IDNUM(ch))
        docwagon = obj;
      else for (temp = obj->contains; temp && !docwagon; temp = temp->next_content)
        if (GET_OBJ_TYPE(temp) == ITEM_DOCWAGON && GET_OBJ_VAL(temp, 1) == GET_IDNUM(ch))
          docwagon = temp;
    }

  if (!docwagon)
    return;

  if (success_test(GET_OBJ_VAL(docwagon, 0),
      MAX(zone_table[world[ch->in_room].zone].security, 4))) {
    if (FIGHTING(ch) && FIGHTING(FIGHTING(ch)) == ch)
      stop_fighting(FIGHTING(ch));
    if (FIGHTING(ch))
      stop_fighting(ch);
    docwagon_message(ch);
    GET_PHYSICAL(ch) = GET_MAX_PHYSICAL(ch);
    GET_MENTAL(ch) = 0;
    SET_BIT(AFF_FLAGS(ch), AFF_SLEEP);
    update_pos(ch);
    send_to_char("\r\n\r\nYour last conscious memory is the arrival of a DocWagon.\r\n", ch);
    char_from_room(ch);
    char_to_room(ch, real_room(DOCWAGON_ROOM));
    creds = MAX((number(8, 12) * 500 / GET_OBJ_VAL(docwagon, 0)), (int)(GET_NUYEN(ch) / 10));
    if ((GET_NUYEN(ch) + GET_BANK(ch)) < creds) {
      send_to_char("Not finding sufficient payment, your DocWagon contract was retracted.\r\n", ch);
      extract_obj(docwagon);
    } else if (GET_BANK(ch) < creds) {
      GET_NUYEN(ch) -= (creds - GET_BANK(ch));
      GET_BANK(ch) = 0;
    } else GET_BANK(ch) -= creds;
  }
  return;
}

#undef DOCWAGON_ROOM

void check_adrenaline(struct char_data *ch, int mode)
{
  int i, dam;
  struct obj_data *bio, *pump = NULL;

  for (bio = ch->bioware; bio && !pump; bio = bio->next_content)
    if (GET_OBJ_VAL(bio, 2) == 4)
      pump = bio;

  if (!pump)
    return;

  if (GET_OBJ_VAL(pump, 5) == 0 && mode == 1) {
    GET_OBJ_VAL(pump, 5) = dice(GET_OBJ_VAL(pump, 0), 6);
    GET_OBJ_VAL(pump, 6) = GET_OBJ_VAL(pump, 5);
    send_to_char("Your body is wracked with renewed vitality as adrenaline "
                 "pumps into your\r\nbloodstream.\r\n", ch);
    for (i = 0; i < MAX_OBJ_AFFECT; i++)
      affect_modify(ch, pump->affected[i].location, pump->affected[i].modifier,
                        pump->obj_flags.bitvector, TRUE);
  } else if (GET_OBJ_VAL(pump, 5) > 0 && !mode) {
    GET_OBJ_VAL(pump, 5)--;
    if (GET_OBJ_VAL(pump, 5) == 0) {
      for (i = 0; i < MAX_OBJ_AFFECT; i++)
        affect_modify(ch, pump->affected[i].location, pump->affected[i].modifier,
                          pump->obj_flags.bitvector, FALSE);
      GET_OBJ_VAL(pump, 5) = -number(168, 180);
      send_to_char("Your body softens and relaxes as the adrenaline wears off.\r\n", ch);
      dam = convert_damage(stage(-success_test(GET_BOD(ch),
            (int)(GET_OBJ_VAL(pump, 6) / 2) + modify_target(ch)), DEADLY));
      GET_OBJ_VAL(pump, 6) = 0;
      damage(ch, ch, dam, TYPE_BIOWARE, FALSE);
    }
  } else if (GET_OBJ_VAL(pump, 5) < 0 && !mode)
    GET_OBJ_VAL(pump, 5)++;
}

void gen_death_msg(struct char_data *ch, struct char_data *vict, int attacktype)
{
  switch (attacktype) {
    case TYPE_SUFFERING:
      switch (number(0, 4)) {
        case 0:
          sprintf(buf2, "%s died (no blood, it seems). {%s (%d)}",
                  GET_NAME(vict), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
        case 1:
          sprintf(buf2, "%s's brain ran out of oxygen {%s (%d)}",
                  GET_NAME(vict), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
        case 2:
          sprintf(buf2, "%s simply ran out of blood. {%s (%d)}",
                  GET_NAME(vict), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
        case 3:
          sprintf(buf2, "%s unwittingly committed suicide. {%s (%d)}",
                  GET_NAME(vict), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
        case 4:
          sprintf(buf2, "Bloodlack stole %s's life. {%s (%d)}",
                  GET_NAME(vict), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
      }
      break;
    case TYPE_EXPLOSION:
      switch (number(0, 4)) {
        case 0:
          sprintf(buf2, "%s blew %s to pieces. {%s (%d)}", ch == vict ? "???"
                  : GET_NAME(ch), GET_NAME(vict), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
        case 1:
          sprintf(buf2, "%s was blown to bits. [%s] {%s (%d)}",
                  GET_NAME(vict), ch == vict ? "???" : GET_NAME(ch),
                  world[vict->in_room].name, world[vict->in_room].number);
          break;
        case 2:
          sprintf(buf2, "%s got vaporized by %s. {%s (%d)}",
                  GET_NAME(vict), ch == vict ? "???" : GET_NAME(ch),
                  world[vict->in_room].name, world[vict->in_room].number);
          break;
        case 3:
          sprintf(buf2, "%s was incinerated by an explosion. [%s] {%s (%d)}",
                  GET_NAME(vict), ch == vict ? "???" : GET_NAME(ch),
                  world[vict->in_room].name, world[vict->in_room].number);
          break;
        case 4:
          sprintf(buf2, "%s got blown to hell by %s. {%s (%d)}",
                  GET_NAME(vict), ch == vict ? "???" : GET_NAME(ch),
                  world[vict->in_room].name, world[vict->in_room].number);
          break;
      }
      break;
    case TYPE_SCATTERING:
      switch (number(0, 4)) {
        case 0:
          sprintf(buf2, "%s accidentally (?) killed by %s. {%s (%d)}",
                  GET_NAME(vict), GET_NAME(ch), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
        case 1:
          sprintf(buf2, "Shouldn't have been standing there, should you %s? "
                  "[%s] {%s (%d)}", GET_NAME(vict), GET_NAME(ch),
                  world[vict->in_room].name, world[vict->in_room].number);
          break;
        case 2:
          sprintf(buf2, "Oops....%s just blew %s's head off. {%s (%d)}",
                  GET_NAME(ch), GET_NAME(vict), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
        case 3:
          sprintf(buf2, "%s's stray bullet caught %s in the heart.  "
                  "What a shame. {%s (%d)}", GET_NAME(ch), GET_NAME(vict),
                  world[vict->in_room].name, world[vict->in_room].number);
          break;
        case 4:
          sprintf(buf2, "A random bullet killed a random person -- poor %s. "
                  "[%s] {%s (%d)}", GET_NAME(vict), GET_NAME(ch),
                  world[vict->in_room].name, world[vict->in_room].number);
          break;
      }
      break;
    case TYPE_FALL:
      switch (number(0, 4)) {
        case 0:
          sprintf(buf2, "%s died on impact. {%s (%d)}", GET_NAME(vict),
                  world[vict->in_room].name, world[vict->in_room].number);
          break;
        case 1:
          sprintf(buf2, "%s failed to miss the ground. {%s (%d)}",
                  GET_NAME(vict), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
        case 2:
          sprintf(buf2, "Life's a bitch, %s.  So's concrete. {%s (%d)}",
                  GET_NAME(vict), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
        case 3:
          sprintf(buf2, "What %s wouldn't have given for a safety net... "
                  "{%s (%d)}", GET_NAME(vict), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
        case 4:
          sprintf(buf2, "The ground can be such an unforgiving thing.  "
                  "Right, %s? {%s (%d)}", GET_NAME(vict),
                  world[vict->in_room].name, world[vict->in_room].number);
          break;
      }
      break;
    case TYPE_DROWN:
      switch (number(0, 4)) {
        case 0:
          sprintf(buf2, "%s drank way, way, WAY too much. {%s (%d)}",
                  GET_NAME(vict), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
        case 1:
          sprintf(buf2, "%s swims with the fishes.  Involuntarily. {%s (%d)}",
                  GET_NAME(vict), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
        case 2:
          sprintf(buf2, "The water had a fight with %s's lungs.  "
                  "The water won. {%s (%d)}", GET_NAME(vict),
                  world[vict->in_room].name, world[vict->in_room].number);
          break;
        case 3:
          sprintf(buf2, "Water doesn't seem so harmless now, does it %s? "
                  "{%s (%d)}", GET_NAME(vict), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
        case 4:
          sprintf(buf2, "%s didn't float. {%s (%d)}", GET_NAME(vict),
                  world[vict->in_room].name, world[vict->in_room].number);
          break;
      }
      break;
    case TYPE_ALLERGY:
      switch (number(0, 2)) {
        case 0:
          sprintf(buf2, "%s died from an allergy, of all things. {%s (%d)}",
                  GET_NAME(vict), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
        case 1:
          sprintf(buf2, "%s, didn't the doctor say NOT to go near that "
                  "stuff? {%s (%d)}", GET_NAME(vict),
                  world[vict->in_room].name, world[vict->in_room].number);
          break;
        case 2:
          sprintf(buf2, "%s died a warrior's death...from an allergy. "
                  "{%s (%d)}", GET_NAME(vict), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
      }


      break;
    case TYPE_BIOWARE:
      switch (number(0, 4)) {
        case 0:
          sprintf(buf2, "%s just hasn't been taking %s medication.  Oops. "
                  "{%s (%d)}", GET_NAME(vict), GET_SEX(vict) == SEX_MALE ?
                  "his" : (GET_SEX(vict) == SEX_FEMALE ? "her" : "its"),
                  world[vict->in_room].name, world[vict->in_room].number);
          break;
        case 1:
          sprintf(buf2, "%s was killed in a bioware rebellion. {%s (%d)}",
                  GET_NAME(vict), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
        case 2:
          sprintf(buf2, "%s has a fatal heart attack.  Wuss. {%s (%d)}",
                  GET_NAME(vict), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
        case 3:
          sprintf(buf2, "Still think the bioware was worth it, %s?. {%s (%d)}",
                  GET_NAME(vict), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
        case 4:
          sprintf(buf2, "Maybe %s got a defective piece of bioware... {%s (%d)}",
                  GET_NAME(vict), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
      }
      break;
    case TYPE_RECOIL:
      switch (number(0, 4)) {
        case 0:
          sprintf(buf2, "You're meant to hit *other* people with that whip, "
                  "%s. {%s (%d)}", GET_NAME(vict), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
        case 1:
          sprintf(buf2, "%s lopped off %s own head.  Oops. {%s (%d)}",
                  GET_NAME(vict), GET_SEX(vict) == SEX_MALE ? "his" :
                  (GET_SEX(vict) == SEX_FEMALE ? "her" : "its"),
                  world[vict->in_room].name, world[vict->in_room].number);
          break;
        case 2:
          sprintf(buf2, "%s, watch out for your whi....nevermind. {%s (%d)}",
                  GET_NAME(vict), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
        case 3:
          sprintf(buf2, "THWAP!  Wait.....was that *your* whip, %s?!? "
                  "{%s (%d)}", GET_NAME(vict), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
        case 4:
          sprintf(buf2, "%s's whip didn't agree with %s. {%s (%d)}",
                  GET_NAME(vict), GET_SEX(vict) == SEX_MALE ? "his" :
                  (GET_SEX(vict) == SEX_FEMALE ? "her" : "its"),
                  world[vict->in_room].name, world[vict->in_room].number);
          break;
      }
      break;
    default:
      if (ch == vict)
        sprintf(buf2, "%s died (cause uncertain). {%s (%d)}",
                GET_NAME(vict), world[vict->in_room].name,
                world[vict->in_room].number);
      else switch (number(0, 5)) {
        case 0:
          sprintf(buf2, "%s killed by %s. {%s (%d)}", GET_NAME(vict),
                  GET_NAME(ch), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
        case 1:
          sprintf(buf2, "%s wiped %s out of existence. {%s (%d)}",
                  GET_NAME(ch), GET_NAME(vict), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
        case 2:
          sprintf(buf2, "%s's life terminated by %s. {%s (%d)}",
                  GET_NAME(vict), GET_NAME(ch), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
        case 3:
          sprintf(buf2, "%s flatlined %s. {%s (%d)}",
                  GET_NAME(ch), GET_NAME(vict), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
        case 4:
          sprintf(buf2, "%s transformed %s into a corpse. {%s (%d)}",
                  GET_NAME(ch), GET_NAME(vict), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
        case 5:
          sprintf(buf2, "%s got geeked by %s. {%s (%d)}",
                  GET_NAME(vict), GET_NAME(ch), world[vict->in_room].name,
                  world[vict->in_room].number);
          break;
      }
      break;
  }

  mudlog(buf2, vict, LOG_DEATHLOG, TRUE);
}

#define IS_RANGED(eq)   (GET_OBJ_TYPE(eq) == ITEM_FIREWEAPON || \
                        (GET_OBJ_TYPE(eq) == ITEM_WEAPON && \
                        (GET_OBJ_VAL(eq, 3) == TYPE_SHURIKEN || \
                         GET_OBJ_VAL(eq, 3) == TYPE_THROWING_KNIFE || \
                         GET_OBJ_VAL(eq, 3) >= TYPE_PISTOL)))

#define RANGE_OK(ch) ((GET_WIELDED(ch, 0) && GET_EQ(ch, WEAR_WIELD) && \
                      IS_RANGED(GET_EQ(ch, WEAR_WIELD))) || (GET_WIELDED(ch, 1) && \
                      GET_EQ(ch, WEAR_HOLD) && IS_RANGED(GET_EQ(ch, WEAR_HOLD))))

#define IS_BURST(eq)    (GET_OBJ_TYPE(eq) == ITEM_WEAPON && \
                        (GET_OBJ_VAL(eq, 3) == TYPE_MACHINE_GUN || \
                        (GET_OBJ_VAL(eq, 3) == TYPE_RIFLE && \
                         GET_OBJ_VAL(eq, 4) == SKILL_ASSAULT_RIFLES)))

// return 1 if victim died, 0 otherwise
int damage(struct char_data *ch, struct char_data *victim, int dam, int attacktype,
           bool is_physical)
{
  char rbuf[MAX_STRING_LENGTH];
  int exp, damage_total;
  bool total_miss = FALSE;
  struct obj_data *bio;
  int counter;
  ACMD(do_disconnect);
  ACMD(do_return);

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

  sprintf(rbuf,"Damage: ");
  if (((!IS_NPC(ch) && GET_LEVEL(ch) >= LVL_LEGEND &&
      !access_level(ch, LVL_PRESIDENT)) || (IS_NPC(ch) && ch->master &&
      AFF_FLAGGED(ch, AFF_CHARM) && !IS_NPC(ch->master) &&
      GET_LEVEL(ch->master) >= LVL_LEGEND &&
      !access_level(ch->master, LVL_PRESIDENT))) &&
      IS_NPC(victim) && dam > 0 && !from_ip_zone(GET_MOB_VNUM(victim)))
    {
      dam = -1;
      buf_mod(rbuf,"Invalid",dam);
    }

  /* shopkeeper protection */
  if (!ok_damage_shopkeeper(ch, victim))
  {
      dam = -1;
      buf_mod(rbuf,"Keeper",dam);
  }
  if(IS_NPC(victim) && MOB_FLAGGED(victim,MOB_NOKILL))
  {
	  dam =-1;
	  buf_mod(rbuf,"Nokill",dam);
	  if (ch->in_room == victim->in_room)
	  {
	    do_action(victim, GET_NAME(ch), cmd_slp, 0);
	    perform_tell(victim, ch, "Well, get your affairs in order, you will be dead by dawn.");
	    GET_MENTAL(ch) = 1;
	  }

  }

  if (victim != ch) {
    if (GET_POS(ch) > POS_STUNNED && attacktype < TYPE_SUFFERING) {
      if (!FIGHTING(ch))
        set_fighting(ch, victim);

      if (!IS_NPC(ch) && IS_NPC(victim) && victim->master && !number(0, 10) &&
          IS_AFFECTED(victim, AFF_CHARM) && (victim->master->in_room == ch->in_room) &&
          !(ch->master && ch->master == victim->master)) {
        if (FIGHTING(ch))
          stop_fighting(ch);
        set_fighting(ch, victim->master);
        if (!FIGHTING(victim->master))
          set_fighting(victim->master, ch);
        return 0;
      }
    }
    if (GET_POS(victim) > POS_STUNNED && !FIGHTING(victim)) {
      if (victim->in_room == ch->in_room)
        set_fighting(victim, ch);
      if (MOB_FLAGGED(victim, MOB_MEMORY) && !IS_NPC(ch) &&
          (GET_LEVEL(ch) < LVL_LEGEND))
        remember(victim, ch);
    }
  }
  if (victim->master && victim->master == ch)
    stop_follower(victim);

  if (IS_AFFECTED(ch, AFF_HIDE))
    appear(ch);

  /* stop sneaking if it's the case */
  if (IS_AFFECTED(ch, AFF_SNEAK))
    REMOVE_BIT(AFF_FLAGS(ch), AFF_SNEAK);

  if (PLR_FLAGGED(victim, PLR_MATRIX)) {
    do_disconnect(victim, "", 0, SCMD_MORTED);
    WAIT_STATE(victim, PULSE_VIOLENCE);
  }
  if (PLR_FLAGGED(victim, PLR_PROJECT) && ch != victim) {
    do_return(victim, "", 0, 0);
    WAIT_STATE(victim, PULSE_VIOLENCE);
  }
  /* Firgure out how to do WANTED flag*/

  if (!pk_allowed && ch != victim)
  {
    check_killer(ch, victim);
  }
  if (PLR_FLAGGED(ch, PLR_KILLER) && !IS_NPC(victim))
  {
	dam = -1;
	buf_mod(rbuf,"No-PK",dam);
  }
  if (attacktype == TYPE_EXPLOSION && (IS_ASTRAL(victim) || MOB_FLAGGED(victim, MOB_IMMEXPLODE)))
    {
      dam = -1;
      buf_mod(rbuf,"ImmExplode",dam);
    }

  if (dam == -1)
    {
      total_miss = TRUE;
    }

  if (ch != victim && !IS_NPC(victim) && !(victim->desc)) {
    if (!FIGHTING(victim)) {
      act("$n is rescued by divine forces.", FALSE, victim, 0, 0, TO_ROOM);
      GET_WAS_IN(victim) = victim->in_room;
      GET_PHYSICAL(victim) = MAX(100, GET_PHYSICAL(victim) -
                                (int)(GET_MAX_PHYSICAL(victim) / 2));
      GET_MENTAL(victim) = MAX(100, GET_MENTAL(victim) -
                               (int)(GET_MAX_MENTAL(victim) / 2));
      char_from_room(victim);
      char_to_room(victim, 0);
    }
    return 0;
  }

  if (is_physical && dam >= 3)
    for (bio = ch->bioware; bio; bio = bio->next_content)
      if (GET_OBJ_VAL(bio, 2) == 0) {
        dam--;
        break;
      }

  if (is_physical)
    GET_PHYSICAL(victim) -= MAX(dam * 100, 0);
  else if (((int)(GET_MENTAL(victim) / 100) - dam) < 0) {
    int physdam = dam - (int)(GET_MENTAL(victim) / 100);
    GET_MENTAL(victim) = 0;
    GET_PHYSICAL(victim) -= MAX(physdam * 100, 0);
  } else GET_MENTAL(victim) -= MAX(dam * 100, 0);

  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 > 0 && ch != victim && attacktype < TYPE_SUFFERING) {
    if (!IS_WEAPON(attacktype)) {
      skill_message(dam, ch, victim, attacktype);
    } else {
      if (GET_POS(victim) == POS_DEAD || total_miss) {
        if (!skill_message(dam, ch, victim, attacktype))
          dam_message(dam, ch, victim, attacktype);
      } else
        dam_message(dam, ch, victim, attacktype);
    }
  }

  if ((ch->in_room != victim->in_room) && total_miss && attacktype < TYPE_SUFFERING &&
      GET_EQ(ch, WEAR_WIELD) && GET_OBJ_VAL(GET_EQ(ch, WEAR_WIELD), 3) >= TYPE_PISTOL)
    weapon_scatter(ch, victim, GET_EQ(ch, WEAR_WIELD));

  if (victim->bioware && GET_POS(victim) > POS_STUNNED && dam > 0 && ch != victim)
    check_adrenaline(victim, 1);

  if (ch != victim && dam > 0 && attacktype >= TYPE_HIT)
    damage_equip(ch, victim, dam, attacktype);

  /* 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);
      if (!IS_NPC(victim))
        docwagon(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 and will slowly die, if not "
                   "aided.\r\n", victim);
      if (!IS_NPC(victim))
        docwagon(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:
      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 > ((int)(GET_MAX_PHYSICAL(victim) / 100) >> 2))
        act("^RThat really did HURT!^n", FALSE, victim, 0, 0, TO_CHAR);

      if (GET_PHYSICAL(victim) < (GET_MAX_PHYSICAL(victim) >> 2))
        send_to_char(victim, "%sYou wish that your wounds would stop "
                     "^RBLEEDING^n so much!%s\r\n", CCRED(victim, C_SPR),
                     CCNRM(victim, C_SPR));

      if (MOB_FLAGGED(victim, MOB_WIMPY) && ch != victim &&
          GET_PHYSICAL(victim) < (GET_MAX_PHYSICAL(victim) >> 2))
        do_flee(victim, "", 0, 0);
      else if (IS_NPC(victim) && ch->in_room != victim->in_room && !RANGE_OK(victim)) {
        do_flee(victim, "", 0, 0);
        stop_fighting(victim);
      }

      if (!IS_NPC(victim) && GET_WIMP_LEV(victim) && victim != ch &&
          (int)(GET_PHYSICAL(victim) / 100) < GET_WIMP_LEV(victim)) {
        send_to_char("You ^Ywimp^n out, and attempt to flee!\r\n", victim);
        do_flee(victim, "", 0, 0);
      }
      break;
  }

  if (GET_MENTAL(victim) < 100)
    if (FIGHTING(ch) == victim)
      stop_fighting(ch);

  if (!AWAKE(victim))
    if (FIGHTING(victim))
      stop_fighting(victim);

  if (GET_POS(victim) == POS_DEAD) {
    if (ch != victim && !IS_NPC(ch) && GET_QUEST(ch) && IS_NPC(victim))
      check_quest_kill(ch, victim);
    else if (ch != victim && AFF_FLAGGED(ch, AFF_GROUP) && ch->master &&
             !IS_NPC(ch->master) && GET_QUEST(ch->master) && IS_NPC(victim))
      check_quest_kill(ch->master, victim);
    if ((IS_NPC(victim) || victim->desc) && ch != victim &&
        attacktype != TYPE_EXPLOSION) {
      if (IS_AFFECTED(ch, AFF_GROUP))
        group_gain(ch, victim);
      else {
        exp = calc_karma(ch, victim);

        exp = gain_exp(ch, exp);
        send_to_char(ch, "You receive %0.2f karma.\r\n", ((float)exp / 100));
        change_alignment(ch, victim);
      }
    }
    if (!IS_NPC(victim)) {
      gen_death_msg(ch, victim, attacktype);
      if (MOB_FLAGGED(ch, MOB_MEMORY) && !IS_NPC(victim))
        forget(ch, victim);
    }
    die(victim);
    return 1;
  }
  return 0;
}

bool has_ammo(struct char_data *ch, int pos)
{
  int i;
  bool found = FALSE;
  struct obj_data *obj;

  if (pos != WEAR_WIELD && pos != WEAR_HOLD)
    return FALSE;

  struct obj_data *wielded = GET_EQ(ch, pos);

  if (wielded && GET_OBJ_TYPE(wielded) == ITEM_FIREWEAPON) {
    for (i = 0; i < NUM_WEARS; i++)
      if (GET_EQ(ch, i) && GET_OBJ_TYPE(GET_EQ(ch, i)) == ITEM_QUIVER)
        for (obj = GET_EQ(ch, i)->contains; obj; obj = GET_EQ(ch, i)->next_content)
          if (GET_OBJ_TYPE(obj) == ITEM_MISSILE && GET_OBJ_VAL(obj, 0) == GET_OBJ_VAL(wielded, 5)) {
            GET_OBJ_VAL(GET_EQ(ch, i), 2)--;
            extract_obj(obj);
            found = TRUE;
          }
    if (found)
      return TRUE;
    else {
      if (IS_NPC(ch)) {
        switch_weapons(ch, pos);
        return TRUE;
      } else {
        send_to_char("You're out of arrows!\r\n", ch);
        stop_fighting(ch);
        return FALSE;
      }
    }
  }

  if (wielded && (GET_OBJ_VAL(wielded, 5) > 0)) {
    if (GET_OBJ_VAL(wielded, 6) > 0) {
      GET_OBJ_VAL(wielded, 6)--;
#if 0
      if (GET_OBJ_VAL(wielded, 6) == GET_OBJ_VAL(wielded, 5)) // just reloaded
        return FALSE;
      GET_INIT_ROLL(ch)--;
#endif
      return TRUE;
    } else
      // make the mob do something (intelligent hopefully)
      if (IS_NPC(ch)) {
        if (!attempt_reload(ch, pos))
          switch_weapons(ch, pos);
        return TRUE;
      } else {
        send_to_char("*Click*\r\n", ch);
        return FALSE;
      }
  } else return TRUE;
}

int check_smartlink(struct char_data *ch, struct obj_data *weapon)
{
  struct obj_data *obj, *access = NULL;
  int i, mod = 0;

  // are they wielding two weapons?
  if (GET_EQ(ch, WEAR_WIELD) && GET_EQ(ch, WEAR_HOLD) &&
      CAN_WEAR(GET_EQ(ch, WEAR_HOLD), ITEM_WEAR_WIELD))
    return 0;

  for (i = 7; !mod && i < 10; i++)
    {
      if (GET_OBJ_VAL(weapon, i) > 0
	  && real_object(GET_OBJ_VAL(weapon, i)) > 0
	  && (access = &obj_proto[real_object(GET_OBJ_VAL(weapon, i))])
	  && GET_OBJ_VAL(access, 1) == 1)
	{
	  for (obj = ch->cyberware; !mod && obj; obj = obj->next_content)
	    if (GET_OBJ_VAL(obj, 2) == 22)
	      mod = 2;
	  access = NULL;
	}
    }

  if (mod < 1 && AFF_FLAGGED(ch, AFF_LASER_SIGHT))
    mod = 1;

  return mod;
}

int check_recoil(struct char_data *ch, struct obj_data *gun)
{
  struct obj_data *obj;
  int i, rnum, comp = 0;

  if (!gun || GET_OBJ_TYPE(gun) != ITEM_WEAPON)
    return 0;

  for (i = 7; i < 10; i++) {
    obj = NULL;

    if (GET_OBJ_VAL(gun, i) > 0 &&
        (rnum = real_object(GET_OBJ_VAL(gun, i))) > -1 &&
        (obj = &obj_proto[rnum]) && GET_OBJ_TYPE(obj) == ITEM_GUN_ACCESSORY) {
      if (GET_OBJ_VAL(obj, 1) == 3)
        comp += 0 - GET_OBJ_VAL(obj, 2);
      else if (GET_OBJ_VAL(obj, 1) == 4)
        comp++;
     }
  }

  return comp;
}

void astral_fight(struct char_data *ch, struct char_data *vict)
{
  int w_type, dam, power, attack_success, newskill, skill_total, base_target, i;
  struct obj_data *wielded, *obj, *o = NULL;
  struct char_data *temp;
  bool focus = FALSE, is_physical;

  wielded = ch->equipment[WEAR_WIELD];

  if (ch->in_room != vict->in_room) {
    stop_fighting(ch);
    if (FIGHTING(vict) == ch)
      stop_fighting(vict);
    return;
  }

  if ((IS_PROJECT(ch) || PLR_FLAGGED(ch, PLR_PERCEIVE))
      && wielded
      && GET_OBJ_TYPE(wielded) == ITEM_WEAPON
      && GET_OBJ_VAL(wielded, 3) < TYPE_TASER
      && GET_OBJ_VAL(wielded, 7)
      && GET_OBJ_VAL(wielded, 8)
      && GET_OBJ_VAL(wielded, 9) == GET_IDNUM(ch))
    focus = TRUE;
  else if (wielded) {
    stop_fighting(ch);
    return;
  }

  if (IS_PROJECT(ch) && (ch != vict) && PLR_FLAGGED(vict, PLR_PERCEIVE) &&
      !PLR_FLAGGED(vict, PLR_KILLER) && !PLR_FLAGGED(vict,PLR_WANTED) && !PLR_FLAGGED(vict, PLR_THIEF) &&
      (!PRF_FLAGGED(ch, PRF_PKER) || !PRF_FLAGGED(vict, PRF_PKER)) &&
      !PLR_FLAGGED(ch->desc->original, PLR_KILLER)) {
    SET_BIT(PLR_FLAGS(ch->desc->original), PLR_KILLER);
    sprintf(buf, "PC Killer bit set on %s (astral) for initiating attack on %s at %s.",
            GET_NAME(ch->desc->original), GET_NAME(vict), world[vict->in_room].name);
    mudlog(buf, ch, LOG_MISCLOG, TRUE);
    send_to_char("If you want to be a PLAYER KILLER, so be it...\r\n", ch);
  }

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

  if (wielded)
    w_type = GET_OBJ_VAL(wielded, 3);
  else {
    if (ch->mob_specials.attack_type != 0)
      w_type = ch->mob_specials.attack_type;
    else w_type = TYPE_HIT;
  }

  if (((w_type == TYPE_HIT) || (w_type == TYPE_BLUDGEON) || (w_type == TYPE_PUNCH) ||
     (w_type == TYPE_TASER) || (w_type == TYPE_CRUSH) || (w_type == TYPE_POUND)) &&
     (GET_MENTAL(vict) >= 100))
    is_physical = FALSE;
  else is_physical = TRUE;

  base_target = 4 + modify_target(ch);

  if (!AWAKE(vict))
    base_target -= 2;

  if ((w_type != TYPE_HIT) && wielded) {
     power = (GET_OBJ_VAL(wielded, 0) ? GET_OBJ_VAL(wielded, 0) : GET_STR(ch)) +
             GET_OBJ_VAL(wielded, 2);
     if (focus)
       power += GET_OBJ_VAL(wielded, 8);
     power -= GET_IMPACT(vict);
     dam = MODERATE;
     if (IS_SPIRIT(vict))
       skill_total = GET_WIL(ch);
     else if (GET_SKILL(ch, GET_OBJ_VAL(wielded, 4)) < 1) {
       newskill = return_general(GET_OBJ_VAL(wielded, 4));
       if (GET_SKILL(ch, newskill) < 1)
         skill_total = reverse_web(ch, newskill, base_target);
       else skill_total = GET_SKILL(ch, newskill);
     } else skill_total = GET_SKILL(ch, GET_OBJ_VAL(wielded, 4));
  } else {
     power = GET_STR(ch) - GET_IMPACT(vict);
     if (IS_PROJECT(ch) || PLR_FLAGGED(ch, PLR_PERCEIVE))
       dam = LIGHT;
     else dam = MODERATE;
     if (IS_SPIRIT(vict))
       skill_total = GET_WIL(ch);
     else if (GET_SKILL(ch, SKILL_UNARMED_COMBAT) < 1) {
       if (GET_SKILL(ch, SKILL_SORCERY) < 1) {
         newskill = SKILL_UNARMED_COMBAT;
         skill_total = reverse_web(ch, newskill, base_target);
       } else skill_total = GET_SKILL(ch, SKILL_SORCERY);
     } else skill_total = MAX(GET_SKILL(ch, SKILL_UNARMED_COMBAT),
         GET_SKILL(ch, SKILL_SORCERY));
  }

  skill_total += (int)(GET_ASTRAL(ch) / 2);

  if (power < 2) {
    skill_total = MAX(0, skill_total - (2 - power));
    power = 2;
  }

  if (AWAKE(vict))
    attack_success = resisted_test(skill_total, base_target,
                                   (int)(GET_ASTRAL(vict) / 2), power + modify_target(vict));
  else attack_success = success_test(skill_total, base_target);

  if (attack_success < 1) {
    if (!AFF_FLAGGED(ch, AFF_COUNTER_ATT)) {
      if ((GET_ASTRAL(vict) > 0) && (attack_success < 0)
	&& FIGHTING(vict) == ch && GET_POS(vict) <= POS_SLEEPING) {
        send_to_char(ch, "%s counters your attack!\r\n", GET_NAME(vict));
        send_to_char(vict, "You counter %s's attack!\r\n", GET_NAME(ch));
        SET_BIT(AFF_FLAGS(vict), AFF_COUNTER_ATT);
        astral_fight(vict, ch);
      }
      return;
    } else {
      REMOVE_BIT(AFF_FLAGS(ch), AFF_COUNTER_ATT);
      return;
    }
  } else if (AFF_FLAGGED(ch, AFF_COUNTER_ATT))
    REMOVE_BIT(AFF_FLAGS(ch), AFF_COUNTER_ATT);

  attack_success -= success_test(GET_BOD(vict), power);

  dam = convert_damage(stage(attack_success, dam));

  if (IS_PROJECT(ch) && (PLR_FLAGGED(ch->desc->original, PLR_KILLER) ||
      PLR_FLAGGED(ch->desc->original, PLR_THIEF)))
    dam = 0;

  damage(ch, vict, dam, w_type, is_physical);
  if (IS_PROJECT(vict) && dam > 0)
    damage(vict->desc->original, vict->desc->original, dam, 0, is_physical);
}

void remove_throwing(struct char_data *ch)
{
  struct obj_data *obj = NULL;
  int i, pos, type;

  for (pos = WEAR_WIELD; pos <= WEAR_HOLD; pos++)
    if (GET_EQ(ch, pos)) {
      type = GET_OBJ_VAL(GET_EQ(ch, pos), 3);
      if (type == TYPE_SHURIKEN || type == TYPE_THROWING_KNIFE) {
        extract_obj(unequip_char(ch, pos));
        for (i = 0; i < NUM_WEARS; i++)
          if (GET_EQ(ch, i) && GET_OBJ_TYPE(GET_EQ(ch, i)) == ITEM_QUIVER)
            for (obj = GET_EQ(ch, i)->contains; obj; obj = obj->next_content)
              if (GET_OBJ_TYPE(obj) == ITEM_WEAPON && GET_OBJ_VAL(obj, 3) == type) {
                obj_from_obj(obj);
                equip_char(ch, obj, pos);
                return;
              }
        return;
      }
    }
}

void hit(struct char_data * ch, struct char_data * victim, int type)
{
  char rbuf[MAX_STRING_LENGTH];
  static struct obj_data *wielded;
  static bool is_physical;
  static bool melee;
  static int base_target, power, w_type, damage_total;
  struct obj_data *obj;
  struct char_data *vict;

  int attack_success = 0, attack_resist=0, skill_total = 1;
  int newskill, successes = 0;
  int room, nextroom, dir, distance, pos, range, dead = 0, hits = 0;
  int vict_found, recoil=0, burst=0, recoil_comp=0;
  int tsleep=0, tcansee=0, toffhand=0, ttwoweap=0, tsmartlink=0;
  int tdistance=0, modtarget=0, tallergic=0, tsight=0;

  if (IS_AFFECTED(ch, AFF_PETRIFY))
    return;

  // determine if attack is in Matrix
  if (IS_SET(world[ch->in_room].room_flags, ROOM_MATRIX)) {
    matrix_fight(ch, victim);
    return;
  }

  if ((IS_ASTRAL(victim) && !IS_ASTRAL(ch) && !IS_DUAL(ch) &&
      !PLR_FLAGGED(ch, PLR_PERCEIVE)) || (IS_ASTRAL(ch) &&
      !IS_ASTRAL(victim) && !IS_DUAL(victim) && !PLR_FLAGGED(victim, PLR_PERCEIVE)))
    return;

  if ((IS_ASTRAL(victim) && CAN_SEE(ch, victim)) || (IS_ASTRAL(ch) &&
      (IS_DUAL(victim) || PLR_FLAGGED(victim, PLR_PERCEIVE)))) {
    astral_fight(ch, victim);
    return;
  }

  for (pos = WEAR_WIELD; !dead && pos <= WEAR_HOLD; pos++)
    {
    if (GET_WIELDED(ch, pos - WEAR_WIELD))
      {
	if (!(wielded = GET_EQ(ch, pos)))
	  GET_WIELDED(ch, pos - WEAR_WIELD) = 0;
      }
    else
      wielded = NULL;

    if (pos == WEAR_HOLD && (type == TYPE_MELEE || !wielded))
      continue;
    else if (pos == WEAR_WIELD && !wielded && GET_WIELDED(ch, 1) &&
             GET_EQ(ch, WEAR_HOLD))
      continue;

    // before you do anything, check to see if they have ammo
    if (wielded && !has_ammo(ch, pos))
      {
	if (!FIGHTING(victim)
	    && ch->in_room == victim->in_room
	    && GET_POS(victim) > POS_STUNNED)
	  set_fighting(victim, ch);
	hits++;
	continue;
      }

    if (wielded && GET_OBJ_TYPE(wielded) == ITEM_WEAPON &&
        GET_OBJ_VAL(wielded, 3) >= TYPE_HAND_GRENADE &&
        GET_OBJ_VAL(wielded, 3) <= TYPE_ROCKET && ch->in_room == victim->in_room)
      continue;

    if (ch->in_room != victim->in_room && !RANGE_OK(ch))
      {
	stop_fighting(ch);
	return;
      }

    // determine attack type
    if (wielded && (GET_OBJ_TYPE(wielded) == ITEM_WEAPON ||
        GET_OBJ_TYPE(wielded) == ITEM_FIREWEAPON))
      w_type = GET_OBJ_VAL(wielded, 3);
    else if (IS_NPC(ch) && ch->mob_specials.attack_type != 0)
      w_type = ch->mob_specials.attack_type;
    else
      w_type = TYPE_HIT;

    /* determine if this attack does mental or physical damage */
    if ((w_type == TYPE_HIT || w_type == TYPE_BLUDGEON || w_type == TYPE_PUNCH ||
        w_type == TYPE_TASER || w_type == TYPE_CRUSH || w_type == TYPE_POUND) &&
        GET_MENTAL(victim) >= 100)
      is_physical = FALSE;
    else
      is_physical = TRUE;

    /* determine if attacker is using melee or fire combat */
    if (!IS_GUN(w_type))
      melee = TRUE;
    else
      melee = FALSE;

    if (type == TYPE_MELEE)
      {
	if (!melee)
	  {
	    wielded = GET_EQ(victim, WEAR_WIELD);
	    if (wielded && (GET_OBJ_TYPE(wielded) == ITEM_WEAPON ||
			    GET_OBJ_TYPE(wielded) == ITEM_FIREWEAPON))
	      w_type = GET_OBJ_VAL(wielded, 3);
	    else
	      {
		if (IS_NPC(victim) && (victim->mob_specials.attack_type != 0))
		  w_type = victim->mob_specials.attack_type;
		else w_type = TYPE_HIT;
	      }
	    damage(victim, ch, -1, w_type, TRUE);
	    return;
	  }
	if ( GET_POS(victim) <= POS_SLEEPING )
	{
	  send_to_char(victim, "%s counters your attack!\r\n", GET_NAME(ch));
	  send_to_char(ch, "You counter %s's attack!\r\n", GET_NAME(victim));
	  SET_BIT(AFF_FLAGS(ch), AFF_COUNTER_ATT);
	}
      }

    if (wielded && IS_BURST(wielded))
      {
	if (GET_OBJ_VAL(wielded, 6) >= 2)
	  {
	    burst = 3;
	    GET_OBJ_VAL(wielded, 6) -= 2;
	  }
	else if (GET_OBJ_VAL(wielded, 6) == 1)
	  {
	    burst = 2;
	    GET_OBJ_VAL(wielded, 6)--;
	  }
	else
	  burst = 0;
      }

    recoil_comp = check_recoil(ch, wielded);
    recoil = MAX(0, burst - recoil_comp);


   /*  lower target number for victim being asleep */
    if (!AWAKE(victim))
      tsleep = -2;

    if (!CAN_SEE(ch, victim))
      tcansee = 4;

    if (pos == WEAR_HOLD)
      toffhand = 1;

    // wielding two weapons
    if (GET_WIELDED(ch, 0) && GET_WIELDED(ch, 1))
      ttwoweap = 1;
    else if (wielded && GET_OBJ_TYPE(wielded) == ITEM_WEAPON &&
             GET_OBJ_VAL(wielded, 3) >= TYPE_PISTOL)
      tsmartlink = 0 - check_smartlink(ch, wielded);

    if (ch->in_room != victim->in_room)
      {
	vict_found = FALSE;
	if (wielded && IS_RANGED(wielded))
	  {
	    range = MIN(find_sight(ch), find_weapon_range(ch, GET_EQ(ch, pos)));
	    for (dir = 0; dir < (NUM_OF_DIRS - 1) && !vict_found; dir++)
	      {
		room = ch->in_room;
		if (CAN_GO2(room, dir))
		  nextroom = EXIT2(room, dir)->to_room;
		else
		  nextroom = NOWHERE;
		for (distance = 1; (nextroom != NOWHERE) && (distance <= range) &&
		       !vict_found; distance++)
		  {
		    for (vict = world[nextroom].people; vict;
			 vict = vict->next_in_room)
		      if (vict == victim)
			{
			  tdistance += 2 * distance;
			  // Too powerful with these.
#if 0
			  if (AFF_FLAGGED(ch, AFF_VISION_MAG_3))
			    tsight = 0 - MIN(6, tdistance);
			  else if (AFF_FLAGGED(ch, AFF_VISION_MAG_2))
			    tsight = 0 - MIN(4, tdistance);
			  else if (AFF_FLAGGED(ch, AFF_VISION_MAG_1))
			    tsight = 0 - MIN(2, tdistance);
#endif
			  vict_found = TRUE;
			  break;
			}
		    room = nextroom;
		    if (CAN_GO2(room, dir))
		      nextroom = EXIT2(room, dir)->to_room;
		    else
		      nextroom = NOWHERE;
		  }
	      }
	  }
	if (!vict_found)
	  continue;
      }

    if (wielded && GET_OBJ_VAL(wielded, 3) < TYPE_HAND_GRENADE && !IS_NPC(ch) &&
        !GET_EQ(ch, WEAR_HANDS) && is_allergic(ch, GET_OBJ_RENT(wielded)) == 2)
      tallergic = 2;

    if (1)
      {
	sprintf( rbuf,
		 "Targ: (b/r %d-%d) ",
		 burst, recoil_comp );
	modtarget = modify_target_rbuf(ch, rbuf);
#if 0
	buf_mod( rbuf, "MT", modtarget );
#endif
	buf_mod( rbuf, "Recoil", recoil);
	buf_mod( rbuf, "Sleep", tsleep);
	buf_mod( rbuf, "Cansee", tcansee);
	buf_mod( rbuf, "Offhand", toffhand);
	buf_mod( rbuf, "2Weap", ttwoweap);
	buf_mod( rbuf, "Smart", tsmartlink);
	buf_mod( rbuf, "Distance", tdistance);
	buf_mod( rbuf, "Sight", tsight);
	buf_mod( rbuf, "Allergic", tallergic);

	// set the base target number and modify
	base_target = 4 + modtarget + recoil + tsleep + tcansee
	  + toffhand + ttwoweap + tsmartlink + tdistance + tsight
	  + tallergic;

	buf_roll( rbuf, "Total", base_target);
	act( rbuf, 1, ch, NULL, NULL, TO_ROLLS );
      }


    damage_total = 0;
    hits++;
    /* find power, staging, and initial damage */
    if (w_type != TYPE_HIT && w_type != TYPE_PUNCH && wielded)
      {
	power = (GET_OBJ_VAL(wielded, 0) ? GET_OBJ_VAL(wielded, 0) :
		 GET_STR(ch)) + GET_OBJ_VAL(wielded, 2) + burst;
	// this is how armor now works
	if (w_type >= TYPE_PISTOL)
	  power -= GET_BALLISTIC(victim);
	else
	  power -= GET_IMPACT(victim);
	damage_total = GET_OBJ_VAL(wielded, 1) + (burst == 3 ? 1 : 0);
	if (IS_SPIRIT(victim))
	  {
	    skill_total = GET_WIL(ch);
	  }
	else if (GET_SKILL(ch, GET_OBJ_VAL(wielded, 4)) < 1)
	  {
	    newskill = return_general(GET_OBJ_VAL(wielded, 4));
	    if (GET_SKILL(ch, newskill) < 1)
	      skill_total = reverse_web(ch, newskill, base_target);
	    else
	      skill_total = GET_SKILL(ch, newskill);
	  }
	else
	  {
	    skill_total = GET_SKILL(ch, GET_OBJ_VAL(wielded, 4));
	  }
	if (w_type < TYPE_TASER
	    && GET_OBJ_VAL(wielded, 7)
	    && GET_OBJ_VAL(wielded, 8)
	    && return_general(GET_OBJ_VAL(wielded, 4)) == SKILL_ARMED_COMBAT
	    && GET_OBJ_VAL(wielded, 9) == (IS_NPC(ch) ? -1 : GET_IDNUM(ch)))
	  skill_total += GET_OBJ_VAL(wielded, 8);
      }
    else
      { // fists
	power = GET_STR(ch) - GET_IMPACT(victim);
	if ((IS_NPC(ch) || GET_TRADITION(ch) == TRAD_ADEPT) &&
	    GET_SKILL(ch, SKILL_KILL_HANDS) > 0)
	  {
	    damage_total = GET_SKILL(ch, SKILL_KILL_HANDS);
	    is_physical = TRUE;
	  }
	else
	  {
	    for (obj = ch->cyberware;
		 obj && !damage_total;
		 obj = obj->next_content)

	      {
		switch (GET_OBJ_VAL(obj, 2))
		  {
		  case 19:
		    damage_total = LIGHT;
		    is_physical = TRUE;
		    w_type = TYPE_SLASH;
		    break;
		  case 21:
		    is_physical = TRUE;
		    w_type = TYPE_SLASH;
		    damage_total = MODERATE;
		    break;
		  case 29:
		    power += GET_OBJ_VAL(obj, 0);
		    damage_total = MODERATE;
		    is_physical = TRUE;
		    break;
		  }
	      }
	  }
      if (!damage_total)
        damage_total = MODERATE;

      if (IS_SPIRIT(victim))
	{
	  skill_total = GET_WIL(ch);
	}
      else if (GET_SKILL(ch, SKILL_UNARMED_COMBAT) < 1)
	{
	  newskill = SKILL_UNARMED_COMBAT;
	  skill_total = reverse_web(ch, newskill, base_target);
	}
      else
	{
	  skill_total = GET_SKILL(ch, SKILL_UNARMED_COMBAT);
	}
    }

    if (IS_SPIRIT(victim)
	&& wielded
	&& ((GET_OBJ_TYPE(wielded) == ITEM_WEAPON
	     && w_type >= TYPE_PISTOL)
	    || (GET_OBJ_TYPE(wielded) == ITEM_FIREWEAPON
		&& GET_OBJ_VAL(wielded, 5) == 1)))
      power -= (2 * GET_LEVEL(victim)) - MAX(GET_BALLISTIC(victim),
					     GET_IMPACT(victim));

    if (wielded
	&& GET_OBJ_VAL(wielded, 3) < TYPE_HAND_GRENADE
	&& !IS_NPC(victim)
	&& is_allergic(victim, GET_OBJ_RENT(wielded)) == 2)
      power += 2;

    // add offense (from combat pool) to dice pool for attacking--divided
    // by the number of folks you are attacking
    if (!IS_SPIRIT(victim))
      skill_total += MAX(0,GET_OFFENSE(ch)); // GET_NUM_FIGHTING(ch);

#if 0
    // I do this because it's silly that if you have enough armor to reduce
    // the power of a weapon below 2, that the armor no longer counts...
    // seems to me it still should, so I'm making it count at least a little
    if (power < 2)
      {
	skill_total = MAX(0, skill_total - (2 - power));
	power = 2;
      }
#endif

    // find # of attack successes, allow defense if victim is awake
    attack_success = success_test(skill_total, base_target);

    modtarget = modify_target(victim);
    if (AWAKE(victim))
      {
	attack_resist = success_test(GET_DEFENSE(victim), power + modtarget);
      }
    else
      {
	// Always hit !awake victims
	attack_success = MAX( attack_success, 1 );
      }

    sprintf(rbuf,"Fight: I %2d, Ski %d, Targ %d, Succ %d. %sDef %d, Targ %d+%d, Succ %d. L:%d",
	    GET_INIT_ROLL(ch),
	    skill_total, base_target, attack_success,
	    AWAKE(victim) ? "" : "!A ",
	    GET_DEFENSE(victim), power, modtarget, attack_resist,
	    damage_total);
    act(rbuf, FALSE, ch, NULL, NULL, TO_ROLLS);

    attack_success -= attack_resist;

    /* if there were no successes, fighter missed from lack of skill or dodge */
    /* send a -1 damage to indicate complete miss                    */
    if (ch->in_room != victim->in_room && attack_success < 1)
      {
	if (wielded && GET_WSPEC(wielded) != NULL)
	  {
	    if (!(GET_WSPEC(wielded) (ch, victim, wielded, -1)))
	      dead = damage(ch, victim, -1, w_type, is_physical);
	    else if (GET_POS(victim) == POS_DEAD || GET_POS(ch) == POS_DEAD)
	      dead = TRUE;
	  }
	else
	  dead = damage(ch, victim, -1, w_type, is_physical);
	remove_throwing(ch);
	continue;
      }
    else if (attack_success < 1)
      {
	if (!AFF_FLAGGED(ch, AFF_COUNTER_ATT))
	  {
	    if ((GET_DEFENSE(victim) > 0)
		&& melee
		&& FIGHTING(victim) == ch
		&& GET_POS(victim) == POS_FIGHTING )
	      {
		if ( type == TYPE_MELEE )
		  hit(victim, ch, TYPE_HIT);
		/*hit(victim, ch, TYPE_MELEE);*/
	      }
	    else if (wielded && GET_WSPEC(wielded) != NULL)
	      {
		if (!(GET_WSPEC(wielded) (ch, victim, wielded, -1)))
		  dead = damage(ch, victim, -1, w_type, is_physical);
		else if (GET_POS(victim) == POS_DEAD || GET_POS(ch) == POS_DEAD)
		  dead = TRUE;
	      }
	    else
	      {
		dead = damage(ch, victim, -1, w_type, is_physical);
	      }
	    remove_throwing(ch);
	    continue;
	  }
	else
	  {
	    if (wielded && GET_WSPEC(wielded) != NULL)
	      {
		if (!(GET_WSPEC(wielded) (ch, victim, wielded, -1)))
		  dead = damage(ch, victim, -1, w_type, is_physical);
		else if (GET_POS(victim) == POS_DEAD || GET_POS(ch) == POS_DEAD)
		  dead = TRUE;
	      }
	    else
	      dead = damage(ch, victim, -1, w_type, is_physical);
	    REMOVE_BIT(AFF_FLAGS(ch), AFF_COUNTER_ATT);
	    remove_throwing(ch);
	    continue;
	  }
      }
    else if (AFF_FLAGGED(ch, AFF_COUNTER_ATT))
      REMOVE_BIT(AFF_FLAGS(ch), AFF_COUNTER_ATT);

    int bod_success=0;

    // then subtract successes from body test of victim if they didn't dodge
    if (IS_SPIRIT(ch) && !IS_NPC(victim) && (GET_TRADITION(victim) == TRAD_HERMETIC ||
        GET_TRADITION(victim) == TRAD_SHAMANIC))
      bod_success = success_test(MAX(GET_BOD(victim), GET_SKILL(victim, SKILL_CONJURING)),
				     power);
    else
      bod_success = success_test(GET_BOD(victim), power);

    attack_success -= bod_success;
    int old_damage_total = damage_total;
    int staged_damage = stage(attack_success, damage_total);
    damage_total = convert_damage(staged_damage);

    sprintf(rbuf, "Fight: Bod %d, Pow %d, Suc %d.  %d(%d)->%d.  %d%c.",
	    GET_BOD(victim), power, bod_success,
	    old_damage_total, attack_success, staged_damage,
	    damage_total,
	    is_physical ? 'P' : 'M');
    act(rbuf, FALSE, ch, NULL, NULL, TO_ROLLS);

    if (wielded && GET_WSPEC(wielded) != NULL)
      {
	if (!(GET_WSPEC(wielded) (ch, victim, wielded, -1)))
	  dead = damage(ch, victim, damage_total, w_type, is_physical);
	else if (GET_POS(victim) == POS_DEAD || GET_POS(ch) == POS_DEAD)
	  dead = TRUE;
      }
    else
      dead = damage(ch, victim, damage_total, w_type, is_physical);
    remove_throwing(ch);
  }

  if (!hits)
    stop_fighting(ch);
}

int find_sight(struct char_data *ch)
{
  int sight;

  if (IS_PERSONA(ch))
    return 1;

  if ((!IS_NPC(ch) && access_level(ch, LVL_OWNER)) || AFF_FLAGGED(ch, AFF_VISION_MAG_3))
    sight = 4;
  else if (AFF_FLAGGED(ch, AFF_VISION_MAG_2))
    sight = 3;
  else if (AFF_FLAGGED(ch, AFF_VISION_MAG_1))
    sight = 2;
  else sight = 1;

  /* add more weather conditions here to affect scan */
  if (SECT(ch->in_room) != SECT_INSIDE && (IS_NPC(ch) || !access_level(ch, LVL_OWNER)))
    switch (weather_info.sky)
    {
      case SKY_RAINING:
        sight -= 1;
        break;
      case SKY_LIGHTNING:
        sight -= 2;
        break;
    }

  sight = MIN(4, MAX(1, sight));

  return sight;
}

int find_weapon_range(struct char_data *ch, struct obj_data *weapon)
{
  int temp;

  if ( weapon == NULL )
    return 0;

  if (GET_OBJ_TYPE(weapon) == ITEM_FIREWEAPON) {
    temp = MIN(MAX(1, ((int)(GET_STR(ch)/3))), 4);
    return temp;
  }

  switch(GET_OBJ_VAL(weapon, 3)) {
    case TYPE_SHURIKEN: case TYPE_THROWING_KNIFE: case TYPE_HAND_GRENADE:
    case TYPE_PISTOL:   case TYPE_SHOTGUN:        case TYPE_BLAST:
      return 1;
    case TYPE_GRENADE_LAUNCHER: case TYPE_BIFURCATE:
      return 2;
    case TYPE_RIFLE: case TYPE_MACHINE_GUN:
      return 3;
    case TYPE_CANNON: case TYPE_ROCKET:
      return 4;
    default:
      return 0;
  }
}

void ranged_response(struct char_data *ch, struct char_data *vict)
{
  int range, sight, distance, dir, room, nextroom = NOWHERE, found = 0;
  struct char_data *temp;

  if (!vict || !IS_NPC(vict) || ch->in_room == vict->in_room ||
      GET_POS(vict) <= POS_STUNNED)
    return;

  if (GET_POS(vict) < POS_FIGHTING)
    GET_POS(vict) = POS_STANDING;
  if (IS_SET(MOB_FLAGS(vict), MOB_WIMPY))
    do_flee(vict, "", 0, 0);
  else if (RANGE_OK(vict) && !FIGHTING(vict)) {
    sight = find_sight(vict);
    range = find_weapon_range(vict, GET_EQ(vict, WEAR_WIELD));
    for (dir = 0; dir < (NUM_OF_DIRS - 1) && !found; dir++) {
      room = vict->in_room;
      if (CAN_GO2(room, dir))
        nextroom = EXIT2(room, dir)->to_room;
      else nextroom = NOWHERE;
      for (distance = 1; !found && ((nextroom != NOWHERE) && (distance <= 4)); distance++) {
        for (temp = world[nextroom].people; !found && temp; temp = temp->next_in_room)
          if (temp == ch && (distance > range || distance > sight)) {
            act("$n runs after $s distant attacker.", TRUE, vict, 0, 0, TO_ROOM);
            act("You charge after $N.", FALSE, vict, 0, ch, TO_CHAR);
            char_from_room(vict);
            char_to_room(vict, ch->in_room);
            set_fighting(vict, ch);
            found = 1;
            act("$n arrives in a rush of fury, immediately attacking $N!",
                TRUE, vict, 0, ch, TO_NOTVICT);
            act("$n arrives in a rush of fury, rushing straight towards you!",
                FALSE, vict, 0, ch, TO_VICT);
          }
        room = nextroom;
        if (CAN_GO2(room, dir))
          nextroom = EXIT2(room, dir)->to_room;
        else nextroom = NOWHERE;
      }
    }
    if (!found)
      set_fighting(vict, ch);
  } else if (!FIGHTING(vict)) {
    act("$n runs after $s distant attacker.", TRUE, vict, 0, 0, TO_ROOM);
    act("You charge after $N.", FALSE, vict, 0, ch, TO_CHAR);
    char_from_room(vict);
    char_to_room(vict, ch->in_room);
    set_fighting(vict, ch);
    act("$n arrives in a rush of fury, immediately attacking $N!", TRUE, vict, 0, ch, TO_NOTVICT);
    act("$n arrives in a rush of fury, rushing straight towards you!", FALSE, vict, 0, ch, TO_VICT);
  }
  return;
}

void explode(struct char_data *ch, struct obj_data *weapon, int room)
{
  int damage_total, exp, i, power, level;
  struct char_data *victim, *next_vict;
  struct obj_data *obj, *next;

  power = GET_OBJ_VAL(weapon, 0);
  level = GET_OBJ_VAL(weapon, 1);

  extract_obj(weapon);

  if (world[room].people) {
    act("The room is lit by an explosion!", FALSE, world[room].people, 0, 0, TO_ROOM);
    act("The room is lit by an explosion!", FALSE, world[room].people, 0, 0, TO_CHAR);
  }

  for (obj = world[room].contents; obj; obj = next) {
    next = obj->next_content;
    damage_obj(NULL, obj, level * 2 + (int)(power / 6), DAMOBJ_EXPLODE);
  }

  for (victim = world[room].people; victim; victim = next_vict) {
    next_vict = victim->next_in_room;
    if (IS_ASTRAL(victim))
      continue;

    act("You are hit by the flames!", FALSE, victim, 0, 0, TO_CHAR);

    for (obj = victim->carrying; obj; obj = next) {
      next = obj->next_content;
      if (number(1, 100) < 50)
        damage_obj(NULL, obj, level * 2 + (int)(power / 6), DAMOBJ_EXPLODE);
    }

    for (i = 0; i < (NUM_WEARS - 1); i++)
      if (GET_EQ(victim, i) && number(1, 100) < 100)
        damage_obj(NULL, GET_EQ(victim, i), level * 2 + (int)(power / 6),
                   DAMOBJ_EXPLODE);

    if (IS_NPC(victim) && !FIGHTING(victim)) {
      GET_DEFENSE(victim) = GET_COMBAT(victim);
      GET_OFFENSE(victim) = 0;
    }
    damage_total =
           convert_damage(stage((number(1, 3) - success_test(GET_BOD(victim) +
                          GET_DEFENSE(victim), MAX(2, (power -
                          (int)(GET_IMPACT(victim) / 2))) + modify_target(victim))),
                          level));
    if (!ch)
      damage(victim, victim, damage_total, TYPE_EXPLOSION, PHYSICAL);
    else damage(ch, victim, damage_total, TYPE_EXPLOSION, PHYSICAL);
  }

  for (i = 0; i < (NUM_OF_DIRS - 1); i++)
    if (world[room].dir_option[i] && IS_SET(world[room].dir_option[i]->exit_info, EX_CLOSED))
      damage_door(NULL, room, i, level * 2 + (int)(power / 6), DAMOBJ_EXPLODE);
}

void target_explode(struct char_data *ch, struct obj_data *weapon, int room, int mode)
{
  int damage_total, exp = 0, i;
  struct char_data *victim, *next_vict;
  struct obj_data *obj, *next;

  sprintf(buf, "The room is lit by a%s explosion!",
          (GET_OBJ_VAL(weapon, 3) == TYPE_ROCKET ? " massive" : "n"));

  if (world[room].people) {
    act(buf, FALSE, world[room].people, 0, 0, TO_ROOM);
    act(buf, FALSE, world[room].people, 0, 0, TO_CHAR);
  }

  for (obj = world[room].contents; obj; obj = next) {
    next = obj->next_content;
    damage_obj(NULL, obj, GET_OBJ_VAL(weapon, 1) * 2 +
               (int)(GET_OBJ_VAL(weapon, 0) / 6), DAMOBJ_EXPLODE);
  }

  for (victim = world[room].people; victim; victim = next_vict) {
    next_vict = victim->next_in_room;
    if (IS_ASTRAL(victim))
      continue;

    act("You are hit by the flames!", FALSE, victim, 0, 0, TO_CHAR);

    for (obj = victim->carrying; obj; obj = next) {
      next = obj->next_content;
      if (number(1, 100) < 50)
        damage_obj(NULL, obj, GET_OBJ_VAL(weapon, 1) * 2 +
                   (int)(GET_OBJ_VAL(weapon, 0) / 6), DAMOBJ_EXPLODE);
    }

    for (i = 0; i < (NUM_WEARS - 1); i++)
      if (GET_EQ(victim, i) && number(1, 100) < 100)
        damage_obj(NULL, GET_EQ(victim, i), GET_OBJ_VAL(weapon, 1) * 2 +
                   (int)(GET_OBJ_VAL(weapon, 0) / 6), DAMOBJ_EXPLODE);

    if (IS_NPC(victim) && !FIGHTING(victim)) {
      GET_DEFENSE(victim) = GET_COMBAT(victim);
      GET_OFFENSE(victim) = 0;
    }
    if (!mode) {
      if (GET_OBJ_VAL(weapon, 3) == TYPE_ROCKET)
        damage_total = convert_damage(stage((number(3,6) - success_test(GET_BOD(victim) + GET_DEFENSE(victim),
             MAX(2, (GET_OBJ_VAL(weapon, 0) - (int)(GET_IMPACT(victim) / 2))) +
             modify_target(victim))), GET_OBJ_VAL(weapon, 1)));
      else damage_total = convert_damage(stage((number(2,5) - success_test(GET_BOD(victim) + GET_DEFENSE(victim),
             MAX(2, (GET_OBJ_VAL(weapon, 0) - (int)(GET_IMPACT(victim) / 2))) +
             modify_target(victim))), GET_OBJ_VAL(weapon, 1)));
    } else damage_total = convert_damage(stage((number(2,5) - success_test(GET_BOD(victim) + GET_DEFENSE(victim),
             MAX(2, (GET_OBJ_VAL(weapon, 0) - 4 - (int)(GET_IMPACT(victim) / 2))) +
             modify_target(victim))), GET_OBJ_VAL(weapon, 1) - 1));
    damage(ch, victim, damage_total, TYPE_EXPLOSION, PHYSICAL);
  }

  if (!mode)
    for (i = 0; i < (NUM_OF_DIRS - 1); i++)
      if (world[room].dir_option[i] &&
          IS_SET(world[room].dir_option[i]->exit_info, EX_CLOSED))
          damage_door(NULL, room, i, GET_OBJ_VAL(weapon, 1) * 2 +
                      (int)(GET_OBJ_VAL(weapon, 0) / 6), DAMOBJ_EXPLODE);
}

void range_combat(struct char_data *ch, char *target, struct obj_data *weapon,
                  int range, int dir)
{
  int room, nextroom, distance, sight, temp, temp2, left, right, scatter[4];
  int power, level;
  struct char_data *vict = NULL;

  if (ROOM_FLAGGED(ch->in_room, ROOM_PEACEFUL)) {
    send_to_char("This room just has a peaceful, easy feeling...\r\n", ch);
    return;
  }

  sight = find_sight(ch);

  if (CAN_GO2(ch->in_room, dir))
    nextroom = EXIT2(ch->in_room, dir)->to_room;
  else nextroom = NOWHERE;

  if (GET_OBJ_TYPE(weapon) == ITEM_WEAPON && GET_OBJ_VAL(weapon, 3) == TYPE_HAND_GRENADE) {
    if (nextroom == NOWHERE) {
      send_to_char("There seems to be something in the way...\r\n", ch);
      return;
    }
    if (IS_SET(ROOM_FLAGS(nextroom), ROOM_PEACEFUL)) {
      send_to_char("Nah - leave them in peace.\r\n", ch);
      return;
    }
    power = GET_OBJ_VAL(weapon, 0);
    level = GET_OBJ_VAL(weapon, 1);
    if (FIGHTING(ch))
      stop_fighting(ch);
    act("You pull the pin and throw $p!", FALSE, ch, weapon, 0, TO_CHAR);
    act("$n pulls the pin and throws $p!", FALSE, ch, weapon, 0, TO_ROOM);

    WAIT_STATE(ch, PULSE_VIOLENCE);

    if (!GET_SKILL(ch, SKILL_NONAERODYNAMIC))
      temp = MAX(1, GET_SKILL(ch, SKILL_THROWING_WEAPONS));
    else temp = GET_SKILL(ch, SKILL_NONAERODYNAMIC);

    if (!number(0, 19)) {
      sprintf(buf, "A defective grenade lands on the floor.\r\n");
      if (world[nextroom].people) {
        act(buf, FALSE, world[nextroom].people, 0, 0, TO_ROOM);
        act(buf, FALSE, world[nextroom].people, 0, 0, TO_CHAR);
      }
      return;
    } else if (!success_test(temp+GET_OFFENSE(ch), 5)) {
      left = -1;
      right = -1;
      if (dir < UP) {
        if ((dir - 1) < NORTH)
          left = NORTHWEST;
        else left = dir - 1;
        if ((dir + 1) > NORTHWEST)
          right = NORTH;
        else right = dir + 1;
      }
      scatter[0] = ch->in_room;
      if (left > 0 && CAN_GO(ch, left))
        scatter[1] = EXIT(ch, left)->to_room;
      else scatter[1] = NOWHERE;
      if (right > 0 && CAN_GO(ch, right))
        scatter[2] = EXIT(ch, right)->to_room;
      else scatter[2] = NOWHERE;
      if (CAN_GO2(nextroom, dir))
        scatter[3] = EXIT2(nextroom, dir)->to_room;
      else scatter[3] = NOWHERE;
      for (temp = 0, temp2 = 0; temp2 < 4; temp2++)
        if (scatter[temp2] != NOWHERE)
          temp++;
      for (temp2 = 0; temp2 < 4; temp2++)
        if (scatter[temp2] != NOWHERE && !number(0, temp-1)) {
          if (temp2 == 0) {
            act("$p deflects due to $n's poor accuracy, landing at $s feet.",
                FALSE, ch, weapon, 0, TO_ROOM);
            sprintf(buf, "Your realize your aim must've been off-target as "
                    "$p lands at your feet.");
          } else if (temp2 == 3)
            sprintf(buf, "Your aim is slightly off, going past its target.");
          else sprintf(buf, "Your aim is slightly off, and $p veers to the %s.",
                       dirs[temp2 == 1 ? left : right]);
          act(buf, FALSE, ch, weapon, 0, TO_CHAR);
          explode(ch, weapon, scatter[temp2]);
          return;
        }
    }
    explode(ch, weapon, nextroom);
    return;
  }

  for (distance = 1; ((nextroom != NOWHERE) && (distance <= sight)); distance++) {
    if ((vict = get_char_room(target, nextroom)) && vict != ch &&
        CAN_SEE(ch, vict))
      break;
    vict = NULL;
    room = nextroom;
    if (CAN_GO2(room, dir))
      nextroom = EXIT2(room, dir)->to_room;
    else nextroom = NOWHERE;
  }

  if (vict) {
    if (vict == FIGHTING(ch)) {
      send_to_char("You're doing the best you can!\r\n", ch);
      return;
    } else if (IS_SET(ROOM_FLAGS(vict->in_room), ROOM_PEACEFUL)) {
      send_to_char("Nah - leave them in peace.\r\n", ch);
      return;
    } else if (distance > range) {
      act("$N seems to be out of $p's range.", FALSE, ch, weapon, vict, TO_CHAR);
      return;
    } else if (!ok_damage_shopkeeper(ch, vict)) {
      send_to_char("Maybe that's not such a good idea.\r\n", ch);
      return;
    }

    if (GET_OBJ_TYPE(weapon) == ITEM_FIREWEAPON) {
      act("$n draws $p and fires into the distance!", TRUE, ch, weapon, 0, TO_ROOM);
      act("You draw $p, aim it at $N and fire!", FALSE, ch, weapon, vict, TO_CHAR);
      check_killer(ch, vict);
      if (IS_NPC(vict) && !IS_PROJECT(vict) && !FIGHTING(vict)) {
        GET_DEFENSE(vict) = GET_COMBAT(vict);
        GET_OFFENSE(vict) = 0;
      }
      if (FIGHTING(ch))
        stop_fighting(ch);
      hit(ch, vict, TYPE_UNDEFINED);
      WAIT_STATE(ch, 2 * PULSE_VIOLENCE);
      return;
    }

    switch(GET_OBJ_VAL(weapon, 3)) {
      case TYPE_SHURIKEN: case TYPE_THROWING_KNIFE:
        act("$n throws $p at something in the distance!", TRUE, ch, weapon, 0, TO_ROOM);
        act("You throw $p at $N!", FALSE, ch, weapon, vict, TO_CHAR);
        break;
      default:
        act("$n aims $p and fires into the distance!", TRUE, ch, weapon, 0, TO_ROOM);
        act("You aim $p at $N and fire!", FALSE, ch, weapon, vict, TO_CHAR);
        break;
    }
    if (GET_OBJ_VAL(weapon, 3) >= TYPE_PISTOL
	|| GET_OBJ_VAL(weapon, 3) == TYPE_SHURIKEN
	|| GET_OBJ_VAL(weapon, 3) == TYPE_THROWING_KNIFE)
      {
	check_killer(ch, vict);
	if (GET_OBJ_VAL(weapon, 3) < TYPE_PISTOL
	    || GET_OBJ_VAL(weapon, 6) > 0)
	  {
	    if (IS_NPC(vict) && !IS_PROJECT(vict) && !FIGHTING(vict))
	      {
		GET_DEFENSE(vict) = GET_COMBAT(vict);
		GET_OFFENSE(vict) = 0;
	      }
	    if (FIGHTING(ch))
	      stop_fighting(ch);
	    hit(ch, vict, TYPE_UNDEFINED);
	    ranged_response(ch, vict);
	  }
	else
	  send_to_char("*Click*\r\n", ch);
	WAIT_STATE(ch, 2 * PULSE_VIOLENCE);
      }
    else
      {
	if (!has_ammo(ch, weapon->worn_on))
	  return;
	stop_fighting(ch);
	if (GET_OBJ_VAL(weapon, 3) == TYPE_ROCKET)
	  {
	    if (!GET_SKILL(ch, SKILL_MISSILE_LAUNCHERS))
	      temp = MAX(1, GET_SKILL(ch, SKILL_GUNNERY));
	    else
	      temp = GET_SKILL(ch, SKILL_MISSILE_LAUNCHERS);
	  }
	else
	  {
	    if (!GET_SKILL(ch, SKILL_GRENADE_LAUNCHERS))
	      temp = MAX(1, GET_SKILL(ch, SKILL_FIREARMS));
	    else
	      temp = GET_SKILL(ch, SKILL_GRENADE_LAUNCHERS);
      }

      WAIT_STATE(ch, 2 * PULSE_VIOLENCE);

      if (!number(0,49)) {
        sprintf(buf, "A defective %s lands on the floor.",
           (GET_OBJ_VAL(weapon, 3) == TYPE_ROCKET ? "rocket" : "grenade"));
        act(buf, TRUE, vict, 0, 0, TO_ROOM);
        act(buf, TRUE, vict, 0, 0, TO_CHAR);
        return;
      }
      else if (!success_test(temp + GET_OFFENSE(ch), 6)) {
        left = -1;
        right = -1;
        if (dir < UP) {
          if ((dir - 1) < NORTH)
            left = NORTHWEST;
          else left = dir - 1;
          if ((dir + 1) > NORTHWEST)
            right = NORTH;
          else right = dir + 1;
        }
        scatter[0] = ch->in_room;
        if (left > 0 && CAN_GO(ch, left))
          scatter[1] = EXIT(ch, left)->to_room;
        else scatter[1] = NOWHERE;
        if (right > 0 && CAN_GO(ch, right))
          scatter[2] = EXIT(ch, right)->to_room;
        else scatter[2] = NOWHERE;
        if (CAN_GO2(nextroom, dir))
          scatter[3] = EXIT2(nextroom, dir)->to_room;
        else scatter[3] = NOWHERE;
        for (temp = 0, temp2 = 0; temp2 < 4; temp2++)
          if (scatter[temp2] != NOWHERE)
            temp++;
        for (temp2 = 0; temp2 < 4; temp2++)
          if (scatter[temp2] != NOWHERE && !number(0, temp-1)) {
            if (temp2 == 0) {
              act("$p's trajectory is slightly off...", FALSE, ch, weapon, 0, TO_ROOM);
              sprintf(buf, "Your arm jerks just before you fire...");
            } else if (temp2 == 3)
              sprintf(buf, "Your aim is slightly off, going past $N.");
            else sprintf(buf, "Your aim is slightly off, the %s veering to the %s.",
                         (GET_OBJ_VAL(weapon, 3) == TYPE_ROCKET ? "rocket" : "grenade"),
                         dirs[temp2 == 1 ? left : right]);
            act(buf, FALSE, ch, weapon, vict, TO_CHAR);
            target_explode(ch, weapon, scatter[temp2], 0);
            if (GET_OBJ_VAL(weapon, 3) == TYPE_ROCKET)
              for (temp = 0; temp < (NUM_OF_DIRS - 1); temp++)
                if (CAN_GO2(scatter[temp2], temp))
                  target_explode(ch, weapon, EXIT2(scatter[temp2], temp)->to_room, 1);
            return;
          }
      }
      temp2 = vict->in_room;
      target_explode(ch, weapon, vict->in_room, 0);
      if (GET_OBJ_VAL(weapon, 3) == TYPE_ROCKET)
        for (temp = 0; temp < (NUM_OF_DIRS - 1); temp++)
          if (CAN_GO2(temp2, temp))
            target_explode(ch, weapon, EXIT2(temp2, temp)->to_room, 1);
      return;
    }
    return;
  }
  bool found = FALSE;

  if (CAN_GO2(ch->in_room, dir))
    nextroom = EXIT2(ch->in_room, dir)->to_room;
  else nextroom = NOWHERE;

  // now we search for a door by the given name
  for (distance = 1; nextroom != NOWHERE && distance <= sight; distance++) {
    if (EXIT2(nextroom, dir) && EXIT2(nextroom, dir)->keyword &&
        isname(target, EXIT2(nextroom, dir)->keyword) &&
        !IS_SET(EXIT2(nextroom, dir)->exit_info, EX_DESTROYED) &&
        (PRF_FLAGGED(ch, PRF_HOLYLIGHT) || IS_AFFECTED(ch, AFF_INFRAVISION) ||
        (IS_LIGHT(nextroom) || (IS_LOW(nextroom) &&
        IS_AFFECTED(ch, AFF_LOW_LIGHT))))) {
      found = TRUE;
      break;
    }
    room = nextroom;
    if (CAN_GO2(room, dir))
      nextroom = EXIT2(room, dir)->to_room;
    else nextroom = NOWHERE;
  }

  if (found) {
    if (FIGHTING(ch)) {
      send_to_char("Maybe you'd better wait...\r\n", ch);
      return;
    } else if (!IS_SET(EXIT2(nextroom, dir)->exit_info, EX_CLOSED) && isname(target, EXIT2(nextroom, dir)->keyword) ) {
      send_to_char("You can only damage closed doors!\r\n", ch);
      return;
    } else if (IS_SET(ROOM_FLAGS(nextroom), ROOM_PEACEFUL)) {
      send_to_char("Nah - leave it in peace.\r\n", ch);
      return;
    } else if (distance > range) {
      send_to_char(ch, "The %s seems to be out of %s's range.\r\n",
                   CAP(fname(EXIT2(nextroom, dir)->keyword)),
                   weapon->short_description);
      return;
    }

    if (GET_OBJ_TYPE(weapon) == ITEM_FIREWEAPON) {
      act("$n draws $p and fires into the distance!", TRUE, ch, weapon, 0, TO_ROOM);
      sprintf(buf, "You draw $p, aim it at the %s and fire!",
              fname(EXIT2(nextroom, dir)->keyword));
      act(buf, FALSE, ch, weapon, vict, TO_CHAR);
    } else {
      switch(GET_OBJ_VAL(weapon, 3)) {
        case TYPE_SHURIKEN: case TYPE_THROWING_KNIFE:
          act("$n throws $p at something in the distance!", TRUE, ch, weapon, 0, TO_ROOM);
          sprintf(buf, "You throw $p at the %s!", fname(EXIT2(nextroom, dir)->keyword));
          act(buf, FALSE, ch, weapon, vict, TO_CHAR);
          break;
        default:
          act("$n aims $p and fires into the distance!", TRUE, ch, weapon, 0, TO_ROOM);
          sprintf(buf, "You aim $p at the %s and fire!",
                  fname(EXIT2(nextroom, dir)->keyword));
          act(buf, FALSE, ch, weapon, vict, TO_CHAR);
          break;
      }
      if (GET_OBJ_VAL(weapon, 3) >= TYPE_PISTOL)
        if (!has_ammo(ch, weapon->worn_on))
          return;
    }
    switch (GET_OBJ_VAL(weapon, 3)) {
      case TYPE_SHURIKEN: case TYPE_THROWING_KNIFE: case TYPE_ARROW:
        damage_door(ch, nextroom, dir, GET_OBJ_VAL(weapon, 1) * 2 +
                    (int)(GET_OBJ_VAL(weapon, 0) / 6), DAMOBJ_PIERCE);
        break;
      case TYPE_GRENADE_LAUNCHER:
        target_explode(ch, weapon, nextroom, 0);
        break;
      case TYPE_ROCKET:
        target_explode(ch, weapon, nextroom, 0);
        for (temp = 0; temp < (NUM_OF_DIRS - 1); temp++)
          if (CAN_GO2(nextroom, temp))
            target_explode(ch, weapon, EXIT2(nextroom, temp)->to_room, 1);
        break;
      default:
        damage_door(ch, nextroom, dir, GET_OBJ_VAL(weapon, 1) * 2 +
                    (int)(GET_OBJ_VAL(weapon, 0) / 6), DAMOBJ_PROJECTILE);
        break;
    }
    WAIT_STATE(ch, 2 * PULSE_VIOLENCE);
    return;
  }

  sprintf(buf, "You can't see any %s there.\r\n", target);
  send_to_char(buf, ch);
  return;
}

void roll_individual_initiative(struct char_data *ch)
{
  if (AWAKE(ch)) {
    if (IS_PERSONA(ch))
      GET_INIT_ROLL(ch) = MIN(39, (dice(1 + GET_INIT_DICE(ch), 6) + GET_REA(ch)));
    else if (IS_IC(ch)) {
      switch(world[ch->in_room].sector_type) {
        case SECT_GREEN:
          GET_INIT_ROLL(ch) = 5 + GET_LEVEL(ch);
          break;
        case SECT_ORANGE:
          GET_INIT_ROLL(ch) = 7 + GET_LEVEL(ch);
          break;
        case SECT_RED:
          GET_INIT_ROLL(ch) = 9 + GET_LEVEL(ch);
          break;
        case SECT_BLACK:
          GET_INIT_ROLL(ch) = 10 + GET_LEVEL(ch);
          break;
        default:
          GET_INIT_ROLL(ch) = 0;
          break;
      }
    }
    else
      GET_INIT_ROLL(ch) = MIN(39, (dice(1 + GET_INIT_DICE(ch), 6) + GET_REA(ch)));
    if (AFF_FLAGGED(ch, AFF_ACTION)) {
      GET_INIT_ROLL(ch) -= 10;
      REMOVE_BIT(AFF_FLAGS(ch), AFF_ACTION);
    }
  }
  if (1)
    {
      char rbuf[MAX_STRING_LENGTH];
      sprintf(rbuf,"Init: %2d %s",
	      GET_INIT_ROLL(ch), GET_NAME(ch));
      act(rbuf,TRUE,ch,NULL,NULL,TO_ROLLS);
    }
}

void decide_combat_pool(void)
{
  struct char_data *ch;

  for (ch = combat_list; ch; ch = ch->next_fighting) {
    if (ch->bioware)
      check_adrenaline(ch, 0);

    if (IS_NPC(ch) && !IS_PROJECT(ch) && !IS_PERSONA(ch) && FIGHTING(ch)) {
      if (GET_INIT_ROLL(ch) == GET_INIT_ROLL(FIGHTING(ch)))
        GET_OFFENSE(ch) = GET_COMBAT(ch) >> 1;
      else if (GET_INIT_ROLL(ch) > GET_INIT_ROLL(FIGHTING(ch)))
        GET_OFFENSE(ch) = (int)(GET_COMBAT(ch) * .75);
      else GET_OFFENSE(ch) = (int)(GET_COMBAT(ch) / 4);
      GET_DEFENSE(ch) = GET_COMBAT(ch) - GET_OFFENSE(ch);
    }
  }
}

void roll_initiative(void)
{
  struct char_data *ch;

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

  }
  return;
}

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

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

    if (FIGHTING(ch) == NULL || GET_POS(ch) <= POS_STUNNED)
      stop_fighting(ch);
    else if (GET_INIT_ROLL(ch) >= 0) {
      if (IS_PERSONA(ch)) {
        if (ch->in_room != FIGHTING(ch)->in_room)
          stop_fighting(ch);
        else {
          hit(ch, FIGHTING(ch), TYPE_UNDEFINED);
          GET_INIT_ROLL(ch) -= 10;
        }
      } else {
        if ((!IS_NPC(ch) || (IS_NPC(ch) && !mob_magic(ch))) && !cycle_spell_q(ch))
          hit(ch, FIGHTING(ch), TYPE_UNDEFINED);
        GET_INIT_ROLL(ch) -= 10;
        if (MOB_FLAGGED(ch, MOB_SPEC) && mob_index[GET_MOB_RNUM(ch)].func != NULL)
          (mob_index[GET_MOB_RNUM(ch)].func) (ch, ch, 0, "");
      }
    }
  }
}

void order_list(void)
{
  register struct char_data *one, *two, *next, *previous = NULL, *temp;

  if (combat_list == NULL)
    return;

  for (one = combat_list; one; previous = NULL, one = next) {
    next = one->next_fighting;
    for (two = combat_list; two && two->next_fighting; previous = two,
         two = two->next_fighting) {
      if (GET_INIT_ROLL(two->next_fighting) > GET_INIT_ROLL(two)) {
        if (previous)
          previous->next_fighting = two->next_fighting;
        else combat_list = two->next_fighting;
        temp = two->next_fighting->next_fighting;
        two->next_fighting->next_fighting = two;
        two->next_fighting = temp;
      }
    }
  }
}