/
roa/
roa/lib/boards/
roa/lib/config/
roa/lib/edits/
roa/lib/help/
roa/lib/misc/
roa/lib/plrobjs/
roa/lib/quests/
roa/lib/socials/
roa/lib/www/
roa/lib/www/LEDSign/
roa/lib/www/LEDSign/fonts/
roa/lib/www/LEDSign/scripts/
roa/src/s_inc/
roa/src/sclient/
roa/src/sclient/binary/
roa/src/sclient/text/
roa/src/util/
/************************************************************************
	Realms of Aurealis 		James Rhone aka Vall of RoA

fight.c					Combat related file, pc/npc fight
					code, some skills, etc.  

		******** Heavily modified and expanded ********
		*** BE AWARE OF ALL RIGHTS AND RESERVATIONS ***
		******** Heavily modified and expanded ********
		        All rights reserved henceforth. 

    Please note that no guarantees are associated with any code from
Realms of Aurealis.  All code which has been released to the general
public has been done so with an 'as is' pretense.  RoA is based on both
Diku and CircleMUD and ALL licenses from both *MUST* be adhered to as well
as the RoA license.   *** Read, Learn, Understand, Improve ***
*************************************************************************/
#include "conf.h"
#include "sysdep.h"

#include "structures.h"
#include "utils.h"
#include "comm.h"
#include "handler.h"
#include "interpreter.h"
#include "acmd.h"
#include "db.h"
#include "magic.h"
#include "mudlimits.h"
#include "nature.h"
#include "objsave.h"
#include "bard.h"
#include "fight.h"
#include "affect.h"
#include "lists.h"
#include "quest.h"
#include "global.h"
#include "darkenelf.h"

/* Global structures */
chdata *combat_list = NULL;		/* head of l-list of fighting chars*/
chdata *combat_next_dude = NULL;	/* Next dude global trick          */

/* External structures */
extern int    exp_table[NUM_CLASSES][MAX_LEVELS];

/* External functions */
void	forget(chdata *ch, chdata *victim);
void	remember(chdata *ch, chdata *victim);
int	check_arena_state(BOOL award);  /* any left? */
void 	damage_object(obdata *ob, chdata *ch);
extern  void send_combat_messages(int dam, chdata *ch, chdata *vict, int spl);
extern 	void send_position_messages(int dam, chdata *ch, chdata *vict);

/* The Fight related routines */
/* New appear() takes care of spelleq now - SL */
void appear(chdata *ch)
{
   BOOL i = IS_AFFECTED(ch, AFF_INVISIBLE);
   BOOL j;

   affect_from_char(ch, SPELL_INVISIBLE);
   REMOVE_BIT(AFF_FLAGS(ch), AFF_INVISIBLE);
   affect_total(ch);

   j = IS_AFFECTED(ch, AFF_INVISIBLE);

   if(i && !j) 
     act("$n slowly fades into existence.", FALSE, ch, 0, 0, TO_ROOM);
}

// set position based on current hit points
void	update_pos(chdata *victim)
{
   if ((GET_HIT(victim) > 0) && (GET_POS(victim) > POS_STUNNED))
      return;
   else if (GET_HIT(victim) > 0)
      GET_POS(victim) = POS_STANDING;
   else if (GET_HIT(victim) <= -11)
      GET_POS(victim) = POS_DEAD;
   else if (GET_HIT(victim) <= -6)
      GET_POS(victim) = POS_MORTALLYW;
   else if (GET_HIT(victim) <= -3)
      GET_POS(victim) = POS_INCAP;
   else
      GET_POS(victim) = POS_STUNNED;
}

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

   assert(!FIGHTING(ch));

   ch->next_fighting = combat_list;
   combat_list = ch;

   affect_from_char(ch, SPELL_SLEEP);

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

/* remove a char from the list of fighting chars */
void	stop_fighting(chdata *ch)
{
   chdata *temp;

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

   REMOVE_FROM_LIST(ch, combat_list, next_fighting);
   ch->next_fighting = NULL;
   FIGHTING(ch) = NULL;

   VWAIT(ch) = 0;
   if (IS_PC(ch))
   {
     ch->pc_specials->whirlwind = 0;
     ch->pc_specials->cyclone = 0;
   }

   // remove some combat character flags
   REMOVE_BIT(CHAR_FLAGS(ch), CH_DISEMBOWEL | CH_SIDESTEP | CH_SUBDUED | CH_REDIRECTED | 
                              CH_LOWSTRIKE | CH_BLADEDANCE | CH_ONEATTACK);

   // yank the tumbling... 4/21/98 -jtrhone
   affect_from_char(ch, SKILL_TUMBLE);

   GET_POS(ch) = POS_STANDING;
   update_pos(ch);
}

// from a chdata structure, make an object filled with ch's stuff 
void	make_corpse(chdata *ch)
{
   obdata *corpse, *o;
   obdata *money;
   int	i;
   static char buf[MAX_STRING_LENGTH];

   obdata *create_money(int amount);

   CREATE(corpse, obdata, 1);
   clear_object(corpse);

   corpse->touched = TRUE;  /* don't purge this on normal reset */
   corpse->item_number = NOWHERE;
   corpse->in_room = NOWHERE;

   if (IS_PC(ch))
      corpse->plr_num = GET_IDNUM(ch);
   else
      corpse->plr_num = 0;

   if (IS_NPC(ch))
     corpse->name = str_dup("corpse remains");
   else
     corpse->name = str_dup("pc corpse remains");

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

   sprintf(buf, "the remains of %s", GET_NAME(ch));
   corpse->shdesc = str_dup(buf);

   corpse->contains = ch->carrying;
   if (GET_GOLD(ch) > 0) {
      /* following 'if' clause added to fix gold duplication loophole */
      if (IS_NPC(ch) || (IS_PC(ch) && ch->desc)) {
	 money = create_money(GET_GOLD(ch));
	 obj_to_obj(money, corpse);
      }
      GET_GOLD(ch) = 0;
   }

   corpse->type_flag = ITEM_CONTAINER;
   OBJ_WEARS(corpse) = ITEM_TAKE;
   OBJ_EXTRAS(corpse) = ITEM_NODONATE;
   corpse->value[0] = 0; /* You can't store stuff in a corpse */
   corpse->value[3] = 1; /* corpse identifyer */
   corpse->weight = GET_WEIGHT(ch) + IS_CARRYING_W(ch);
   if (IS_NPC(ch))
      corpse->timer = max_npc_corpse_time;
   else
      corpse->timer = max_pc_corpse_time;

   for (i = 0; i < MAX_WEAR; i++)
      if (EQ(ch, i))
	 obj_to_obj(unequip_char(ch, i, TRUE), corpse);

   ch->carrying = 0;
   IS_CARRYING_N(ch) = 0;
   IS_CARRYING_W(ch) = 0;

   corpse->next = object_list;
   object_list = corpse;

   for (o = corpse->contains; o; o->in_obj = corpse, o = o->next_content)
      ;
   object_list_new_owner(corpse, 0);

   float_sink_object(corpse, ch->in_room);  /* will sink RoA*/
}

/* for a corpseless death jtrhone aka Vall*/
void	drop_all(chdata *ch)
{
   obdata *o;
   obdata *money;
   int	i;
   obdata *create_money(int amount);

   if (GET_GOLD(ch) > 0) {
      /* following 'if' clause added to fix gold duplication loophole */
      if (IS_NPC(ch) || (IS_PC(ch) && ch->desc)) {
	 money = create_money(GET_GOLD(ch));
	 float_sink_object(money, ch->in_room);
      }
      GET_GOLD(ch) = 0;
   }

   while(ch->carrying)
   {
     o = ch->carrying;
     obj_from_char(o);
     float_sink_object(o, ch->in_room);
   }

   for (i = 0; i < MAX_WEAR; i++)
      if (EQ(ch, i))
	 float_sink_object(unequip_char(ch, i, TRUE), ch->in_room);

   ch->carrying = NULL;
   IS_CARRYING_N(ch) = 0;
   IS_CARRYING_W(ch) = 0;
}

/* When ch kills victim -adopted from 3.0 */
void change_alignment(chdata *ch, chdata *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;
}

// send cry of death to room, and to rooms nearby
// Added check for undead. 08/13/98 -callahan
void	death_cry(chdata *ch)
{
   int	door, was_in;

   if (IN_NOWHERE(ch))
     return;

   if (MOB_CLASS_FLAGGED(ch, MOB_UNDEAD))
     strcpy(buf, "$n begins to crumble, and falls into a pile of dust.");
   else {
     /* add a bit of randomness */
     switch(number(0, 4)) {
     case 0:
       strcpy(buf, "Your blood %B%6freezes%0 as you hear $n's death cry.");
       strcpy(buf2, "Your blood %B%6freezes%0 as you hear someone's death cry.");
       break;
     case 1:
       strcpy(buf, "A blood %4chilling%0 scream accompanies $n's death.");
       strcpy(buf2, "A blood %4chilling%0 scream accompanies someone's death.");
       break;
     case 2:
       strcpy(buf, "$n %Bscreams%0 out in anguish as $e visits death.");
       strcpy(buf2, "Someone %Bscreams%0 out in anguish as they visit death.");
       break;
     case 3:
       strcpy(buf, "$n's %4cry of death%0 chills you to the bone.");
       strcpy(buf2, "Someone's %4cry of death%0 chills you to the bone.");
       break;
     default:
       strcpy(buf, "You hear $n's %4death cry%0, then %4silence...%0");
       strcpy(buf2, "You hear someone's %4death cry%0, then %4silence...%0");
       break;
     }
   }

   act(buf, 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 = DIR(was_in, door)->to_room;
	act(buf2, FALSE, ch, 0, 0, TO_ROOM);
	ch->in_room = was_in;
     }
}

// the actual killing of chdata
// Added check for undead. 08/13/98 -callahan
void	raw_kill(chdata *ch)
{
  void CharToDust(CharData *ch);

  if (!INVALID_ROOM(ch->in_room) && IS_PC(ch) &&
      REAL_ZONE(world[ch->in_room].zone))
    zone_table[world[ch->in_room].zone].pc_deaths++;

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

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

  death_cry(ch);

  if (MOB_FLAGGED(ch, MOB_NO_CORPSE) || MOB_CLASS_FLAGGED(ch, MOB_UNDEAD))
    drop_all(ch);
/*
  else if (MOB_CLASS_FLAGGED(ch, MOB_UNDEAD))
    CharToDust(ch);
*/
  else
    make_corpse(ch);

  extract_char(ch);
}

// die without penalty, remove possible arena harmful affects
void die_arena_style(chdata *ch)
{
  extern struct arena_data arena_info;
  dsdata *pt;

  arena_info.ticks_since_kill = 0;

  char_from_room(ch);
  if (ch->pc_specials->preroom > 0 && ch->pc_specials->preroom < top_of_world)
    char_to_room(ch, ch->pc_specials->preroom);
  else
    char_to_room(ch, real_room(GET_LOADROOM(ch)));
  do_look_at_room(ch, 0, 0);

  // restore old hit/move/mana only if they were less than now
  GET_HIT(ch) = ch->pc_specials->prehit;
  GET_MANA(ch) = ch->pc_specials->premana;
  GET_MOVE(ch) = ch->pc_specials->premove;
  ch->pc_specials->prehit = 1;
  ch->pc_specials->premana = 1;
  ch->pc_specials->premove = 1;
  update_pos(ch);

  send_to_char("You have been eliminated!!!\n\r",ch);

  // remove ill arena affects -roa jtrhone
  affect_from_char(ch, SPELL_HOLD_PERSON);
  affect_from_char(ch, SPELL_BLINDNESS);
  affect_from_char(ch, SPELL_POISON);
  affect_from_char(ch, SPELL_POISON_DART);
  affect_from_char(ch, SPELL_ENSNARE);

  REMOVE_BIT(PLR_FLAGS(ch), PLR_ARENA);
  REMOVE_BIT(PRF_FLAGS(ch), PRF_AREN_CH);

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

  if (ch->followers || ch->master)
    die_follower(ch);

  sprintf(buf, "%s has been eliminated from the ARENA!!\n\r",GET_NAME(ch));
  for (pt = descriptor_list; pt; pt=pt->next)
   if (D_CHECK(pt) && pt->character != ch && SEND_OK(pt->character) &&
	!PRF2_FLAGGED(pt->character, PRF2_NOARENA))
     send_to_char(buf, pt->character);

  act("$n arrives amidst a flash of light.", TRUE, ch, 0, 0, TO_ROOM);
  check_arena_state(TRUE);
}

/* do_die */
// die! distributes exp loss, resets violence wait, updates rent/reimb files
// calls the raw_kill - actual death function
void	die(chdata *ch, BOOL akill)
{
  VWAIT(ch) = 0;

  if (IN_ARENA(ch))
    die_arena_style(ch);
  else
  {
    if (akill)
      gain_exp(ch, -(GET_EXP(ch)/12));	/* an assassin kill */
    else
    {
      if (GET_LEVEL(ch) < 50)
        gain_exp(ch, -GET_EXP(ch) / 8);  /* div by 6 for now less loss */
      else
        gain_exp(ch, -GET_EXP(ch) / 6);  // higher levels LOSE way more now
    }
   
    if (GET_LEVEL(ch) >= number(0, 50))   /* level 50+ WILL lose CON */
      GET_CON(ch) -= 1;
 
    /* save objlist for players here in text file for reimb command */
    write_reimb_file(ch);
    delete_object_file(ch);
    raw_kill(ch);
  }
}

/* group gain rewritten RoA James Rhone 1/11/96 */
/* use levels of group as percentage bases */
void	group_gain(chdata *ch, chdata *victim)
{
  int share = 0, tot_levels = 0, lev_diff = 0;
  float percentage = 0.0, roughshare = 0.0;
  int avail_exp = 0, highest_level = 0;
  chdata *k;

  while ((k = group_member(ch, TRUE)))
  {
    tot_levels += GET_LEVEL(k);
    highest_level = MAX(highest_level, GET_LEVEL(k));	    
  }
    
  /* find available EXP based on highest level in the group */
  if (!(lev_diff = highest_level - GET_LEVEL(victim)))
    avail_exp = GET_EXP(victim);  
  else
  if (lev_diff > 0)  /* ch is a higher level */
  {
    percentage = (5.0 * (float) lev_diff) / 100.0;
    roughshare = (float) GET_EXP(victim) * percentage;
    avail_exp  = GET_EXP(victim) - (int) roughshare;
  }
  else  /* mob is a higher level */
  if (lev_diff < 0)
  {
    lev_diff   = -(lev_diff);
    percentage = (5.0 * (float) lev_diff) / 100.0;
    roughshare = (float) GET_EXP(victim) * percentage;
    avail_exp  = GET_EXP(victim) + (int) roughshare;
  }

  avail_exp = MAX(avail_exp, 1);

  while ((k = group_member(ch, TRUE)))
  {
    percentage = (float) GET_LEVEL(k) / (float) tot_levels; 
    roughshare = percentage * (float) avail_exp;
    share = (int) roughshare;
    share = MAX(share, 1);

    // have HOLD check here...
    if (IS_HELD(victim) && HOLDER(victim) != k)
      share /= 10;
         
    if (IS_ASSASSIN(victim) || IN_ARENA(victim))
      share = gain_exp(k, 1);
    else
    if (highest_level - GET_LEVEL(k) > 20) /* must be within 20 levels! -roa */
      share = gain_exp(k, 1);
    else
      share = gain_exp(k, share);

    if (share > 1) 
      sprintf(buf2, "You receive your share of experience -- %d points.", share);
    else
      strcpy(buf2, "You receive no experience from the battle.");
    act(buf2, FALSE, k, 0, 0, TO_CHAR);

    change_alignment(k, victim);

    if (auto_save && !(number(0, 4)))
      save_char(k, NOWHERE);
  }
}

// when a nongrouped character kills someone other than him/herself
// NEW SOLO ALGORITHM   RoA  James Rhone  (now solo will yield
// more exp than grouping)
void solo_gain_exp(chdata *ch, chdata *victim)
{
  int lev_diff = 0, exp = 0;
  float percentage = 0.0, roughgain = 0.0;

  if (!(lev_diff = GET_LEVEL(ch) - GET_LEVEL(victim)))
    exp = GET_EXP(victim);  
  else
  if (lev_diff > 0)  /* ch is a higher level */
  {
    percentage = (5.0 * (float) lev_diff) / 100.0;
    roughgain = (float) GET_EXP(victim) * percentage;
    exp = GET_EXP(victim) - (int) roughgain;
  }
  else  /* mob is a higher level */
  if (lev_diff < 0)
  {
    lev_diff = -(lev_diff);
    percentage = (5.0 * (float) lev_diff) / 100.0;
    roughgain = (float) GET_EXP(victim) * percentage;
    exp = GET_EXP(victim) + (int) roughgain;
  }

  if (IS_ASSASSIN(victim))
  {
    send_to_char("You receive no experience for killing other assassins.\n\r",ch);
    change_alignment(ch,victim);
  } 
  else
  if (IN_ARENA(victim)) 
    send_to_char("You receive no experience for defeating ARENA combatants.\n\r",ch);
  else 
  {
    exp = MAX(exp, 1);

    // have HOLD check here...
    if (IS_HELD(victim) && HOLDER(victim) != ch)
      exp /= 10;
         
    exp = gain_exp(ch, exp);

    change_alignment(ch, victim);

    if (exp > 1)
      sprintf(buf2, "You receive %d experience points.\n\r", exp);
    else
      strcpy(buf2, "You receive no experience from the battle.\n\r");
    send_to_char(buf2, ch);
  }

  if (auto_save && !number(0, 1))
    save_char(ch, NOWHERE);
}

// returns one member of ch's group, possibly ch
chdata	*random_group_member(chdata *ch)
{
   chdata *k;
   struct follow_type *f;
   int num = 0, pick = 0;
   int i = 0;

   if (!IS_AFFECTED(ch, AFF_GROUP))
     return (ch);

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

   /* tally levels for group starting with leader */
   if (IS_AFFECTED(k, AFF_GROUP) && SAME_ROOM(k, ch))
      num++;

    for (f = k->followers; f; f = f->next)
      if (IS_AFFECTED(f->follower, AFF_GROUP) && 
	  SAME_ROOM(f->follower, ch) &&
          (f->follower != k))  /* we counted K already */
	num++;

   if (num <= 1)
     return (ch);

   pick = number(1, num);
   for (f = k->followers; f && i != pick; f = f->next)
     if (IS_AFFECTED(f->follower, AFF_GROUP) && SAME_ROOM(f->follower, ch))
     {
	i++;
	if (i == pick) break;
     }
 
  if (f && f->follower)
    return(f->follower);
  else
    return(ch);
}

/* return true if (both PC and both ASSASSINS or both in ARENA) */
BOOL check_mortal_combat(chdata *ch, chdata *vict)
{
  if (IS_NPC(ch) || IS_NPC(vict)) return TRUE;
  if (IN_ARENA(ch) && IN_ARENA(vict)) return TRUE;
  if (IS_ASSASSIN(ch) && IS_ASSASSIN(vict)) return TRUE;

  act("%1Assassin%0 vs %1Assassin%0 only.",FALSE,ch, 0, 0, TO_CHAR);
  return FALSE;
}

int spell_adjust_damage(chdata *victim, int dam)
{
  if (IS_AFFECTED(victim, AFF_SANCTUARY))
    dam /= 2;  /* 1/2 damage when sanctuary */
  else
  if (IS_AFFECTED2(victim, AFF2_CROW))
   dam /= 3;  /* 1/3 damage when protected by crow */
  return dam;
}

// takes damage into account and adjusts it according to spells/skills etc
// returns adjusted damage
int  adjust_damage(chdata *ch, chdata *victim, int dam, int *spellnum, BOOL regardless, int *shock_dam)
{
   chdata *tmp_ch;
   sh_int pack;

   if (regardless) 
   {
     // max damage is a range from 250 to 350, random :) -jtrhone roa
     dam = MAX(0, MIN(dam, number(250, 350)));
     GET_HIT(victim) -= dam;
     return dam;
   }

   dam = spell_adjust_damage(victim, dam);

   /* drow fight better outside in the dark James Rhone RoA*/
   if (IS_DROW(ch) && OUTSIDE(ch)) 
   {
     if (global_weather.sunlight == SUN_LIGHT) 
       dam -= (dam / 4);
     else
     if (global_weather.sunlight == SUN_DARK)
       dam += (dam / 4);
   }

   /* orcs fight better in packs James Rhone RoA*/
   if (ch != victim && IS_ORC(ch)) 
   {
     for (tmp_ch = world[ch->in_room].people, pack = 0; tmp_ch;
	  tmp_ch=tmp_ch->next_in_room)
       if (tmp_ch != ch && IS_ORC(tmp_ch) && FIGHTING(tmp_ch) == FIGHTING(ch))
	 pack++;
     if (pack)
      dam += 2 * pack;
   }

   /* If victim has mirror images, see if ch hit one of them */
  if (victim->specials.num_of_images > 0 && 
      number(0, victim->specials.num_of_images) &&
      !saves_spell(ch, SPELL_MIRRORIMAGE))   // add sv_vs_magic now  11/29/97 -jtrhone
  {
     victim->specials.num_of_images--;
     dam = 0;
     *spellnum = SPELL_MIRRORIMAGE;
  }

  /* if amitars or baals, reduce mana each hit */
  if (affected_by_spell(victim, SPELL_SONG_AMITAR) &&
      bard_song_near_char(victim, SONG_AMITAR))
  {
    GET_MANA(victim) -= dam / 4;
    GET_MANA(victim) = MAX(0, GET_MANA(victim));
    if (!GET_MANA(victim) && IS_PC(victim))
    {
     if (SINGING(victim))
       do_finish(ch, " song", 0, 0);
     if (PLAYING(victim))
       do_finish(ch, " tune", 0, 0);
    }
    dam = (int) (dam * 0.6);
    *spellnum = SPELL_SONG_AMITAR;
  }
  else
  if (affected_by_spell(victim, SPELL_SONG_BALM) &&
      bard_song_near_char(victim, SONG_BALM))
  {
    GET_MANA(victim) -= dam / 8;
    GET_MANA(victim) = MAX(0, GET_MANA(victim));
    if (!GET_MANA(victim) && IS_PC(victim))
    {
     if (SINGING(victim))
       do_finish(ch, " song", 0, 0);
     if (PLAYING(victim))
       do_finish(ch, " tune", 0, 0);
    }
    dam -= GET_LEVEL(victim);
    dam = MAX(0, MIN(dam, 250));
    *shock_dam = dam;
    GET_HIT(victim) -= dam;
    dam = 0;
    *spellnum = SPELL_SONG_AMITAR;  /* same combat messages as amitar */
  }

   // max damage is a range from 250 to 350, random :) -jtrhone roa
   dam = MAX(0, MIN(dam, number(250, 350)));
   GET_HIT(victim) -= dam;

   if (!*shock_dam)
    *shock_dam = dam;

   return dam;
}

void send_combat_mortlog(chdata *ch, chdata *victim)
{
  dsdata *jt;

  if (!INCOG(victim) && ch != victim) 
  {
    for (jt = descriptor_list; jt; jt = jt->next) 
      if (D_CHECK(jt) && jt->character != victim &&
          PRF_FLAGGED(jt->character,PRF_MORTLOG) && SEND_OK(jt->character))
      {
        sprintf(buf2, "[ %s killed by %s -> %s ]\n\r", GET_NAME(victim), 
       	      GET_NAME(ch), world[victim->in_room].name);
        send_to_char(buf2, jt->character);
      }
  }
  else 
  for (jt = descriptor_list; jt; jt = jt->next) 
    if (D_CHECK(jt) && jt->character != victim &&
        PRF_FLAGGED(jt->character,PRF_MORTLOG) && SEND_OK(jt->character))
    {
      sprintf(buf2, "[ %s embraces %%1death%%0. ]\n\r",GET_NAME(victim));
      send_to_char(buf2, jt->character);
    }

  sprintf(buf2, "%s killed by %s  rm#%d", GET_NAME(victim), GET_NAME(ch),
          world[victim->in_room].number);
  mudlog(buf2, BRF, LEV_IMM, TRUE);
}

void do_combat_autos(chdata *ch)
{
  long money1, money2, money_looted;

  if (IS_PC(ch))  // can have a SPC_AUTOLOOT here? (roa) 
  {
    money1 = GET_GOLD(ch);

    if (PRF_FLAGGED(ch, PRF_AGOLD))
    {
      sprintf(buf, "all.%s from corpse", currency_name);
      do_get(ch, buf, 0, 0);
    }

    if (PRF_FLAGGED(ch, PRF_ALOOT))
      do_get(ch, "all from corpse", 0, 0);

    money2 = GET_GOLD(ch);
    money_looted = MAX(0, (money2 - money1));
    if (PRF_FLAGGED(ch, PRF_ASPLIT) && IS_AFFECTED(ch, AFF_GROUP))
    {
      sprintf(buf, "%ld", money_looted);
      do_split(ch, buf, 0, 0);
    }
  }
}

// small proc for doing the shaman wolf thing
void do_shaman_wolf(int dam, chdata *ch, chdata *victim)
{
  if (GET_LEVEL(ch) < 60)
    GET_HIT(ch) -= dam;
  else
    GET_HIT(ch) -= dam/2;
  GET_HIT(ch) = MAX(1, GET_HIT(ch));
  act("The %B%1WOLF%0 lashes out at you from $M!", FALSE, ch, 0, victim, TO_CHAR);
  act("The %B%1WOLF%0 lashes out at $N!", FALSE, victim, 0, ch, TO_CHAR);
  act("The %B%1WOLF%0 lashes out from $n towards $N!", FALSE, victim, 0, ch, TO_NOTVICT);
  update_pos(ch);
}   

// if pc and fighting linkless...
void linkless_flee(chdata *victim)
{
  do_flee(victim, "", 0, 0);
  if (!FIGHTING(victim)) 
  {
    act("$n is rescued by divine forces.", FALSE, victim, 0, 0, TO_ROOM);
    victim->specials.was_in_room = victim->in_room;
    char_from_room(victim);
    char_to_room(victim, 0);
  }
}

// if in shock, send a little message
void send_shock_message(chdata *ch, chdata *victim)
{
  act("$N falters under your deadly blow!", FALSE, ch, 0, victim,TO_CHAR);
  act("AHHH!  You %1falter%0 under the blow!", FALSE, victim, 0, 0, TO_CHAR);
  act("$n drops to $s knees in %Bshock%0!", FALSE, victim, 0, 0, TO_ROOM);
  VWAIT(victim) += 1; /* in case it wasnt 0 :) */
}

// these are the things done if victim dies in damage()
void do_death_procs(chdata *ch, chdata *victim)
{
  BOOL loot_ok = FALSE;

  if (IS_NPC(victim) || victim->desc)
  {
    if (IS_AFFECTED(ch, AFF_GROUP)) 
      group_gain(ch, victim);
    else 
    if (ch != victim)  // if suicide, no exp, i.e. DTs and such
      solo_gain_exp(ch, victim);
  }

  if (IS_PC(victim)) 
   send_combat_mortlog(ch, victim);

  if (ch != victim && (IS_NPC(victim) || IS_ASSASSIN(victim)))
    loot_ok = TRUE;

  if (IS_PC(victim) && (MOB_FLAGGED(ch, MOB_MEMORY) || ch->npc_specials.memory))
    forget(ch, victim);

  if (FIGHTING(ch) == victim)
    stop_fighting(ch);

  // ok, ch killed victim, check SLAYMOB quests see if ch finished one...
  if (IS_NPC(victim))
    qcheck_slaymob(ch, GET_MOB_VNUM(victim));

  if (ch != victim && IS_ASSASSIN(ch) && IS_ASSASSIN(victim) && !IN_ARENA(victim) && !IN_ARENA(ch))
    die(victim, TRUE);  /* yes an assassin kill */
  else 
    die(victim, FALSE);

  if (loot_ok)
    do_combat_autos(ch);
}

// Some spells are touch-sensitive. 05/03/98 -callahan
int spell_touch_affects(chdata *vict, chdata *ch)
{
  int dam = 0;

  if (!vict)
    return 0;

  if (affected_by_spell(ch, SPELL_WALL_FIRE)) {
    act("You are burned by the flames surrounding $n.",
        FALSE, ch , 0, vict, TO_VICT);
    act("$N is burned by the flames surrounding you.",
        FALSE, ch, 0, vict, TO_CHAR);
    act("$N is burned by the flames surrounding $n.",
        FALSE, ch, 0, vict, TO_NOTVICT);

    dam += (int) ((float) Level(ch) * 0.02);

  } else if (affected_by_spell(ch, SPELL_WALL_BRAMBLES)) {
    act("You are scathed by the brambles which surround $n.",
        FALSE, ch , 0, vict, TO_VICT);
    act("$N is scathed by the brambles which surround you.",
        FALSE, ch, 0, vict, TO_CHAR);
    act("$N is scathed by the brambles which surround $n.",
        FALSE, ch, 0, vict, TO_NOTVICT);

    dam += (int) ((float) Level(ch) * 0.05);
  }
  return dam;
}

/* do_damage */
int  damage(chdata *ch, chdata *victim, int dam, int spellnum, BOOL regardless)
{
   int shock_dam = 0;
   extern struct con_app_type con_app[];

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

   /* You can't damage an immortal! */
   if (IS_IMMORTAL(victim))
      dam = 0;

   if (victim != ch) 
   {
      if (GET_POS(ch) > POS_STUNNED) 
      {
	 if (!(FIGHTING(ch)))
	    set_fighting(ch, victim);

         // if both fighters are NPCs, see if the master of one charmie is in room
         // to attack :)  
	 if (IS_NPC(ch) && IS_NPC(victim) && victim->master && CHARMED(victim) && SAME_ROOM(victim->master, ch) && !number(0, 10))
 	 {
	    if (FIGHTING(ch))
	       stop_fighting(ch);
	    hit(ch, victim->master, TYPE_UNDEFINED, FALSE);
	    return CHAR_OK;
	 }
      }

      if (GET_POS(victim) > POS_STUNNED)
      {
	 if (!FIGHTING(victim))
	    set_fighting(victim, ch);

         // immortal memory safety check
	 if (MOB_FLAGGED(victim, MOB_MEMORY) && IS_PC(ch) && (GET_LEVEL(ch) < LEV_IMM))
	    remember(victim, ch);

         if (PC_MOUNTING(ch))
      	   do_dismount(ch, "", 0, 0);  /* for now dismount in combat */

	 if (IS_NPC(ch))
	   GET_POS(ch) = POS_FIGHTING;
      }
   }

   // Added undead check. 08/14/98 -callahan
   if (victim->master == ch) {
     if (MOB_CLASS_FLAGGED(ch, MOB_UNDEAD) &&
         affected_by_spell(ch, SPELL_ANIMATE_DEAD))
       raw_kill(victim);
     else
       stop_follower(victim);
   }

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

   // If the victim is affected by any spells that take effect upon
   // contact, initiate them now
   // Added to facilitate wall of fire and brambles 05/03/98 -callahan
   dam += spell_touch_affects(victim, ch);

   dam = adjust_damage(ch, victim, dam, &spellnum, regardless, &shock_dam);

   /* does character go into shock because of blow? -roa based on con */
   if (shock_dam && 
       shock_dam > (int) ((float)GET_MAX_HIT(victim) *
		          ((float)con_app[GET_CON(victim)].shock / 100.0)))
     send_shock_message(ch, victim);

   if (GET_LEVEL(ch) >= LEV_GOD)
   {
     sprintf(buf, "[%d points of damage]\n\r",dam);
     S2C();
   }

   if (ch != victim && IS_AFFECTED2(victim, AFF2_WOLF))
     do_shaman_wolf(dam, ch, victim);

   if ((ch != victim) && dam && !regardless)
      gain_exp(ch, GET_LEVEL(victim) + dam);

   update_pos(victim);

   // message sending related to combat and positions in fightmes.c
   send_combat_messages(dam, ch, victim, spellnum);

   send_position_messages(dam, ch, victim);

   if (ch != victim && IS_PC(victim) && !(victim->desc)) 
     linkless_flee(victim);

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

   if (GET_POS(victim) == POS_DEAD) 
   {  
     do_death_procs(ch, victim);
     return CHAR_DIED;
   } else
    return CHAR_OK;
}

int successful_parry(chdata *victim)
{
  int percent;

  if (IS_HELD(victim) || !AWAKE(victim) || IN_A_RITUAL(victim))
    return FALSE;

  percent = 5 + (3 * (IS_THIEF(victim)));

  // if tumbling, double chance to parry  4/21/98 -jtrhone
  if (affected_by_spell(victim, SKILL_TUMBLE))
    percent *= 2;

  if (IS_PC(victim) || IS_AFFECTED(victim, AFF_PARRY) )   
    if ((GET_SKILL(victim, SKILL_PARRY) > number(1,100) ||
          IS_NPC(victim)) && (number(1,100) < percent)) 
     {
       do_parry(victim, "", 0, 0);
       return TRUE;
     }

  return FALSE;
}

int successful_dodge(chdata *victim)
{
  int percent;

  if (IS_HELD(victim) || !AWAKE(victim) || IN_A_RITUAL(victim))
    return FALSE;

  percent = 5 + (3 * (IS_THIEF(victim)));

  if (IS_PC(victim) || IS_AFFECTED(victim, AFF_DODGE))
    if ((GET_SKILL(victim, SKILL_DODGE) > number(1,100) ||
         IS_NPC(victim)) && (number(1,100) < percent)) 
    {
      do_dodge(victim, "", 0, 0);
      return TRUE;
    }

  return FALSE;
}

int successful_block(chdata *victim)
{
  int percent;

  if (IS_HELD(victim) || !AWAKE(victim) || IN_A_RITUAL(victim))
    return FALSE;

  percent = 5 + (3 * (IS_THIEF(victim)));

  // conform to slot revamp 5/28/98 -jtrhone
  if (IS_PC(victim) && EQ(victim, W_HOLD) && IS_ARMOR(EQ(victim, W_HOLD)))
    if (GET_SKILL(victim, SKILL_SHIELDBLOCK) > number(1,100) &&
        number(1,100) < percent)
    {
      do_shieldblock(victim, "", 0, 0);
      return TRUE;
    }

  return FALSE;
}

/* weapon type + base (types range from roughly 0 - 10) */
int get_weapon_type(obdata *weap)
{
  return (weap->value[3] + TYPE_HIT);
}

int get_thaco(chdata *ch)
{
  float class_thacos[NUM_CLASSES + 1] =
  {
    0.0,	// class 0 is undefined
    0.18,	// mage
    0.20,	// cleric
    0.23,	// thief 
    0.28,	// warrior
    0.24,	// shaman
    0.26,	// ranger
    0.20,	// bard
    0.25,	// warlock
    0.27	// monk
  };

  if (IS_NPC(ch)) return 20;
  if (IS_IMMORTAL(ch)) return 0;

  return (20 - (int) (class_thacos[(int) GET_CLASS(ch)] * GET_LEVEL(ch)) );
}

// modular function to determine if one player hits another
// uses thaco/ac/etc etc etc etc return TRUE if yep
// updated to check some CH_ flags  4/21/98 -jtrhone
int successful_hit(chdata *ch, chdata *victim)
{
  int victim_ac, calc_thaco;
  byte diceroll;
  extern struct str_app_type str_app[];
  extern struct dex_app_type dex_app[];

  if (!AWAKE(victim) || IS_HELD(victim))
    return TRUE;

  diceroll = number(1, 20);
  if (diceroll == 20) return TRUE;	// always hit on 20 roll
  if (diceroll == 1) return FALSE;	// always miss on 1 roll

  calc_thaco  = get_thaco(ch);
  calc_thaco -= str_app[TRUE_STRENGTH(ch)].tohit;
  calc_thaco -= GET_HITROLL(ch);

  victim_ac  = GET_AC(victim) / 10;
  victim_ac += dex_app[GET_DEX(victim)].defensive;

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

  if (IS_GOOD(ch) && IS_AFFECTED(victim, AFF_PROTECT_GOOD))
    victim_ac -= 10;

  // updated to look at mob class undead as well  4/26/98 -jtrhone
  if (affected_by_spell(victim, SPELL_PROTECT_UNDEAD) &&
      (SPC_FLAGGED(ch, SPC_UNDEAD) || MOB_CLASS_FLAGGED(ch, MOB_UNDEAD)))
    victim_ac -= 10;

  if (IS_AFFECTED2(victim, AFF2_ILLUMINATE)) /* easier to hit */
    victim_ac += GET_LEVEL(ch)/2;

  // if hamstrung... easier to hit... 4/26/98 -jtrhone
  if (affected_by_spell(victim, SKILL_HAMSTRING))
    victim_ac += GET_LEVEL(ch)/2;

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

  if ((calc_thaco - diceroll) > victim_ac) 
    return FALSE;	// MISSED
  else
  // if vict is about to sidestep, roll ch DEX against it
  // if ch makes dex roll, nullify sidestep  4/21/98 -jtrhone
  if (CHAR_FLAGGED(victim, CH_SIDESTEP))
  {
    // if ch fails the roll, then ch will miss no matter what
    if ((GET_DEX(ch)-13) < number(1, 10))
      return FALSE;
    else
    {
      REMOVE_BIT(CHAR_FLAGS(victim), CH_SIDESTEP);
      act("$n avoids your sidestep!", FALSE, ch, 0, victim, TO_VICT);
      act("You avoid $N's sidestep!", FALSE, ch, 0, victim, TO_CHAR);
      act("$n avoid $N's sidestep!", FALSE, ch, 0, victim, TO_NOTVICT);
      return TRUE;	// HIT
    }
  }
  else
    return TRUE;	// HIT
}

/* do_hit  char hits another */
// updated return type to let us know when victim dies...
int hit(chdata *ch, chdata *victim, int type, BOOL dual) 
{
  obdata *wielded = 0;
  obdata *held = 0;
  int	w_type, h_type = 0;
  int	dam = 0;
  int  hdam = 0;
  byte isthief = FALSE;
  extern struct str_app_type str_app[];

  if (!SAME_ROOM(ch, victim)) 
  {
     log("SYSERR: NOT SAME ROOM WHEN FIGHTING!");
     if (FIGHTING(ch)) stop_fighting(ch);
     if (FIGHTING(victim)) stop_fighting(victim);
     return CHAR_OK;
  }

  if (IS_PC(ch) && SUMMONED(victim) && SUMMONER(victim) == GET_IDNUM(ch))
  {
    REMOVE_BIT(CHAR_FLAGS(victim), CH_SUMMONED);
    SUMMONER(victim) = -1;
  }

  if (IS_PC(ch) && IN_A_RITUAL(ch)) 
    return CHAR_OK;   /* cant attack in ritual */

  if (!check_mortal_combat(ch, victim))
  {
     if (FIGHTING(ch)) stop_fighting(ch);
     if (FIGHTING(victim)) stop_fighting(victim);
     return CHAR_OK;
  }

  if (!check_truce(ch, victim) || IS_HELD(ch)) 
    return CHAR_OK;  /* cant hit truced players, cant hit if held */

  // if they are brewing, stop them here... 4/19/98 -jtrhone
  affect_from_char(ch, SKILL_BREW);

  // if they have the loseattack flag, remove it and return  4/26/98 -jtrhone
  if (CHAR_FLAGGED(ch, CH_LOSEATTACK))
  {
    REMOVE_BIT(CHAR_FLAGS(ch), CH_LOSEATTACK);
    return CHAR_OK;
  }

  if (successful_dodge(victim))
    return CHAR_OK;
  if (successful_parry(victim))
    return CHAR_OK;
  if (successful_block(victim))
    return CHAR_OK;

  if ((held = EQ(ch, W_HOLD)) && IS_WEAPON(held) && IS_THIEF(ch))
  {
    isthief = TRUE;
    h_type = get_weapon_type(held);
  }

  if ((wielded = EQ(ch, W_WIELD)) && IS_WEAPON(wielded))
    w_type = get_weapon_type(wielded);
  else 
    w_type = TYPE_HIT;

  // if ch is sidestepping, next hit is backstab  4/21/98 -jtrhone
  // must remove this before return...
  if (CHAR_FLAGGED(ch, CH_SIDESTEP))
    type = SKILL_BACKSTAB;

  // similar, types to backstab, however, lasts entire round,
  // not just one hit... 4/22/98 -jtrhone
  if (CHAR_FLAGGED(ch, CH_DISEMBOWEL))
    type = SKILL_BACKSTAB;

  if (!successful_hit(ch, victim))
  {
    REMOVE_BIT(CHAR_FLAGS(ch), CH_SIDESTEP);

    if (type == SKILL_BACKSTAB)
      damage(ch, victim, 0, SKILL_BACKSTAB, FALSE);
    else
      damage(ch, victim, 0, w_type, FALSE);
  } 
  else  // or else we HIT 
  {
    REMOVE_BIT(CHAR_FLAGS(ch), CH_SIDESTEP);

    dam  = str_app[TRUE_STRENGTH(ch)].todam;
    dam += GET_DAMROLL(ch);
    if (isthief)
    {
      hdam  = str_app[TRUE_STRENGTH(ch)].todam;
      hdam += GET_DAMROLL(ch);
    }

    if (!wielded) 
    {
	 if (IS_NPC(ch))
	    dam += dice(ch->npc_specials.damnodice, ch->npc_specials.damsizedice);
	 else 
         {
	   if (IS_AFFECTED2(ch, AFF2_CLAWS))  /* if cougar claws 11d3 */
	     dam += dice(11, 3);
	   else
	   if (IS_NAT_MONK(ch))
	    dam += dice(MAX(1, (GET_LEVEL(ch) / 10)), 4); /* 1d4 per 10 levels */
	   else
	    dam += number(0, 2);  /* Max. 2 dam with bare hands non monk*/
         }
    } 
    else
    {
	 damage_object(wielded, ch);
	 dam += dice(wielded->value[1], wielded->value[2]);
         if (isthief)
         {
  	   damage_object(held, ch);
   	   hdam += dice(held->value[1], held->value[2]);
	 }
    }

    if (GET_POS(victim) < POS_FIGHTING)
      dam *= 1 + (POS_FIGHTING - GET_POS(victim)) / 3;

    if (isthief && GET_POS(victim) < POS_FIGHTING)
      hdam *= 1 + (POS_FIGHTING - GET_POS(victim)) / 3;
      
    /* If the char was stun touched, multiply the dam */
    if (CHAR_FLAGGED(victim, CH_STUNTOUCHED))
    {
	 REMOVE_BIT(CHAR_FLAGS(victim), CH_STUNTOUCHED);
         dam = (int) (dam * 1.66);
         if (isthief) 
           hdam = (int) (hdam * 1.66);
    }

    // If the the attacker is in lowstrike mode, multiply the dam
    // 4/19/98 -jtrhone
    if (CHAR_FLAGGED(ch, CH_LOWSTRIKE))
    {
	 REMOVE_BIT(CHAR_FLAGS(ch), CH_LOWSTRIKE);
         dam = (int) (dam * 1.66);
         if (isthief) 
           hdam = (int) (hdam * 1.66);
         act("%B%1OOOF!  $n lowstrikes you!%0", FALSE, ch, 0, victim, TO_VICT);
    }

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

    // if either weapon is poisoned, apply poison vs save and
    // and decrement poison on object  4/19/98 -jtrhone
    if (wielded && wielded->poisoned)
    {
        (*spell_info[SPELL_POISON].spell_pointer) (ch, "", victim, NULL);
        if (--wielded->poison_duration <= 0)
        {
          wielded->poisoned = FALSE;
          act("The poison on $p has dried up.", FALSE, ch, wielded, 0, TO_CHAR);
        }
    }

    if (isthief && held && held->poisoned)
    {
        (*spell_info[SPELL_POISON].spell_pointer) (ch, "", victim, NULL);
        if (--held->poison_duration <= 0)
        {
          held->poisoned = FALSE;
          act("The poison on $p has dried up.", FALSE, ch, held, 0, TO_CHAR);
        }
    }

    if (type == SKILL_BACKSTAB) 
    {
	 dam *= MAX(1, (GET_LEVEL(ch) / 10));  /* multiplier lev/10 */
	 if (damage(ch, victim, dam, SKILL_BACKSTAB, FALSE) == CHAR_DIED)
	   return CHAR_DIED;
    }
    else
    if (IS_AFFECTED2(ch, AFF2_CLAWS))
    {
       if (damage(ch, victim, dam, SKILL_CLAWS, FALSE) == CHAR_DIED)
	   return CHAR_DIED;
    }
    else 
    {
	 if (damage(ch, victim, dam, w_type, FALSE) == CHAR_DIED)
	   return CHAR_DIED;
    }

    if (isthief && dual && FIGHTING(ch) && GET_POS(FIGHTING(ch)) > POS_DEAD &&
        GET_SKILL(ch, SKILL_DUAL) > number(1, 100)) 
      if (damage(ch, victim, hdam, h_type, FALSE) == CHAR_DIED)
	 return CHAR_DIED;

    if (GET_POS(victim) == POS_SITTING)
      do_stand(victim, "", 0, 0);
  }
  
  return CHAR_OK;
}

void perform_whirlwind(chdata *ch)
{
    chdata *t, *next_t;
    int n;

    if (IN_NOWHERE(ch)) return;

    if (IS_NPC(ch))
     return;

    for (n = 0, t = world[ch->in_room].people; t; t = next_t)
    {
     next_t = t->next_in_room;
     if (IS_NPC(t) && (can_see(ch, t) || FIGHTING(t)))
     {
      if (!n)
      {
	act("$n WHIRLS around in blurred movements!",FALSE, ch, 0, 0, TO_ROOM);
	act("You WHIRL around in blurred movements!",FALSE, ch, 0, 0, TO_CHAR);
      }    
      n++;
      hit(ch, t, TYPE_UNDEFINED, FALSE);
     }
    }

    if (!n)
    {
      act("$n swings wildly at the air.", FALSE, ch, 0, 0, TO_ROOM);
      act("You swing wildly at the air.", FALSE, ch, 0, 0, TO_CHAR);
    }
    
    ch->pc_specials->whirlwind--;
}

// enhanced whirlwind,  3/27/98 -jtrhone
void perform_cyclone(chdata *ch)
{
    chdata *t, *next_t;
    int n;

    if (IN_NOWHERE(ch)) return;

    if (IS_NPC(ch))
     return;

    for (n = 0, t = world[ch->in_room].people; t; t = next_t)
    {
     next_t = t->next_in_room;
     if (IS_NPC(t) && (can_see(ch, t) || FIGHTING(t)))
     {
      if (!n)  // first time thru, send these...
      {
	act("You WHIRL around like a %Bcyclone%0!",FALSE, ch, 0, 0, TO_CHAR);
	act("$n WHIRLS around like a %Bcyclone%0!",FALSE, ch, 0, 0, TO_ROOM);
      }    
      n++;
   
      // two hits in cyclone...
      hit(ch, t, TYPE_UNDEFINED, FALSE);
      hit(ch, t, TYPE_UNDEFINED, FALSE);
     }
    }

    if (!n)
    {
      act("$n swings wildly at the air.", FALSE, ch, 0, 0, TO_ROOM);
      act("You swing wildly at the air.", FALSE, ch, 0, 0, TO_CHAR);
    }
    
    ch->pc_specials->cyclone--;
}

// use this function to see if ch can multi attack based on affectuals
BOOL no_extra_attacks(chdata *ch)
{
  int res;

  if (CHAR_FLAGGED(ch, CH_ONEATTACK))
  {
    REMOVE_BIT(CHAR_FLAGS(ch), CH_ONEATTACK);
    return TRUE;
  }

  if (affected_by_spell(ch, SPELL_SONG_BOG))
  {
    act("Your actions feel slow and sluggish!", FALSE, ch, 0, 0, TO_CHAR);
    act("$n's actions look slow and sluggish!", FALSE, ch, 0, 0, TO_ROOM);
    return TRUE;
  }

  if (affected_by_spell(ch, SKILL_CRIPPLE))
  {
    act("You are in severe pain!", FALSE, ch, 0, 0, TO_CHAR);
    act("$n seems to be in severe pain!", FALSE, ch, 0, 0, TO_ROOM);
    return TRUE;
  }

  if (affected_by_spell(ch, SPELL_FOREST_EMBRACE)) {
    act("The grip of the vines hinders your movements.",
        FALSE, ch, 0, 0, TO_CHAR);
    act("$n's movements seem hindered by the vines which bind $m.",
        FALSE, ch, 0, 0, TO_ROOM);
    return TRUE;
  }

  // added rangers entangle, must decrement and check affect here!  3/27/98 -jtrhone
  res = decrement_affect(ch, SPELL_ENTANGLE);
  switch (res) {
    case AFFECT_DECREMENTED:
      act("You are entangled!", FALSE, ch, 0, 0, TO_CHAR);
      act("$n seems to be entangled!", FALSE, ch, 0, 0, TO_ROOM);
      return TRUE;

    case AFFECT_REMOVED:
      act("You break free of the entanglement!", FALSE, ch, 0, 0, TO_CHAR);
      act("$n breaks free of the entanglement!", FALSE, ch, 0, 0, TO_ROOM);
      return TRUE;

    case AFFECT_NOTFOUND:
    default: break;
  }

  return FALSE;		// ch attacks normally...
}

void do_combat_songs(chdata *ch, chdata *vict)
{
  switch (SINGING(ch))
  {
   case 0: break;
   case 10:  /* harsh dissonance */
     if (GET_MANA(ch) < 2)
     {
	send_to_char("You're out of mana!\n\r",ch);
	do_finish(ch, " song", 0, 0);
	break;
     }
     GET_MANA(ch) -= 2;
     GET_HIT(FIGHTING(ch)) -= GET_LEVEL(ch)/2;
     GET_HIT(ch) = MAX(1, GET_HIT(ch));
     act("$N's song forces you to kneel in pain!", FALSE, FIGHTING(ch), 0, ch, TO_CHAR);
     act("Your song forces $N to kneel in pain!", FALSE, ch, 0, FIGHTING(ch), TO_CHAR);
     act("$n's song forces $N to kneel in pain!", FALSE, ch, 0, FIGHTING(ch), TO_ROOM);
     update_pos(ch);
     break;

   case 23:  /* tortured soul */
     if (GET_MANA(ch) < 5)
     {
	send_to_char("You're out of mana!\n\r",ch);
	do_finish(ch, " song", 0, 0);
	break;
     }
     GET_MANA(ch) -= 5;
     GET_HIT(FIGHTING(ch)) -= GET_LEVEL(ch);
     GET_HIT(ch) = MAX(1, GET_HIT(ch));
     act("$N's song tortures your soul!", FALSE, FIGHTING(ch), 0, ch, TO_CHAR);
     act("Your song tortures $N's soul!", FALSE, ch, 0, FIGHTING(ch), TO_CHAR);
     act("$n's song tortures $N's soul!", FALSE, ch, 0, FIGHTING(ch), TO_ROOM);
     update_pos(ch);
     break;

   default: break;
  } 
}

void do_combat_tunes(chdata *ch, chdata *vict)
{
  switch (PLAYING(ch))
  {
   case 0: break;

   case 10:  /* harsh dissonance */
     if (GET_MANA(ch) < 2)
     {
	send_to_char("You're out of mana!\n\r",ch);
	do_finish(ch, " tune", 0, 0);
	break;
     }
     GET_MANA(ch) -= 2;
     GET_HIT(FIGHTING(ch)) -= GET_LEVEL(ch)/2;
     GET_HIT(ch) = MAX(1, GET_HIT(ch));
     act("$N's tune forces you to kneel in pain!", FALSE, FIGHTING(ch), 0, ch, TO_CHAR);
     act("Your tune forces $N to kneel in pain!", FALSE, ch, 0, FIGHTING(ch), TO_CHAR);
     act("$n's tune forces $N to kneel in pain!", FALSE, ch, 0, FIGHTING(ch), TO_ROOM);
     update_pos(ch);
     break;

   case 23:  /* tortured soul */
     if (GET_MANA(ch) < 5)
     {
	send_to_char("You're out of mana!\n\r",ch);
	do_finish(ch, " tune", 0, 0);
	break;
     }
     GET_MANA(ch) -= 5;
     GET_HIT(FIGHTING(ch)) -= GET_LEVEL(ch);
     GET_HIT(ch) = MAX(1, GET_HIT(ch));
     act("$N's tune tortures your soul!", FALSE, FIGHTING(ch), 0, ch, TO_CHAR);
     act("Your tune tortures $N's soul!", FALSE, ch, 0, FIGHTING(ch), TO_CHAR);
     act("$n's tune tortures $N's soul!", FALSE, ch, 0, FIGHTING(ch), TO_ROOM);
     update_pos(ch);
     break;

   default: break;
  } 
}

// things that get removed or stopped after a round of combat for ch
// 4/22/98 -jtrhone
// remove brambles and wall fire... corrected infinite loop 5/7/98 -jtrhone
void end_combat_round(chdata *ch)
{
  chdata *person;
  AffType *af, *next_af;

  // yank the DISEMBOWEL bit
  REMOVE_BIT(CHAR_FLAGS(ch), CH_DISEMBOWEL);

  // any per round affects to be yanked?

  // Get rid of walls if no more attackers. 05/05/98 -callahan
  if (FIGHTING(ch))
    return;

  CharsInRoom(ch, person) {
    if (FIGHTING(person) == ch)
      return;
    else
      continue;
  }
    
  for (af = ch->affected; af; af = next_af) {
    next_af = af->next;

    if (af->type == SPELL_WALL_FIRE || 
        af->type == SPELL_WALL_BRAMBLES) {
      if (spell_info[af->type].wear_off)
        send_to_char(tprintf("%s\r\n", spell_info[af->type].wear_off), ch);

      affect_remove(ch, af);
    }
  }
}

// take care of combat, called every PULSE_VIOLENCE (roughly 2 seconds)
void	perform_violence(void)
{
   void diag_char_to_char(chdata *i, chdata *ch);
   chdata *ch, *vict;
   obdata *wielded;
   int spell = -1;
   int i;

   for (ch = combat_list; ch; ch = combat_next_dude) 
   {
     combat_next_dude = ch->next_fighting;
     assert(FIGHTING(ch));

     // try to remove CH_SUBDUED via DEX roll
     if (CHAR_FLAGGED(ch, CH_SUBDUED))
       if (GET_DEX(ch) > number(3, 18))
         REMOVE_BIT(CHAR_FLAGS(ch), CH_SUBDUED);

     // check affects that last # of rounds... 4/18/98 -jtrhone
     check_per_round_affects(ch);

     if (!VWAIT(ch))
     {
        if (!AWAKE(ch))
        {
  	  if (FIGHTING(ch))
	   stop_fighting(ch);
          continue;
        }

        if (IS_PC(ch) && ch->pc_specials->whirlwind > 0)
	    perform_whirlwind(ch);

        if(!FIGHTING(ch)) 
        {
          end_combat_round(ch);
          continue;
        }

        if (SAME_ROOM(ch, FIGHTING(ch)))
        {
          if((wielded = EQ(ch, W_WIELD)) && (IS_WEAPON(wielded)) && OBJ_FLAGGED(wielded, ITEM_SPELLEQ)) 
	  {
	   spell = wielded->eqspell;

           if(spell >= 0 && !number(0, 3))  /* roughly 25% */
           {
            /* once every 5 or so rounds, launch a spell - SL */
            act("$p shivers mightily in your hands!", TRUE, ch, wielded, NULL, TO_CHAR);
            act("$p shivers mightily in the hands of $n!", TRUE, ch, wielded, NULL, TO_ROOM);

            // if evil weapon and violent spell, swap ch and vict
            if (SPELL_FLAGGED(spell, S_VIOLENT) && !OBJ_FLAGGED(wielded, ITEM_EVIL))
              vict = FIGHTING(ch);
            else
              vict = ch;

	    do_cast_spell(spell, ch, NULL, SPELL_TYPE_SPELL, vict, NULL);
           }
         }  /* end if weapon && spelleq */

         // we must continually check for death...
         if(!FIGHTING(ch))
         {
           end_combat_round(ch);
           continue;
         }

	 if (CHAR_FLAGGED(ch, CH_REDIRECTED))
	 {
	   act("$n's slips and strikes $mself!", FALSE, ch, 0, 0, TO_ROOM);
           // minor bug fix if ch kills himself we were still removing a bit...  4/22/98 -jtrhone
 	   if (hit(ch, ch, TYPE_UNDEFINED, TRUE) == CHAR_DIED)
             continue;

	   REMOVE_BIT(CHAR_FLAGS(ch), CH_REDIRECTED);
	   continue;	   
	 }

         // if hamstrung... set one attack loss now  4/26/98 -jtrhone
	 if (CHAR_FLAGGED(ch, CH_HAMSTRUNG))
           SET_BIT(CHAR_FLAGS(ch), CH_LOSEATTACK);
        
         // the NORMAL hit here
	 if (hit(ch, FIGHTING(ch), TYPE_UNDEFINED, TRUE) == CHAR_DIED)
         {
           end_combat_round(ch);
           continue;
         }

	 // check extra attack reduction affects...
	 if (no_extra_attacks(ch))
         {
           end_combat_round(ch);
           continue;
         }

         // Modified for Druid spell 'forest warrior' 06/01/98 -callahan
         if (((IS_WARRIOR(ch) || IS_RANGER(ch) || IS_THIEF(ch) || IS_MONK(ch) ||
	      IS_SHAMAN(ch) || IS_BARD(ch)) && GET_SKILL(ch, SKILL_DOUBLE)) ||
             GET_SKILL(ch, SPELL_FOREST_WARRIOR))
	   do_double(ch, "", 0, 0);

         if(!FIGHTING(ch))
         {
           end_combat_round(ch);
           continue;
         }

         if ((IS_WARRIOR(ch) || IS_MONK(ch) || IS_RANGER(ch)) && GET_SKILL(ch, SKILL_TRIPLE)) 
	   do_triple(ch, "", 0, 0); 

         if(!FIGHTING(ch))
         {
           end_combat_round(ch);
           continue;
         }

         // check for multiples from thief bladedance  4/21/98 -jtrhone
         if (CHAR_FLAGGED(ch, CH_BLADEDANCE))
         {
           REMOVE_BIT(CHAR_FLAGS(ch), CH_BLADEDANCE);
           SET_BIT(CHAR_FLAGS(ch), CH_ONEATTACK);

           act("%BYou bladedance around $N!%0", FALSE, ch, 0, FIGHTING(ch), TO_CHAR);
           act("%B$n bladedances around you!%0", TRUE, ch, 0, FIGHTING(ch), TO_VICT);
           act("%B$n bladedances around $N!%0", TRUE,  ch, 0, FIGHTING(ch), TO_NOTVICT);

           if (hit(ch, FIGHTING(ch), TYPE_UNDEFINED, FALSE) == CHAR_DIED)
           {
             end_combat_round(ch);
             continue;
           }

           if(!FIGHTING(ch))
           {
             end_combat_round(ch);
             continue;
           }
          
           if (hit(ch, FIGHTING(ch), TYPE_UNDEFINED, FALSE) == CHAR_DIED)
           {
             end_combat_round(ch);
             continue;
           }

           if(!FIGHTING(ch))
           {
             end_combat_round(ch);
             continue;
           }
         }

	 /* BARDs check the singing and playing stuff RoA*/
 	 if (IS_PC(ch))
           do_combat_songs(ch, FIGHTING(ch));

         if(!FIGHTING(ch))
         {
           end_combat_round(ch);
           continue;
         }

 	 if (IS_PC(ch))
           do_combat_tunes(ch, vict);

         if(!FIGHTING(ch))
         {
           end_combat_round(ch);
           continue;
         }

         /* If a character is affected by haste, give him an extra attack 50% of the time */
         if(IS_AFFECTED(ch, AFF_HASTE) && number(0, 1)) 
           hit(ch, FIGHTING(ch), TYPE_UNDEFINED, FALSE);

         if(!FIGHTING(ch))
         {
           end_combat_round(ch);
           continue;
         }
 
         /* MH0 - 1 extra attack 90% of the time */
         if(MOB_FLAGGED(ch, MOB_MH0) && number(0, 9)) 
	 {
	   if (IS_AFFECTED(FIGHTING(ch), AFF_GROUP) && MOB_FLAGGED(ch, MOB_SPLITATTACK))
	     vict = random_group_member(FIGHTING(ch));
	   else
	     vict = FIGHTING(ch);
           hit(ch, vict, TYPE_UNDEFINED, FALSE);
	 }

         if(!FIGHTING(ch))
         {
           end_combat_round(ch);
           continue;
         }
 
         /* MH1 - 2 extra attacks, each with 90% */
         if(MOB_FLAGGED(ch, MOB_MH1)) 
           for(i = 0; i < 2; i++) 
             if(number(0, 9)) 
	     {
               if(!FIGHTING(ch)) break;
	       if (IS_AFFECTED(FIGHTING(ch), AFF_GROUP) && MOB_FLAGGED(ch, MOB_SPLITATTACK))
	         vict = random_group_member(FIGHTING(ch));
	       else
	         vict = FIGHTING(ch);
               hit(ch, vict, TYPE_UNDEFINED, FALSE);
	     }

         if(!FIGHTING(ch))
         {
           end_combat_round(ch);
           continue;
         }
 
         /* MH2 - 4 extra attacks, each with 90% */
         if(MOB_FLAGGED(ch, MOB_MH2)) 
           for(i = 0; i < 4; i++) 
             if(number(0, 9)) 
	     {
               if(!FIGHTING(ch)) break;
	       if (IS_AFFECTED(FIGHTING(ch), AFF_GROUP) && MOB_FLAGGED(ch, MOB_SPLITATTACK))
	         vict = random_group_member(FIGHTING(ch));
	       else
	         vict = FIGHTING(ch);
               hit(ch, vict, TYPE_UNDEFINED, FALSE);
	     }
       
         if(!FIGHTING(ch))
         {
           end_combat_round(ch);
           continue;
         }
 
         /* showing the condition of enemy here JRhone*/
         if (FIGHTING(ch))
	   diag_char_to_char(FIGHTING(ch), ch);

         // this round is over for this char  4/22/98 -jtrhone
         end_combat_round(ch);
      } 
      else /* Not in same room */
        stop_fighting(ch);
     }
     else  /* they have a violence wait */
       VWAIT(ch)--;

     if (!FIGHTING(ch))
       VWAIT(ch) = 0;
   }
}