SmaugWizard/Backup/
SmaugWizard/Backup/L/
SmaugWizard/Boards/
SmaugWizard/Building/
SmaugWizard/Corpses/
SmaugWizard/Councils/
SmaugWizard/Deity/
SmaugWizard/Gods/
SmaugWizard/MudProgs/
SmaugWizard/Player/L/
SmaugWizard/Src/
SmaugWizard/Src/res/
/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |				*
 * -----------------------------------------------------------|   \\._.//	*
 * SmaugWiz (C) 1998 by Russ Pillsbury (Windows NT version)   |   (0...0)	*
 * -----------------------------------------------------------|    ).:.(	*
 * SMAUG (C) 1994, 1995, 1996 by Derek Snider                 |    {o o}	*
 * -----------------------------------------------------------|   / ' ' \	*
 * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,      |~'~.VxvxV.~'~*
 * Scryn, Swordbearer, Rennard, Tricops, and Gorog.           |				*
 * ------------------------------------------------------------------------ *
 * Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael        *
 * Chastain, Michael Quan, and Mitchell Tse.                                *
 * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,          *
 * Michael Seifert, Hans Henrik Staerfeldt, Tom Madsen, and Katja Nyboe.    *
 * ------------------------------------------------------------------------ *
 *			    Battle & death module									    *
 ****************************************************************************/

#include	"stdafx.h"
#include	"smaug.h"
#include	"SysData.h"
#include	"skill.h"
#include	"mobiles.h"
#include	"objects.h"
#include	"rooms.h"
#include	"deity.h"
#include	"area.h"
#include	"races.h"
#include	"class.h"
#include	"Exits.h"
#include	"SmaugWizDoc.h"
#include	"descriptor.h"
#include	"character.h"
#include	"smaugx.h"

extern CCharacter *	gch_prev;

// Local functions.
void	dam_message (CCharacter *ch, CCharacter *victim, int dam, int dt,
			CObjData* pObj = NULL);
void	death_cry (CCharacter *ch);
void	group_gain (CCharacter *ch, CCharacter *victim);
int		xp_compute (CCharacter *gch, CCharacter *victim);
int		align_compute (CCharacter *gch, CCharacter *victim);
ch_ret	one_hit (CCharacter *ch, CCharacter *victim, int dt);
int		obj_hitroll (CObjData *obj);
BOOL	dual_flip = FALSE;
BOOL	HandleAttackFlags (CCharacter* ch, CCharacter* victim);
BOOL	HandleDefenseFlags (CCharacter* ch, CCharacter* victim);


// Check to see if weapon is poisoned.
BOOL is_wielding_poisoned (CCharacter *ch)
{
	CObjData	*obj;

	if ((obj = get_eq_char (ch, WEAR_WIELD)) && obj->IsPoisoned ())
		return TRUE;

	return FALSE;
}


// hunting, hating and fearing code				-Thoric
BOOL is_hunting (CCharacter *ch, CCharacter *victim)
{
	if (!ch->hunting || ch->hunting->who != victim)
		return FALSE;

	return TRUE;    
}


BOOL is_hating (CCharacter *ch, CCharacter *victim)
{
    if (!ch->hating || ch->hating->who != victim)
      return FALSE;
    
    return TRUE;    
}

BOOL is_fearing (CCharacter *ch, CCharacter *victim)
{
    if (!ch->fearing || ch->fearing->who != victim)
      return FALSE;
    
    return TRUE;    
}


void stop_hunting (CCharacter *ch)
{
	if (ch->hunting) {
		STRFREE (ch->hunting->name);
		delete ch->hunting;
		ch->hunting = NULL;
	}
}


void stop_hating (CCharacter *ch)
{
	if (ch->hating) {
		STRFREE (ch->hating->name);
		delete ch->hating;
		ch->hating = NULL;
	}
}


void stop_fearing (CCharacter *ch)
{
	if (ch->fearing) {
		STRFREE (ch->fearing->name);
		delete ch->fearing;
		ch->fearing = NULL;
	}

}


void start_hunting (CCharacter *ch, CCharacter *victim)
{
	if (ch->hunting)
		stop_hunting (ch);

	ch->hunting = new HHF_DATA;
	ch->hunting->name = QUICKLINK (victim->GetName ());
	ch->hunting->who  = victim;
}


void start_hating (CCharacter *ch, CCharacter *victim)
{
	if (ch->hating)
		stop_hating (ch);

	ch->hating = new HHF_DATA;
	ch->hating->name = QUICKLINK (victim->GetName ());
	ch->hating->who  = victim;
}


void start_fearing (CCharacter *ch, CCharacter *victim)
{
	if (ch->fearing)
		stop_fearing (ch);

	ch->fearing = new HHF_DATA;
	ch->fearing->name = QUICKLINK (victim->GetName ());
	ch->fearing->who = victim;
}


// Get the current armor class for a vampire based on time of day
short VAMP_AC (CCharacter * ch)
{
	if (ch->IsVampire () && ch->IsOutside ()) {
		switch (weather_info.sunlight) {
		  case SUN_DARK:
			return -10;
		  case SUN_RISE:
			return 5;
		  case SUN_LIGHT:
			return 10;
		  case SUN_SET:
			return 2;
		  default:
			return 0;
		}
	}

	return 0;
}


int max_fight (CCharacter *ch)
{
	return 8;
}


// Control the fights going on.
// Called periodically by update_handler.
// Many hours spent fixing bugs in here by Thoric, as noted by residual
// debugging checks.  If you never get any of these error messages again
// in your logs... then you can comment out some of the checks without
// worry.
void violence_update (void)
{
	char		buf [MAX_STRING_LENGTH];
	CCharacter	*ch;
	CCharacter	*lst_ch;
	CCharacter	*victim;
	CCharacter	*rch, *rch_next;
	CTimerData	*timer, *timer_next;
	ch_ret		retcode;
	CSkill		*skill;

	try {
		lst_ch = NULL;
		for (ch = last_char; ch; lst_ch = ch, ch = gch_prev) {
			set_cur_char (ch);

			gch_prev = ch->GetPrev ();

			if (gch_prev && gch_prev->GetNext () != ch) {
				bug ("FATAL: violence_update: %s->GetPrev ()->GetNext () "
					"doesn't point to ch.", ch->GetName ());
				bug ("Short-cutting here");
				ch->SetPrev (NULL);
				gch_prev = NULL;
				sprintf (buf, "%s says, 'Prepare for the worst!'",
					SysData.GetSupremeEntity ());
				do_shout (ch, buf);
			}

			// See if we got a pointer to someone who recently died...
			// if so, either the pointer is bad... or it's a player who
			// "died", and is back at the healer...
			// Since he/she's in the char_list, it's likely to be the later...
			// and should not already be in another fight already
			if (char_died (ch))
				continue;

			// See if we got a pointer to some bad looking data...
			if (! ch->GetInRoom () || ! ch->GetName ()) {
				gpDoc->LogString ("violence_update: bad ch record!   "
					"(Shortcutting.)", LOG_BUG);
				sprintf (buf, "ch: %d  ch->GetInRoom (): %d  ch->GetPrev (): "
					"%d  ch->GetNext (): %d", (int) ch, (int) ch->GetInRoom (),
					(int) ch->GetPrev (), (int) ch->GetNext ());
				gpDoc->LogString (buf, LOG_BUG);
				gpDoc->LogString (lastplayercmd, LOG_BUG);
				if (lst_ch)
					sprintf (buf, "lst_ch: %d  lst_ch->GetPrev (): "
						"%d  lst_ch->GetNext (): %d", (int) lst_ch,
						(int) lst_ch->GetPrev (), (int) lst_ch->GetNext ());
				else
					strcpy (buf, "lst_ch: NULL");
				gpDoc->LogString (buf, LOG_BUG);
				gch_prev = NULL;
				continue;
			}

			// Experience gained during battle deceases as battle drags on
			if (ch->GetFightData ())
				if ((++ch->GetFightData ()->duration % 24) == 0)
					ch->GetFightData ()->xp =
						((ch->GetFightData ()->xp * 9) / 10);


			for (timer = ch->first_timer; timer; timer = timer_next) {
				timer_next = timer->GetNext ();
				if (timer->CountDown () <= 0) {
					if (timer->GetType () == TIMER_ASUPRESSED) {
						if (timer->GetValue () == -1) {
							timer->SetCount (1000);
							continue;
						}
					}
					if (timer->GetType () == TIMER_DO_FUN) {
						int tempsub;

						tempsub = ch->GetSubstate ();
						ch->SetSubstate (timer->GetValue ());
						timer->DoFun (ch, "");
						if (char_died (ch))
							break;
						ch->SetSubstate (tempsub);
					}
					extract_timer (ch, timer);
				}
			}

			if (char_died (ch))
				continue;

			// We need spells that have shorter durations than an hour.
			// So a melee round sounds good to me... -Thoric
			POSITION	pos = ch->m_AffectList.GetHeadPosition ();
			while (pos) {
				CAffectData	*paf = ch->m_AffectList.GetNext (pos);

				if (paf->duration > 0) {
					--paf->duration;
					continue;
				}

				if (paf->duration == 0) {
					CAffectData	*pNext = NULL;
					if (pos)
						pNext = ch->m_AffectList.GetAt (pos);
					if (! pNext || pNext->type != paf->type
					  || pNext->duration > 0) {
						skill = SkillTable.GetValidSkill (paf->type);
						if (paf->type > 0 && skill && skill->ValidOffMsg ()) {
							set_char_color (AT_WEAROFF, ch);
							ch->SendText (skill->GetOffMsg ());
							ch->SendText ("\n\r");
						}
					}
					if (paf->type == gsn_possess) {
						ch->GetDesc ()->m_pCharacter = ch->GetDesc ()->m_pOriginal;
						ch->GetDesc ()->m_pOriginal = NULL;
						ch->GetDesc ()->m_pCharacter->SetDesc (ch->GetDesc ());
						ch->GetDesc ()->m_pCharacter->switched = NULL;
						ch->SetDesc (NULL);
					}
					affect_remove (ch, paf);
				}
			}

			if (char_died (ch))
				continue;

			// check for exits moving players around
//			if ((retcode = pullcheck (ch, pulse)) == rCHAR_DIED
//				|| char_died (ch))
//					continue;

			if ((victim = ch->GetFightWho ()) == NULL || ch->IsParalysed ())
				continue;

			// Let the battle begin!
			retcode = rNONE;

			if (ch->GetInRoom ()->IsSafe ()) {
				sprintf (buf, "violence_update: %s fighting %s in a SAFE room.",
					ch->GetName (), victim->GetName ());
				gpDoc->LogString (buf);
				stop_fighting (ch, TRUE);
			}
			else
				if (ch->IsAwake () && ch->GetInRoom () == victim->GetInRoom ())
					retcode = multi_hit (ch, victim, TYPE_UNDEFINED);
			else
				stop_fighting (ch, FALSE);

			if (char_died (ch))
				continue;

			if (retcode == rCHAR_DIED || (victim = ch->GetFightWho ()) == NULL)
				continue;

			//  Mob triggers
			rprog_rfight_trigger (ch);
			if (char_died (ch))
				continue;
			mprog_hitprcnt_trigger (ch, victim);
			if (char_died (ch))
				continue;
			mprog_fight_trigger (ch, victim);
			if (char_died (ch))
				continue;

			if (ch->IsNpc ()) {
				// NPC special attack flags					-Thoric
				if (ch->HasAttacks ())
					if (! HandleAttackFlags (ch, victim)) then continue;

				// NPC special defense flags				-Thoric
				if (ch->HasDefenses ())
					if (! HandleDefenseFlags (ch, victim)) then continue;
			}

			// Fun for the whole family!
			for (rch = ch->GetInRoom ()->first_person; rch; rch = rch_next) {
				rch_next = rch->GetNextInRoom ();

				// Group Fighting Styles Support:
				// If ch is tanking
				// If rch is using a more aggressive style than ch
				// Then rch is the new tank   -h
				if ((! ch->IsNpc () && ! rch->IsNpc ())
					&& rch != ch
					&& rch->GetFightWho () == ch
					&& ! rch->GetFightWho ()->IsAutonomous ()
					&& rch->GetStyle () < ch->GetStyle ())
						rch->GetFightWho ()->SetFightWho (rch);

				if (rch->IsAwake () && !rch->GetFightData ()) {
					// PC's auto-assist others in their group.
					if (! ch->IsNpc () || ch->IsCharmed ()) {
						if ((!rch->IsNpc () || rch->IsCharmed ())
						  && is_same_group (ch, rch))
							multi_hit (rch, victim, TYPE_UNDEFINED);
						continue;
					}

					// NPC's assist NPC's of same type or 12.5% chance regardless.
					if (rch->IsNpc () && !rch->IsCharmed ()
					  && ! rch->IsNoAssist ()) {
						if (char_died (ch))
							break;
						if (rch->GetMobIndex () == ch->GetMobIndex ()
						  || number_bits (3) == 0) {
							CCharacter	*vch;
							CCharacter	*target;
							int			number;

							target = NULL;
							number = 0;
							vch = ch->GetInRoom ()->first_person;
							for ( ; vch; vch = vch->GetNext ()) {
								if (can_see (rch, vch)
								  && is_same_group (vch, victim)
								  && number_range (0, number) == 0) {
									if (vch->mount && vch->mount == rch)
										target = NULL;
									else {
										target = vch;
										number++;
									}
								}
							}

							if (target)
								multi_hit (rch, target, TYPE_UNDEFINED);
						}
					}
				}
			}
		}
	}
	catch (...) {
		CSwException	ex;
		ex.Printf ("violence_update Exception.");
		throw ex;
	}
}


BOOL HandleAttackFlags (CCharacter* ch, CCharacter* victim)
{
	if (30 + (ch->GetLevel () / 4) < number_percent ()) then return TRUE;

	int		attacktype = -1;
	int		cnt = 0;
	int		retcode;

	for (;;) {
		if (cnt++ > 10) {
			attacktype = -1;
			break;
		}
		attacktype = number_range (7, MAX_ATTACK_TYPE-1);
		if (ch->CanAttack (attacktype))
			break;
	}

	switch (attacktype) {
	  case ATCK_BASH:
		do_bash (ch, "");
		retcode = global_retcode;
		break;
	  case ATCK_STUN:
		do_stun (ch, ""); 
		retcode = global_retcode;
		break;
	  case ATCK_GOUGE:
		do_gouge (ch, "");
		retcode = global_retcode;
		break;
	  case ATCK_FEED:
		do_gouge (ch, "");
		retcode = global_retcode;
		break;
	  case ATCK_DRAIN:
		retcode = spell_energy_drain (SkillTable.Lookup ("energy drain"), ch->GetLevel (), ch, victim);
		break;
	  case ATCK_FIREBREATH:
		retcode = spell_fire_breath (SkillTable.Lookup ("fire breath"), ch->GetLevel (), ch, victim);
		break;
	  case ATCK_FROSTBREATH:
		retcode = spell_frost_breath (SkillTable.Lookup ("frost breath"), ch->GetLevel (), ch, victim);
		break;
	  case ATCK_ACIDBREATH:
		retcode = spell_acid_breath (SkillTable.Lookup ("acid breath"), ch->GetLevel (), ch, victim);
		break;
	  case ATCK_LIGHTNBREATH:
		retcode = spell_lightning_breath (SkillTable.Lookup ("lightning breath"), ch->GetLevel (), ch, victim);
		break;
	  case ATCK_GASBREATH:
		retcode = spell_gas_breath (SkillTable.Lookup ("gas breath"), ch->GetLevel (), ch, victim);
		break;
	  case ATCK_SPIRALBLAST:         
		retcode = spell_spiral_blast (SkillTable.Lookup ("spiral blast"),
		ch->GetLevel (), ch, victim);
		break;
	  case ATCK_POISON:
		retcode = spell_poison (gsn_poison, ch->GetLevel (), ch, victim);
		break;
	  case ATCK_NASTYPOISON:
		/*
		retcode = spell_nasty_poison (SkillTable.Lookup ("nasty poison"), ch->GetLevel (), ch, victim);
		*/
		break;
	  case ATCK_GAZE:
		/*
		retcode = spell_gaze (SkillTable.Lookup ("gaze"), ch->GetLevel (), ch, victim);
		*/
		break;
	  case ATCK_BLINDNESS:
		retcode = spell_blindness (gsn_blindness, ch->GetLevel (), ch, victim);
		break;
	  case ATCK_CAUSESERIOUS:
		retcode = spell_cause_serious (SkillTable.Lookup ("cause serious"), ch->GetLevel (), ch, victim);
		break;
	  case ATCK_EARTHQUAKE:
		retcode = spell_earthquake (SkillTable.Lookup ("earthquake"), ch->GetLevel (), ch, victim);
		break;
	  case ATCK_CAUSECRITICAL:
		retcode = spell_cause_critical (SkillTable.Lookup ("cause critical"), ch->GetLevel (), ch, victim);
		break;
	  case ATCK_CURSE:
		retcode = spell_curse (SkillTable.Lookup ("curse"), ch->GetLevel (), ch, victim);
		break;
	  case ATCK_FLAMESTRIKE:
		retcode = spell_flamestrike (SkillTable.Lookup ("flamestrike"), ch->GetLevel (), ch, victim);
		break;
	  case ATCK_HARM:
		retcode = spell_harm (SkillTable.Lookup ("harm"), ch->GetLevel (), ch, victim);
		break;
	  case ATCK_FIREBALL:
		retcode = spell_fireball (SkillTable.Lookup ("fireball"), ch->GetLevel (), ch, victim);
		break;
	  case ATCK_COLORSPRAY:
		retcode = spell_colour_spray (SkillTable.Lookup ("colour spray"), ch->GetLevel (), ch, victim);
		break;
	  case ATCK_WEAKEN:
		retcode = spell_weaken (SkillTable.Lookup ("weaken"), ch->GetLevel (), ch, victim);
		break;
	}

	if (attacktype != -1 && retcode == rCHAR_DIED || (char_died (ch)))
		return FALSE;

	return TRUE;
}


BOOL HandleDefenseFlags (CCharacter* ch, CCharacter* victim)
{
	if (50 + (ch->GetLevel () / 4) < number_percent ()) then return TRUE;

	int		attacktype = -1;
	int		cnt = 0;
	int		retcode;

	for (;;) {
		if (cnt++ > 10) {
			attacktype = -1;
			break;
		}
		attacktype = number_range (2, MAX_DEFENSE_TYPE-1);
		if (ch->CanDefend (attacktype))
			break;
	}

	switch (attacktype) {
	  case DFND_CURELIGHT:
		act (AT_MAGIC, "$n mutters a few incantations...and looks "
			"a little better.", ch, NULL, NULL, TO_ROOM);
		retcode = spell_smaug (SkillTable.Lookup ("cure light"),
			ch->GetLevel (), ch, ch);
		break;
	  case DFND_CURESERIOUS:
		act (AT_MAGIC, "$n mutters a few incantations...and looks "
			"a bit better.", ch, NULL, NULL, TO_ROOM);
		retcode = spell_smaug (SkillTable.Lookup ("cure serious"),
			ch->GetLevel (), ch, ch);
		break;
	  case DFND_CURECRITICAL:
		act (AT_MAGIC, "$n mutters a few incantations...and looks "
			"a bit healthier.", ch, NULL, NULL, TO_ROOM);
		retcode = spell_smaug (SkillTable.Lookup ("cure critical"),
			ch->GetLevel (), ch, ch);
		break;
	  case DFND_DISPELMAGIC:
		act (AT_MAGIC, "$n mutters a few incantations...and "
			"waves $s arms about.", ch, NULL, NULL, TO_ROOM);
		retcode = spell_dispel_magic (SkillTable.Lookup (
			"dispel magic"), ch->GetLevel (), ch, victim);
		break;
	  case DFND_DISPELEVIL:
		act (AT_MAGIC, "$n mutters a few incantations...and "
			"waves $s arms about.", ch, NULL, NULL, TO_ROOM);
		retcode = spell_dispel_evil (SkillTable.Lookup (
			"dispel evil"), ch->GetLevel (), ch, victim);
		break;
	  case DFND_SHOCKSHIELD:
		if (! ch->HasShockShield ()) {
			act (AT_MAGIC, "$n utters a few incantations...", ch,
				NULL, NULL, TO_ROOM);
			retcode = spell_smaug (SkillTable.Lookup ("shockshield"),
				ch->GetLevel (), ch, ch);
		}
		else retcode = rNONE;
		break;
	  case DFND_FIRESHIELD:
		if (! ch->HasFireShield ()) {
			act (AT_MAGIC, "$n utters a few incantations...", ch,
				NULL, NULL, TO_ROOM);
			retcode = spell_smaug (SkillTable.Lookup ("fireshield"),
				ch->GetLevel (), ch, ch);
		}
		else retcode = rNONE;
		break;
	  case DFND_ICESHIELD:
		if (! ch->HasIceShield ()) {
			act (AT_MAGIC, "$n utters a few incantations...", ch,
				NULL, NULL, TO_ROOM);
			retcode = spell_smaug (SkillTable.Lookup ("iceshield"),
				ch->GetLevel (), ch, ch);
		}
		else retcode = rNONE;
		break;
	  case DFND_TRUESIGHT:
		if (! ch->HasTrueSight ()) {
			act (AT_MAGIC, "$n utters a few incantations...", ch,
				NULL, NULL, TO_ROOM);
			retcode = spell_smaug (SkillTable.Lookup ("true"),
				ch->GetLevel (), ch, ch);
		}
		else retcode = rNONE;
		break;
	  case DFND_SANCTUARY:
		if (! victim->HasSanctuary ()) {
			act (AT_MAGIC, "$n mutters a few incantations...",
				ch, NULL, NULL, TO_ROOM);
			retcode = spell_smaug (SkillTable.Lookup ("sanctuary"),
				ch->GetLevel (), ch, ch);
		}
		else retcode = rNONE;
		break;
	}

	if (attacktype != -1 && retcode == rCHAR_DIED || (char_died (ch)))
		return FALSE;

	return TRUE;
}


// Do one group of attacks.
ch_ret multi_hit (CCharacter *ch, CCharacter *victim, int dt)
{
    int     chance;
    int	    dual_bonus;
    ch_ret  retcode;

    /* add timer if player is attacking another player */
    if (!ch->IsNpc () && !victim->IsNpc ())
      add_timer (ch, TIMER_RECENTFIGHT, 20, NULL, 0);

    if (!ch->IsNpc () && ch->IsNice () && !victim->IsNpc ())
      return rNONE;

    if ((retcode = one_hit (ch, victim, dt)) != rNONE)
      return retcode;

    if (ch->GetFightWho () != victim || dt == gsn_backstab || dt == gsn_circle)
	return rNONE;
	
    // Very high chance of hitting compared to chance of going berserk
    // 40% or higher is always hit.. don't learn anything here though.
    // -- Altrag
    chance = ch->IsNpc () ? 100 : (ch->GetPcData ()->learned[gsn_berserk]*5/2);
    if (ch->IsBeserk () && number_percent () < chance)
      if ((retcode = one_hit (ch, victim, dt)) != rNONE ||
            ch->GetFightWho () != victim)
        return retcode;

    if (get_eq_char (ch, WEAR_DUAL_WIELD))
    {
      dual_bonus = ch->IsNpc () ?  (ch->GetLevel () / 10) :  (ch->GetPcData ()->learned[gsn_dual_wield] / 10);
      chance = ch->IsNpc () ? ch->GetLevel () : ch->GetPcData ()->learned[gsn_dual_wield];
      if (number_percent () < chance)
      {
	learn_from_success (ch, gsn_dual_wield);
	retcode = one_hit (ch, victim, dt);
	if (retcode != rNONE || ch->GetFightWho () != victim)
	    return retcode;
      }
      else
	learn_from_failure (ch, gsn_dual_wield);
    }
    else
      dual_bonus = 0;

    if (ch->GetMove () < 10)
      dual_bonus = -20;

    /*
     * NPC predetermined number of attacks			-Thoric
     */
    if (ch->IsNpc () && ch->numattacks > 0)
    {
	for (chance = 0; chance <= ch->numattacks; chance++)
	{
	   retcode = one_hit (ch, victim, dt);
	   if (retcode != rNONE || ch->GetFightWho () != victim)
	     return retcode;
	}
	return retcode;
    }

    chance = ch->IsNpc () ? ch->GetLevel ()
	   :  (int)  ((ch->GetPcData ()->learned[gsn_second_attack]+dual_bonus)/1.5);
    if (number_percent () < chance)
    {
	learn_from_success (ch, gsn_second_attack);
	retcode = one_hit (ch, victim, dt);
	if (retcode != rNONE || ch->GetFightWho () != victim)
	    return retcode;
    }
    else
	learn_from_failure (ch, gsn_second_attack);

    chance = ch->IsNpc () ? ch->GetLevel ()
	   :  (int)  ((ch->GetPcData ()->learned[gsn_third_attack]+ (dual_bonus*1.5))/2);
    if (number_percent () < chance)
    {
	learn_from_success (ch, gsn_third_attack);
	retcode = one_hit (ch, victim, dt);
	if (retcode != rNONE || ch->GetFightWho () != victim)
	    return retcode;
    }
    else
	learn_from_failure (ch, gsn_third_attack);

    chance = ch->IsNpc () ? ch->GetLevel ()
	   :  (int)  ((ch->GetPcData ()->learned[gsn_fourth_attack]+ (dual_bonus*2))/3);
    if (number_percent () < chance)
    {
	learn_from_success (ch, gsn_fourth_attack);
	retcode = one_hit (ch, victim, dt);
	if (retcode != rNONE || ch->GetFightWho () != victim)
	    return retcode;
    }
    else
	learn_from_failure (ch, gsn_fourth_attack);

    chance = ch->IsNpc () ? ch->GetLevel ()
	   :  (int)  ((ch->GetPcData ()->learned[gsn_fifth_attack]+ (dual_bonus*3))/4);
    if (number_percent () < chance)
    {
	learn_from_success (ch, gsn_fifth_attack);
	retcode = one_hit (ch, victim, dt);
	if (retcode != rNONE || ch->GetFightWho () != victim)
	    return retcode;
    }
    else
	learn_from_failure (ch, gsn_fifth_attack);

    retcode = rNONE;

    chance = ch->IsNpc () ?  (int)  (ch->GetLevel () / 2) : 0;
    if (number_percent () < chance)
	retcode = one_hit (ch, victim, dt);

    if (retcode == rNONE)
    {
	int move;

	if (! ch->IsFlying ()
	&&   ! ch->IsFloating ())
	  move = encumbrance (ch, movement_loss[UMIN (SECT_MAX-1, ch->GetInRoom ()->sector_type)]);
	else
	  move = encumbrance (ch, 1);
	if (ch->GetMove ())
	  ch->SetMove (UMAX (0, ch->GetMove () - move));
    }

    return retcode;
}


/*
 * Weapon types, haus
 */
int weapon_prof_bonus_check (CCharacter *ch, CObjData *wield, int *gsn_ptr)
{
    int bonus;

    bonus = 0;	*gsn_ptr = -1;
    if (!ch->IsNpc () && ch->GetLevel () > 5 && wield)   
    {
	switch (wield->value[3])
	{
	   default:	*gsn_ptr = -1;			break;
           case 0:
	   case 12:
	   case 10:
	   case 6:	*gsn_ptr = gsn_pugilism;	break;
           case 3:
           case 1:	*gsn_ptr = gsn_long_blades;	break;
           case 11:
           case 2:	*gsn_ptr = gsn_short_blades;	break;
           case 4:	*gsn_ptr = gsn_flexible_arms;	break;
           case 5:	*gsn_ptr = gsn_talonous_arms;	break;
           case 7:
           case 8:	*gsn_ptr = gsn_bludgeons;	break;

	}
	if (*gsn_ptr != -1)
	  bonus =  (int)  ((ch->GetPcData ()->learned[*gsn_ptr] -50)/10);

       /* Reduce weapon bonuses for misaligned clannies.
       if (ch->IsClanned ())
       {
          bonus = bonus / 
           (1 + abs (ch->alignment - ch->GetPcData ()->clan->alignment) / 1000);
       }*/

	if (ch->IsDevoted ()) {
	   bonus = bonus - abs (ch->GetPcData ()->favor) / -100 ;
	}

    }
    return bonus;
}

// Calculate the tohit bonus on the object and return RIS values.
// -- Altrag
int obj_hitroll (CObjData *obj)
{
	int			tohit = 0;
	CAffectData	*paf;

	CAffectList		&IList = obj->pIndexData->AffList;
	POSITION		apos = IList.GetHeadPosition ();
	while (apos) {
		paf = IList.GetNext (apos);	
		if (paf->location == APPLY_HITROLL)
			tohit += paf->modifier;
	}

	CAffectList		&AList = obj->AffList;
	apos = AList.GetHeadPosition ();
	while (apos) {
		paf = AList.GetNext (apos);	
		if (paf->location == APPLY_HITROLL)
			tohit += paf->modifier;
	}

	return tohit;
}

/*
 * Offensive shield level modifier
 */
short off_shld_lvl (CCharacter *ch, CCharacter *victim)
{
    short lvl;

    if (!ch->IsNpc ())		/* players get much less effect */
    {
	lvl = UMAX (1,  (ch->GetLevel () - 10) / 2);
	if (number_percent () +  (victim->GetLevel () - lvl) < 35)
	  return lvl;
	else
	  return 0;
    }
    else
    {
	lvl = ch->GetLevel () / 2;
	if (number_percent () +  (victim->GetLevel () - lvl) < 70)
	  return lvl;
	else
	  return 0;
    }
}


// Hit one guy once.
ch_ret one_hit (CCharacter *ch, CCharacter *victim, int dt)
{
	CObjData	*wield;
	int		victim_ac;
	int		thac0;
	int		thac0_00;
	int		thac0_32;
	int		plusris;
	int		dam;
	int		diceroll;
	int		attacktype, cnt;
	int		prof_bonus;
	int		prof_gsn;
	ch_ret	retcode;

	// Can't beat a dead char!
	// Guard against weird room-leavings.
	if (victim->GetPosition () == POS_DEAD
		|| ch->GetInRoom () != victim->GetInRoom ())
			return rVICT_DIED;

	// Figure out the weapon doing the damage			-Thoric
	// Dual wield support -- switch weapons each attack
	if ((wield = get_eq_char (ch, WEAR_DUAL_WIELD)) != NULL) {
		if (dual_flip == FALSE) {
			dual_flip = TRUE;
			wield = get_eq_char (ch, WEAR_WIELD);
		}
		else dual_flip = FALSE;
	}
	else wield = get_eq_char (ch, WEAR_WIELD);

	prof_bonus = weapon_prof_bonus_check (ch, wield, &prof_gsn);

	// make sure fight is already started
	if (ch->GetFightData () && dt == TYPE_UNDEFINED && ch->IsNpc () && ch->HasAttacks ()) {
		cnt = 0;
		for (;;) {
			attacktype = number_range (0, 6);
			if (ch->CanAttack (attacktype))
				break;
			if (cnt++ > 16) {
				attacktype = -1;
				break;
			}
		}

		if (attacktype == ATCK_BACKSTAB)
			attacktype = -1;
		if (wield && number_percent () > 25)
			attacktype = -1;
		if (! wield && number_percent () > 50)
			attacktype = -1;

		switch (attacktype) {
		  default:
			break;
		  case ATCK_BITE:
			do_bite (ch, "");
			retcode = global_retcode;
			break;
		  case ATCK_CLAWS:
			do_claw (ch, "");
			retcode = global_retcode;
			break;
		  case ATCK_TAIL:
			do_tail (ch, "");
			retcode = global_retcode;
			break;
		  case ATCK_STING:
			do_sting (ch, "");
			retcode = global_retcode;
			break;
		  case ATCK_PUNCH:
			do_punch (ch, "");
			retcode = global_retcode;
			break;
		  case ATCK_KICK:
			do_kick (ch, "");
			retcode = global_retcode;
			break;
		  case ATCK_TRIP:
			attacktype = 0;
			break;
		}
		if (attacktype)
			return retcode;
	}

	if (dt == TYPE_UNDEFINED) {
		dt = TYPE_HIT;
		if (wield && wield->item_type == ITEM_WEAPON)
			dt += wield->value [3];
	}

	// Calculate to-hit-armor-class-0 versus armor.
	if (ch->IsNpc ()) {
		thac0_00 = ch->mobthac0;
		thac0_32 =  0;
	} else {
		thac0_00 = ClassTable.GetThac0 (ch->GetClass ());
		thac0_32 = ClassTable.GetThac32 (ch->GetClass ());
	}
	thac0 = interpolate (ch->GetLevel (), thac0_00, thac0_32) -
		GET_HITROLL (ch);
	victim_ac = UMAX (-19,  (int)  (victim->GetAc () / 10));

	// if you can't see what's coming...
	if (wield && ! can_see_obj (victim, *wield))
		victim_ac += 1;
	if (! can_see (ch, victim))
		victim_ac -= 4;

	// "learning" between combatients.  Takes the intelligence difference,
	// and multiplies by the times killed to make up a learning bonus
	// given to whoever is more intelligent		-Thoric
	if (ch->GetFightData () && ch->GetFightData ()->who == victim) {
		short	times = ch->GetFightData ()->timeskilled;

		if (times) {
			short intdiff = ch->GetIntelligence () -
				victim->GetIntelligence ();

		if (intdiff != 0)
			victim_ac +=  (intdiff*times)/10;
		}
	}

	// Weapon proficiency bonus
	victim_ac += prof_bonus;

	// The moment of excitement!
	while ((diceroll = number_bits (5)) >= 20);

	if (diceroll == 0 || (diceroll != 19 && diceroll < thac0 - victim_ac)) {
		// Miss.
		if (prof_gsn != -1)
			learn_from_failure (ch, prof_gsn);
		damage (ch, victim, 0, dt);
		return rNONE;
	}

	// Hit.
	// Calc damage.
	if (!wield)					// bare hand dice formula fixed by Thoric
		dam = number_range (ch->barenumdie, ch->baresizedie * ch->barenumdie);
	else
		dam = number_range (wield->value [1], wield->value [2]);

	// Bonuses.
	dam += GET_DAMROLL (ch);

	if (prof_bonus)
		dam += prof_bonus / 4;

	// Calculate Damage Modifiers from Victim's Fighting Style
	switch (victim->GetPosition ()) {
	  case POS_BERSERK:
		dam = (int) (1.2 * dam);
		break;
	  case POS_AGGRESSIVE:
		dam = (int) (1.1 * dam);
		break;
	  case POS_DEFENSIVE:
		dam = (int) (.85 * dam);
		break;
	  case POS_EVASIVE:
		dam = (int) (.8 * dam);
	}

	// Calculate Damage Modifiers from Attacker's Fighting Style
	switch (ch->GetPosition ()) {
	  case POS_BERSERK:
		dam = (int) (1.2 * dam);
		break;
	  case POS_AGGRESSIVE:
		dam = (int) (1.1 * dam);
		break;
	  case POS_DEFENSIVE:
		dam = (int) (.85 * dam);
		break;
	  case POS_EVASIVE:
		dam = (int) (.8 * dam);
	}

	if (!ch->IsNpc () && ch->GetPcData ()->learned[gsn_enhanced_damage] > 0) {
		dam += (int) (dam *
			ch->GetPcData ()->learned [gsn_enhanced_damage] / 120);
		learn_from_success (ch, gsn_enhanced_damage);
	}

	if (! victim->IsAwake ())
		dam *= 2;
	if (dt == gsn_backstab)
		dam *= (2 + URANGE (2, ch->GetLevel () -
			(victim->GetLevel ()/4), 30) / 8);

	if (dt == gsn_circle)
		dam *= (2 + URANGE (2, ch->GetLevel () -
			(victim->GetLevel ()/4), 30) / 16); 

	if (dam <= 0)
		dam = 1;

	plusris = 0;

	if (wield) {
		if (wield->IsMagic ())
			dam = ris_damage (victim, dam, RIS_MAGIC);
		else
			dam = ris_damage (victim, dam, RIS_NONMAGIC);

		// Handle PLUS1 - PLUS6 ris bits vs. weapon hitroll	-Thoric
		plusris = obj_hitroll (wield);
	}
	else dam = ris_damage (victim, dam, RIS_NONMAGIC);

	// check for RIS_PLUSx - Thoric
	if (dam) {
		int		x, res, imm, sus, mod;

		if (plusris)
			plusris = RIS_PLUS1 << UMIN (plusris, 7);

		// initialize values to handle a zero plusris
		imm = res = -1;  sus = 1;

		// find high ris
		for (x = RIS_PLUS1; x <= RIS_PLUS6; x <<= 1) {
			if (victim->IsImmune (x))
				imm = x;
			if (victim->CanResist (x))
				res = x;
			if (victim->IsSusceptible (x))
				sus = x;
		}
		mod = 10;
		if (imm >= plusris)
			mod -= 10;
		if (res >= plusris)
			mod -= 2;
		if (sus <= plusris)
			mod += 2;

		// check if immune
		if (mod <= 0)
			dam = -1;
		if (mod != 10)
			dam = (dam * mod) / 10;
	}

	if (prof_gsn != -1) {
		if (dam > 0)
			learn_from_success (ch, prof_gsn);
		else
			learn_from_failure (ch, prof_gsn);
	}

	// immune to damage
	if (dam == -1) {
		if (dt >= 0 && dt < SkillTable.GetCount ()) {
			CSkill	*skill = SkillTable.GetSkill (dt);
			BOOL	found = FALSE;

			if (skill->ValidImmuneCharMsg ()) {
				act (AT_HIT, skill->GetImmuneCharMsg (), ch, NULL, victim,
					TO_CHAR);
				found = TRUE;
			}
			if (skill->ValidImmuneVictMsg ()) {
				act (AT_HITME, skill->GetImmuneVictMsg (), ch, NULL, victim,
					TO_VICT);
				found = TRUE;
			}
			if (skill->ValidImmuneRoomMsg ()) {
				act (AT_ACTION, skill->GetImmuneRoomMsg (), ch, NULL, victim,
					TO_NOTVICT);
				found = TRUE;
			}
			if (found)
				return rNONE;
		}
		dam = 0;
	}

	if ((retcode = damage (ch, victim, dam, dt)) != rNONE)
		return retcode;
	if (char_died (ch))
		return rCHAR_DIED;
	if (char_died (victim))
		return rVICT_DIED;

	retcode = rNONE;
	if (dam == 0)
		return retcode;

	// weapon spells	-Thoric
	// Each successful hit casts a spell
	if (wield && ! victim->IsImmuneMagic ()
	  && ! victim->GetInRoom ()->IsNoMagic ()) {
		CAffectData	*aff;

		CAffectList		&IList = wield->pIndexData->AffList;
		POSITION		apos = IList.GetHeadPosition ();
		while (apos) {
			aff = IList.GetNext (apos);
			if (aff->location == APPLY_WEAPONSPELL
				&& SkillTable.IsValid (aff->modifier)
				&& SkillTable.GetSpellFunction (aff->modifier))
					retcode = (*SkillTable.GetSpellFunction (aff->modifier))
						(aff->modifier, (wield->level+3)/3, ch, victim);
		}
		if (retcode != rNONE || char_died (ch) || char_died (victim))
			return retcode;

		CAffectList		&AList = wield->AffList;
		apos = AList.GetHeadPosition ();
		while (apos) {
			aff = AList.GetNext (apos);
			if (aff->location == APPLY_WEAPONSPELL
				&& SkillTable.IsValid (aff->modifier)
				&& SkillTable.GetSpellFunction (aff->modifier))
					retcode = (*SkillTable.GetSpellFunction ( aff->modifier))
						(aff->modifier, (wield->level+3)/3, ch, victim);
		}
		if (retcode != rNONE || char_died (ch) || char_died (victim))
			return retcode;
	}

	// magic shields that retaliate				-Thoric
	if (victim->IsAffected (AFF_FIRESHIELD)
		&& !ch->IsAffected (AFF_FIRESHIELD))
			retcode = spell_fireball (gsn_fireball,
				off_shld_lvl (victim, ch), victim, ch);
	if (retcode != rNONE || char_died (ch) || char_died (victim))
		return retcode;

	if (victim->IsAffected (AFF_ICESHIELD)
		&& ! ch->IsAffected (AFF_ICESHIELD))
			retcode = spell_chill_touch (gsn_chill_touch,
				off_shld_lvl (victim, ch), victim, ch);
	if (retcode != rNONE || char_died (ch) || char_died (victim))
		return retcode;

	if (victim->IsAffected (AFF_SHOCKSHIELD)
		&& ! ch->IsAffected (AFF_SHOCKSHIELD))
			retcode = spell_lightning_bolt (gsn_lightning_bolt,
				off_shld_lvl (victim, ch), victim, ch);
	if (retcode != rNONE || char_died (ch) || char_died (victim))
		return retcode;

	return retcode;
}


// Hit one guy with a projectile.
// Handles use of missile weapons (wield = missile weapon)
// or thrown items/weapons
ch_ret projectile_hit (CCharacter *ch, CCharacter *victim, CObjData *wield,
						CObjData *projectile, short dist)
{
	int		victim_ac;
	int		thac0;
	int		thac0_00;
	int		thac0_32;
	int		plusris;
	int		dam;
	int		diceroll;
	int		prof_bonus;
	int		prof_gsn = -1;
	int		proj_bonus;
	int		dt;
	ch_ret	retcode;

	if (! projectile)
		return rNONE;

	if (projectile->item_type == ITEM_PROJECTILE 
	  || projectile->item_type == ITEM_WEAPON) {
		dt = TYPE_HIT + projectile->value [3];
		proj_bonus = number_range (projectile->value [1],
			projectile->value [2]);
	} else {
		dt = TYPE_UNDEFINED;
		proj_bonus = number_range (1, URANGE (2,
			get_obj_weight (projectile), 100));
	}

	// Can't beat a dead char!
	if (victim->IsDead () || char_died (victim)) {
		extract_obj (projectile);
		return rVICT_DIED;
	}

	if (wield)
		prof_bonus = weapon_prof_bonus_check (ch, wield, &prof_gsn);
	else
		prof_bonus = 0;

	if (dt == TYPE_UNDEFINED) {
		dt = TYPE_HIT;
		if (wield && wield->item_type == ITEM_MISSILE_WEAPON)
			dt += wield->value [3];
	}

	// Calculate to-hit-armor-class-0 versus armor.
	if (ch->IsNpc ()) {
		thac0_00 = ch->mobthac0;
		thac0_32 = 0;
	} else {
		thac0_00 = ClassTable.GetThac0 (ch->GetClass ());
		thac0_32 = ClassTable.GetThac32 (ch->GetClass ());
	}
	thac0 = interpolate (ch->GetLevel (), thac0_00, thac0_32)
		- GET_HITROLL (ch) + (dist*2);
	victim_ac = UMAX (-19, (victim->GetAc () / 10));

	// if you can't see what's coming...
	if (! can_see_obj (victim, *projectile))
		++victim_ac;
	if (! can_see (ch, victim))
		victim_ac -= 4;

	// Weapon proficiency bonus
	victim_ac += prof_bonus;

	// The moment of excitement!
	while ((diceroll = number_bits (5)) >= 20)
	;

	if (diceroll == 0 || (diceroll != 19 && diceroll < thac0 - victim_ac)) {
		// Miss.
		if (prof_gsn != -1)
			learn_from_failure (ch, prof_gsn);

		// Do something with the projectile
		if (number_percent () < 50)
			extract_obj (projectile);
		else {
			if (projectile->in_obj)
				obj_from_obj (projectile);
			if (projectile->carried_by)
				obj_from_char (projectile);
			obj_to_room (projectile, victim->GetInRoom ());
		}
		damage (ch, victim, 0, dt);
		return rNONE;
	}

	// Hit, Calc damage.
	if (! wield)			// dice formula fixed by Thoric
		dam = proj_bonus;
	else
		dam = number_range (wield->value [1],
			wield->value [2]) + (proj_bonus / 10);

	// Bonuses.
	dam += GET_DAMROLL (ch);

	if (prof_bonus)
		dam += prof_bonus / 4;

	// Calculate Damage Modifiers from Victim's Fighting Style
	if (victim->GetPosition () == POS_BERSERK)
		dam = (int) (dam * 1.2);
	else if (victim->GetPosition () == POS_AGGRESSIVE)
		dam = (int) (dam * 1.1);
	else if (victim->GetPosition () == POS_DEFENSIVE)
		dam = (int) (dam * .85);
	else if (victim->GetPosition () == POS_EVASIVE)
		dam = (int) (dam * .8);

	if (! ch->IsNpc ()
	  && ch->GetPcData ()->learned [gsn_enhanced_damage] > 0) {
		dam += ch->GetLearnedPercent (gsn_enhanced_damage) / 120;
		learn_from_success (ch, gsn_enhanced_damage);
	}

	if (! victim->IsAwake ())
		dam *= 2;

	if (dam <= 0)
		dam = 1;

	plusris = 0;

	if (projectile->IsMagic ())
		dam = ris_damage (victim, dam, RIS_MAGIC);
	else
		dam = ris_damage (victim, dam, RIS_NONMAGIC);

	// Handle PLUS1 - PLUS6 ris bits vs. weapon hitroll	-Thoric
	if (wield)
		plusris = obj_hitroll (wield);

	// check for RIS_PLUSx 					-Thoric
	if (dam) {
		if (plusris)
			plusris = RIS_PLUS1 << UMIN (plusris, 7);

		// initialize values to handle a zero plusris
		int		imm = -1;
		int		res = -1;
		int		sus = 1;

		// find high ris
		for (int x = RIS_PLUS1; x <= RIS_PLUS6; x <<= 1) {
			if (victim->IsImmune (x))
				imm = x;
			if (victim->CanResist (x))
				res = x;
			if (victim->IsSusceptible (x))
				sus = x;
		}
		int	mod = 10;
		if (imm >= plusris)
			mod -= 10;
		if (res >= plusris)
			mod -= 2;
		if (sus <= plusris)
			mod += 2;

		// check if immune
		if (mod <= 0)
			dam = -1;
		if (mod != 10)
			dam = (dam * mod) / 10;
	}

	if (prof_gsn != -1) {
		if (dam > 0)
			learn_from_success (ch, prof_gsn);
		else
			learn_from_failure (ch, prof_gsn);
	}

	// immune to damage
	if (dam == -1) {
		CSkill	*pSkill = SkillTable.GetSkill (dt);
		if (pSkill) {
			BOOL	bFound = FALSE;

			if (pSkill->ValidImmuneCharMsg ()) {
				act (AT_HIT, pSkill->GetImmuneCharMsg (), ch, NULL,
					victim, TO_CHAR);
				bFound = TRUE;
			}
			if (pSkill->ValidImmuneVictMsg ()) {
				act (AT_HITME, pSkill->GetImmuneVictMsg (), ch, NULL,
					victim, TO_VICT);
				bFound = TRUE;
			}
			if (pSkill->ValidImmuneRoomMsg ()) {
				act (AT_ACTION, pSkill->GetImmuneRoomMsg (), ch, NULL,
					victim, TO_NOTVICT);
				bFound = TRUE;
			}
			if (bFound) {
				if (number_percent() < 50)
					extract_obj (projectile);
				else {
					if (projectile->in_obj)
						obj_from_obj (projectile);
					if (projectile->carried_by)
						obj_from_char (projectile);
					obj_to_room (projectile, victim->GetInRoom ());
				}
				return rNONE;
			}
		}
		dam = 0;
	}
	if ((retcode = damage (ch, victim, dam, dt)) != rNONE) {
		extract_obj (projectile);
		return retcode;
	}
	if (char_died (ch)) {
		extract_obj (projectile);
		return rCHAR_DIED;
	}
	if (char_died (victim)) {
		extract_obj (projectile);
		return rVICT_DIED;
	}

	retcode = rNONE;
	if (dam == 0) {
		if (number_percent() < 50)
			extract_obj (projectile);
		else {
			if (projectile->in_obj)
				obj_from_obj (projectile);
			if (projectile->carried_by)
				obj_from_char (projectile);
			obj_to_room (projectile, victim->GetInRoom ());
		}
		return retcode;
	}

	// weapon spells	-Thoric
	if (wield && ! victim->IsImmuneMagic ()
	  && ! victim->GetInRoom ()->IsNoMagic ()) {
		CAffectList		&IList = wield->pIndexData->AffList;
		POSITION		pos = IList.GetHeadPosition ();
		while (pos) {
			CAffectData	&Af = *IList.GetNext (pos);
			if (Af.location == APPLY_WEAPONSPELL
				&& SkillTable.IsValid (Af.modifier)
				&& SkillTable.GetSpellFunction (Af.modifier))
					retcode = (*SkillTable.GetSpellFunction (Af.modifier))
						(Af.modifier, (wield->level+3)/3, ch, victim);
		}
		if (retcode != rNONE || char_died (ch) || char_died (victim)) {
			extract_obj (projectile);
			return retcode;
		}

		CAffectList		&AList = wield->AffList;
		pos = AList.GetHeadPosition ();
		while (pos) {
			CAffectData	&Af = *AList.GetNext (pos);
			if (Af.location == APPLY_WEAPONSPELL
				&& SkillTable.IsValid (Af.modifier)
				&& SkillTable.GetSpellFunction (Af.modifier))
					retcode = (*SkillTable.GetSpellFunction (Af.modifier))
						(Af.modifier, (wield->level+3)/3, ch, victim);
		}
		if (retcode != rNONE || char_died (ch) || char_died (victim)) {
			extract_obj (projectile);
			return retcode;
		}
	}

	extract_obj (projectile);
	return retcode;
}


// Calculate damage based on resistances, immunities and suceptibilities
//					-Thoric
short ris_damage (CCharacter *ch, short dam, int ris)
{
	short	modifier;

	modifier = 10;
	if (ch->IsImmune (ris))
		modifier -= 10;
	if (ch->CanResist (ris))
		modifier -= 2;
	if (ch->IsSusceptible (ris))
		modifier += 2;
	if (modifier <= 0)
		return -1;
	if (modifier == 10)
		return dam;
	return  (dam * modifier) / 10;
}


// Inflict damage from a hit.
ch_ret damage (CCharacter *ch, CCharacter *victim, int dam, int dt)
{
	char		buf1 [MAX_STRING_LENGTH];
	short		dameq;
	short		maxdam;
	BOOL		npcvict;
	BOOL		loot;
	int			xp_gain;
	CObjData	*damobj;
	short		dampmod;
	CCharacter	*gch;
	int			init_gold, new_gold, gold_diff;

	ch_ret	retcode = rNONE;

	if (! ch) {
		bug ("Damage: null ch!");
		return rERROR;
	}
	if (! victim) {
		bug ("Damage: null victim!");
		return rVICT_DIED;
	}

	if (victim->GetPosition () == POS_DEAD)
		return rVICT_DIED;

	npcvict = victim->IsNpc ();

	// Check damage types for RIS				-Thoric
	if (dam && dt != TYPE_UNDEFINED) {
		if (IS_FIRE (dt))
			dam = ris_damage (victim, dam, RIS_FIRE);
		else if (IS_COLD (dt))
			dam = ris_damage (victim, dam, RIS_COLD);
		else if (IS_ACID (dt))
			dam = ris_damage (victim, dam, RIS_ACID);
		else if (IS_ELECTRICITY (dt))
			dam = ris_damage (victim, dam, RIS_ELECTRICITY);
		else if (IS_ENERGY (dt))
			dam = ris_damage (victim, dam, RIS_ENERGY);
		else if (IS_DRAIN (dt))
			dam = ris_damage (victim, dam, RIS_DRAIN);
		else if (dt == gsn_poison || IS_POISON (dt))
			dam = ris_damage (victim, dam, RIS_POISON);
		else if (dt == (TYPE_HIT+DAM_POUND) || dt == (TYPE_HIT+DAM_CRUSH)
			|| dt == (TYPE_HIT+DAM_STONE) || dt == (TYPE_HIT+DAM_PEA))
				dam = ris_damage (victim, dam, RIS_BLUNT);
		else if (dt == (TYPE_HIT+DAM_STAB) || dt == (TYPE_HIT+DAM_PIERCE)
			|| dt == (TYPE_HIT+DAM_BITE) || dt == (TYPE_HIT+DAM_BOLT)
			|| dt == (TYPE_HIT+DAM_DART) || dt == (TYPE_HIT+DAM_ARROW))
				dam = ris_damage (victim, dam, RIS_PIERCE);
		else if (dt == (TYPE_HIT+DAM_SLICE) || dt == (TYPE_HIT+DAM_SLASH)
			|| dt == (TYPE_HIT+DAM_WHIP) || dt == (TYPE_HIT+DAM_CLAW))
				dam = ris_damage (victim, dam, RIS_SLASH);

		if (dam == -1) {
			if (dt >= 0 && dt < SkillTable.GetCount ()) {
				BOOL	found = FALSE;
				CSkill	*skill = SkillTable.GetSkill (dt);

				if (skill->ValidImmuneCharMsg ()) {
					act (AT_HIT, skill->GetImmuneCharMsg (),
						ch, NULL, victim, TO_CHAR);
					found = TRUE;
				}
				if (skill->ValidImmuneVictMsg ()) {
					act (AT_HITME, skill->GetImmuneVictMsg (),
						ch, NULL, victim, TO_VICT);
					found = TRUE;
				}
				if (skill->ValidImmuneRoomMsg ()) {
					act (AT_ACTION, skill->GetImmuneRoomMsg (),
						ch, NULL, victim, TO_NOTVICT);
					found = TRUE;
				}
				if (found)
				return rNONE;
			}
			dam = 0;
		}
	}

	// Precautionary step mainly to prevent people in Hell from finding
	// a way out. --Shaddai
	if (victim->GetInRoom ()->IsSafe ())
		dam = 0;

	if (dam && npcvict && ch != victim) {
		if (! victim->IsAction (ACT_SENTINEL)) {
			if (victim->hunting) {
				if (victim->hunting->who != ch) {
					STRFREE (victim->hunting->name);
					victim->hunting->name = QUICKLINK (ch->GetName ());
					victim->hunting->who  = ch;
				}
			}
			else if (! victim->IsPacifist ())	// Gorog
				start_hunting (victim, ch);
		}

		if (victim->hating) {
			if (victim->hating->who != ch) {
				STRFREE (victim->hating->name);
				victim->hating->name = QUICKLINK (ch->GetName ());
				victim->hating->who  = ch;
			}
		}
		else if (! victim->IsPacifist ())	// Gorog
			start_hating (victim, ch);
	}

	// Stop up any residual loopholes.
	maxdam = (dt == gsn_backstab) ?
		ch->GetLevel () * 80 : ch->GetLevel () * 30;
	if (dam > maxdam) {
		bug ("Damage: %d more than %d points!", dam, maxdam);
		bug ("** %s  (lvl %d) -> %s **",
			ch->GetName (), ch->GetLevel (), victim->GetName ());
		dam = maxdam;
	}

	if (victim != ch) {
		// Certain attacks are forbidden.
		// Most other attacks are returned.
		if (is_safe (ch, victim))
			return rNONE;
		check_attacker (ch, victim);


		if (victim->GetPosition () > POS_STUNNED) {
			if (! victim->GetFightData ())
				set_fighting (victim, ch);
			
			if (victim->GetFightData ()) {
				if (victim->IsNpc ())
					victim->SetPosition (POS_FIGHTING);
				else switch (victim->GetStyle ()) {
				  case (STYLE_EVASIVE):
					victim->SetPosition (POS_EVASIVE);
					break;
				  case (STYLE_DEFENSIVE):
					victim->SetPosition (POS_DEFENSIVE);
					break;
				  case (STYLE_AGGRESSIVE):
					victim->SetPosition (POS_AGGRESSIVE);
					break;
				  case (STYLE_BERSERK):
					victim->SetPosition (POS_BERSERK);
					break;
				  default:
					victim->SetPosition (POS_FIGHTING);
				}
			}
		}

		if (victim->GetPosition () > POS_STUNNED) {
			if (! ch->GetFightData ())
				set_fighting (ch, victim);

			// If victim is charmed, ch might attack victim's master.
			if (ch->IsNpc ()
			  && npcvict
			  && victim->IsCharmed ()
			  && victim->GetMaster ()
			  && victim->GetMaster ()->GetInRoom () == ch->GetInRoom ()
			  && number_bits (3) == 0) {
				stop_fighting (ch, FALSE);
				retcode = multi_hit (ch, victim->GetMaster (), TYPE_UNDEFINED);
				return retcode;
			}
		}


		// More charm stuff.
		if (victim->GetMaster () == ch)
			stop_follower (victim);

		// Pkill stuff.  If a deadly attacks another deadly or is attacked
		// by one, then ungroup any nondealies.  Disabled untill I can figure
		// out the right way to do it.

		#ifdef XXXX
		/*
		short		anopc = 0;	// # of  (non-pkill) pc in a  (ch)
		short		bnopc = 0;	// # of  (non-pkill) pc in b  (victim)
		CCharacter	*lch;		// leader ch

		// count the # of non-pkill pc in a  (not including == ch)
		for (gch = ch->GetInRoom ()->first_person; gch;
			gch = gch->GetNextInRoom ())
				if (is_same_group (ch, gch) && ! gch->IsNpc ()
					&& ! gch->IsPkiller () &&  (ch != gch))
						anopc++;

		// count the # of non-pkill pc in b  (not including == victim)
		for (gch = victim->GetInRoom ()->first_person; gch;
			gch = gch->GetNextInRoom ())
				if (is_same_group (victim, gch) && ! gch->IsNpc ()
					&& ! gch->IsPkiller () &&  (victim != gch))
						bnopc++;


		// only consider disbanding if both groups have 1 (+) non-pk pc
		if ((bnopc > 0) &&  (anopc > 0)) {
			// look at group a through ch's leader first
			lch = ch->GetLeader () ? ch->GetLeader () : ch;
			if (lch != ch) {
				// stop following leader if it isn't pk
				if (! IS_NPC (lch) && ! IS_PKILL (lch))
					stop_follower (ch);
				else {
					// disband non-pk members from lch's group if it is pk
					for (gch = ch->GetInRoom ()->first_person; gch; 
					  gch = gch->GetNextInRoom ()) {
						if (is_same_group (lch, gch) && (lch != gch)
							&& ! gch->IsNpc () && ! gch->IsPkiller ())
								stop_follower (gch);
					}
				}
			} 
			else for (gch = ch->GetInRoom ()->first_person; gch;
			  gch = gch->GetNextInRoom ()) {
				// ch is leader - disband non-pks from group
				if (is_same_group (ch, gch) && (ch != gch)
					&& (! gch->IsPkiller () && ! gch->IsNpc ()))
						stop_follower (gch);
			}

			// time to look at the victims group through its leader
			lch = victim->GetLeader () ? victim->GetLeader () : victim;

			if (lch != victim) {
				// if leader isn't deadly, stop following lch
				if (! IS_PKILL (lch) && ! IS_NPC (lch))
					stop_follower (victim);
				else {
					// lch is pk, disband non-pk's from group
					for (gch = victim->GetInRoom ()->first_person; gch; 
					  gch = gch->GetNextInRoom ()) {
						if (is_same_group (lch, gch) && (lch != gch)
							&& (! gch->IsPkiller () && ! gch->IsNpc ()))
								stop_follower (gch);
					}
				}
			} else {
				// victim is leader of group - disband non-pks
				for (gch = victim->GetInRoom ()->first_person; gch;
				  gch = gch->GetNextInRoom ()) {
					if (is_same_group (victim, gch) && (victim != gch)
						&& ! gch->IsPkiller () && ! gch->IsNpc ())
							stop_follower (gch);
				}
			}
		}
		*/
#endif

		short anopc = 0;	// # of  (non-pkill) pc in a  (ch)
		short bnopc = 0;	// # of  (non-pkill) pc in b  (victim)

		// count the # of non-pkill pc in a  (not including == ch)
		for (gch = ch->GetInRoom ()->first_person; gch;
			gch = gch->GetNextInRoom ())
				if (is_same_group (ch, gch) && ! gch->IsNpc ()
					&& ! gch->IsPkiller () && (ch != gch))
						anopc++;

		// count the # of non-pkill pc in b  (not including == victim)
		for (gch = victim->GetInRoom ()->first_person; gch;
			gch = gch->GetNextInRoom ())
				if (is_same_group (victim, gch) && ! gch->IsNpc ()
					&& ! gch->IsPkiller () && (victim != gch))
						bnopc++;


		// only consider disbanding if both groups have 1 (+) non-pk pc,
		// or when one participant is pc, and the other group has 1 (+)
		// pk pc's  (in the case that participant is only pk pc in group)
		if ((bnopc > 0 && anopc > 0)
		  || (bnopc > 0 && !ch->IsNpc ())
		  || (anopc > 0 && !victim->IsNpc ())) {
			// Disband from same group first
			if (is_same_group (ch, victim)) {
				// Messages to char and master handled in stop_follower
				act (AT_ACTION, "$n disbands from $N's group.",
					(ch->GetLeader () == victim) ? victim : ch, NULL,
					(ch->GetLeader () == victim) ?
					victim->GetMaster () : ch->GetMaster (),
					TO_NOTVICT);
				if (ch->GetLeader () == victim)
					stop_follower (victim);
				else
					stop_follower (ch);
			}

			// if leader isnt pkill, leave the group and disband ch
			if (ch->GetLeader () != NULL && ! ch->GetLeader ()->IsNpc () &&
			  ! ch->GetLeader ()->IsPkiller ()) {
				act (AT_ACTION, "$n disbands from $N's group.", ch, NULL,
					ch->GetMaster (), TO_NOTVICT);
				stop_follower (ch);
			} else {
				for (gch = ch->GetInRoom ()->first_person; gch;
				  gch = gch->GetNextInRoom ())
					if (is_same_group (gch, ch) && !gch->IsNpc () &&
					  ! gch->IsPkiller () && gch != ch) {
						act (AT_ACTION, "$n disbands from $N's group.",
							ch, NULL,
						gch->GetMaster (), TO_NOTVICT);
						stop_follower (gch);
					}
			}
			// if leader isnt pkill, leave the group and disband victim
			if (victim->GetLeader () != NULL
			  && ! victim->GetLeader ()->IsNpc ()
			  && ! victim->GetLeader ()->IsPkiller ()) {
				act (AT_ACTION, "$n disbands from $N's group.", victim,
					NULL, victim->GetMaster (), TO_NOTVICT);
				stop_follower (victim);
			} else {
				for (gch = victim->GetInRoom ()->first_person; gch;
					gch = gch->GetNextInRoom ())
				if (is_same_group (gch, victim) && !gch->IsNpc () &&
				  ! gch->IsPkiller () && gch != victim) {
					act (AT_ACTION, "$n disbands from $N's group.", gch,
						NULL, gch->GetMaster (), TO_NOTVICT);
					stop_follower (gch);
				}
			}
		}
			
		// Inviso attacks ... not.
		if (ch->IsInvis ()) {
			affect_strip (ch, gsn_invis);
			affect_strip (ch, gsn_mass_invis);
			ch->ClrInvis ();
			act (AT_MAGIC, "$n fades into existence.", ch, NULL, NULL,
				TO_ROOM);
		}

		// Take away Hide
		if (ch->IsHidden ())
			 ch->ClrHide ();

		// Damage modifiers.
		if (victim->HasSanctuary ())
			dam /= 2;

		if (victim->IsProtected () && ch->IsEvil ())
			dam -=  (int)  (dam / 4);

		if (dam < 0)
			dam = 0;


		// Check for disarm, trip, parry, and dodge.
		if (dt >= TYPE_HIT && ch->GetInRoom () == victim->GetInRoom ()) {
			if (ch->IsNpc () && ch->CanDisarm ()
				&& ch->GetLevel () > 9
				&& number_percent () < ch->GetLevel () / 3)
					disarm (ch, victim);

			if (ch->IsNpc () && ch->CanTrip ()
				&& ch->GetLevel () > 5
				&& number_percent () < ch->GetLevel () / 2)
					trip (ch, victim);

			if (check_parry (ch, victim))
				return rNONE;
			if (check_dodge (ch, victim))
				return rNONE;
//			if (check_tumble (ch, victim))
//				return rNONE;
		}

		// Check control panel settings and modify damage
		if (ch->IsNpc ()) {
			if (npcvict)
				dampmod = SysData.MobMobDamage;
			else
				dampmod = SysData.MobPlrDamage;
		} else {
			if (npcvict)
				dampmod = SysData.PlrMobDamage;
			else
				dampmod = SysData.PlrPlrDamage;
		}
		if (dampmod > 0)
			dam =  (dam * dampmod) / 100;

		dam_message (ch, victim, dam, dt);
    }


	// Code to handle equipment getting damaged, and also support  -Thoric
	// bonuses/penalties for having or not having equipment where hit
	if (dam > 10 && dt != TYPE_UNDEFINED) {
		// get a random body eq part
		dameq  = number_range (WEAR_LIGHT, WEAR_EYES);
		damobj = get_eq_char (victim, dameq);
		if (damobj) {
			if (dam > get_obj_resistance (damobj) && number_bits (1) == 0) {
				set_cur_obj (damobj);
				damage_obj (damobj);
			}
			dam -= 5;	// add a bonus for having something to block the blow
		}
		else
			dam += 5;	// add penalty for bare skin!
	}

	// Hurt the victim.
	// Inform the victim of his new state.
	victim->AddHp (-dam);

	// Get experience based on % of damage done			-Thoric
	if (dam && ch != victim
	  &&  !ch->IsNpc () && ch->GetFightData () && ch->GetFightData ()->xp) {
		if (ch->GetFightData ()->who == victim)
			xp_gain = (int) (ch->GetFightData ()->xp * dam)
				/ victim->GetMaxHp ();
		else
			xp_gain = (int) (xp_compute (ch, victim) * 0.85 * dam)
				/ victim->GetMaxHp ();
		gain_exp (ch, xp_gain);
	}

	if (! victim->IsNpc ()
		&& victim->GetLevel () >= LEVEL_IMMORTAL && victim->GetHp () < 1)
			victim->SetHp (1);

	// Make sure newbies dont die

	if (!victim->IsNpc () && !victim->IsAuthed () && victim->GetHp () < 1)
		victim->SetHp (1);

	if (dam > 0 && dt > TYPE_HIT
	  && ! victim->IsPoisoned ()
	  &&  is_wielding_poisoned (ch)
	  && ! victim->IsImmunePoison ()
	  && !saves_poison_death (ch->GetLevel (), victim)) {
		CAffectData af;

		af.type      = gsn_poison;
		af.duration  = 20;
		af.location  = APPLY_STR;
		af.modifier  = -2;
		af.bitvector = AFF_POISON;
		affect_join (victim, &af);
		victim->SetMentalState (URANGE (20,
			victim->GetMentalState () + victim->IsPkiller () ? 1 : 2, 100));
	}

	// Vampire self preservation				-Thoric
	if (victim->IsVampire ()) {
		if (dam >= (victim->GetMaxHp () / 10))	// get hit hard, lose blood
			gain_condition (victim,
				COND_BLOODTHIRST, -1 - (victim->GetLevel () / 20));
		if (victim->GetHp () <= (victim->GetMaxHp () / 8)
		  && victim->GetPcData ()->condition[COND_BLOODTHIRST] > 5) {
			gain_condition (victim, COND_BLOODTHIRST,
				-URANGE (3, victim->GetLevel () / 10, 8));
			victim->AddHp (URANGE (4,  (victim->GetMaxHp () / 30), 15));
			set_char_color (AT_BLOOD, victim);
			victim->SendText ("You howl with rage as the beast within stirs!\n\r");
		}
	}

	if (! npcvict && victim->GetTrustLevel () >= LEVEL_IMMORTAL
		&& ch->GetTrustLevel () >= LEVEL_IMMORTAL && victim->GetHp () < 1)
			victim->SetHp (1);

	update_pos (victim);

	switch (victim->GetPosition ()) {
	  case POS_MORTAL:
		act (AT_DYING, "$n is mortally wounded, and will die soon, if not aided.",
			victim, NULL, NULL, TO_ROOM);
		act (AT_DANGER, "You are mortally wounded, and will die soon, if not aided.",
			victim, NULL, NULL, TO_CHAR);
		break;

	  case POS_INCAP:
		act (AT_DYING, "$n is incapacitated and will slowly die, if not aided.",
			victim, NULL, NULL, TO_ROOM);
		act (AT_DANGER, "You are incapacitated and will slowly die, if not aided.",
			victim, NULL, NULL, TO_CHAR);
		break;

	  case POS_STUNNED:
		if (! victim->IsParalysed ()) {
			act (AT_ACTION, "$n is stunned, but will probably recover.",
				victim, NULL, NULL, TO_ROOM);
			act (AT_HURT, "You are stunned, but will probably recover.",
				victim, NULL, NULL, TO_CHAR);
		}
		break;

	  case POS_DEAD:
		if (dt >= 0 && dt < SkillTable.GetCount ()) {
			CSkill *skill = SkillTable.GetSkill (dt);

			if (skill->ValidDieCharMsg ())
				act (AT_DEAD, skill->GetDieCharMsg (), ch, NULL, victim, TO_CHAR);
			if (skill->ValidDieVictMsg ())
				act (AT_DEAD, skill->GetDieVictMsg (), ch, NULL, victim, TO_VICT);
			if (skill->ValidDieRoomMsg ())
				act (AT_DEAD, skill->GetDieRoomMsg (), ch, NULL, victim, TO_NOTVICT);
		}
		act (AT_DEAD, "$n is DEAD!", victim, 0, 0, TO_ROOM);
		act (AT_DEAD, "You have been KILLED!\n\r", victim, 0, 0, TO_CHAR);
		break;

	  default:
		if (dam > victim->GetMaxHp () / 4) {
			act (AT_HURT, "That really did HURT!", victim, 0, 0, TO_CHAR);
			if (number_bits (3) == 0)
				worsen_mental_state (victim, 1);
		}

		if (victim->GetHp () < victim->GetMaxHp () / 4) {
			act (AT_DANGER, "You wish that your wounds would stop BLEEDING "
				"so much!", victim, 0, 0, TO_CHAR);
			if (number_bits (2) == 0)
				worsen_mental_state (victim, 1);
		}
		break;
	}

	// Sleep spells and extremely wounded folks.
	if (! victim->IsAwake ()		// lets make NPC's not slaughter PC's
	  && ! victim->IsParalysed ()) {
		if (victim->GetFightData ()
			&& victim->GetFightData ()->who->hunting
			&& victim->GetFightData ()->who->hunting->who == victim)
				stop_hunting (victim->GetFightData ()->who);

		if (victim->GetFightData ()
			&& victim->GetFightData ()->who->hating
			&& victim->GetFightData ()->who->hating->who == victim)
				stop_hating (victim->GetFightData ()->who);

		if (! npcvict && ch->IsNpc ())
			stop_fighting (victim, TRUE);
		else
			stop_fighting (victim, FALSE);
	}

	// Payoff for killing things.
	if (victim->GetPosition () == POS_DEAD) {
		group_gain (ch, victim);

		if (! npcvict) {
			sprintf (log_buf, "%s killed by %s at %d",
				victim->GetName (),
				(ch->IsNpc () ? ch->GetShortDescr () : ch->GetName ()),
				victim->GetInRoom ()->vnum);
			gpDoc->LogString (log_buf, LOG_PLAYER);
			to_channel (log_buf, CHANNEL_MONITOR, "Monitor", LEVEL_IMMORTAL);

			// Dying penalty:
			// 1/2 way back to previous level.
			if (victim->GetExp () > exp_level (victim,
				victim->GetLevel ()))
					gain_exp (victim,
						(exp_level (victim,
							victim->GetLevel ()) - victim->GetExp ())/2);

			// New penalty... go back to the beginning of current level.
			// victim->GetExp () = exp_level (victim, victim->GetLevel ());
		}
		else if (! ch->IsNpc ())		// keep track of mob vnum killed
			add_kill (ch, victim);

		check_killer (ch, victim);

		if (ch->GetInRoom () == victim->GetInRoom ())
			loot = legal_loot (ch, victim);
		else loot = FALSE;

		set_cur_char (victim);
		raw_kill (ch, victim);
		victim = NULL;

		if (! ch->IsNpc () && loot) {
			// Autogold by Scryn 8/12
			if (ch->IsAction (PLR_AUTOGOLD)) {
				init_gold = ch->GetGold ();
				do_get (ch, "coins corpse");
				new_gold = ch->GetGold ();
				gold_diff =  (new_gold - init_gold);
				if (gold_diff > 0) {
					sprintf (buf1,"%d",gold_diff);
					do_split (ch, buf1);
				} 
			}
			if (ch->IsAction (PLR_AUTOLOOT) && victim != ch)
				do_get (ch, "all corpse");
			else
				do_look (ch, "in corpse");

			if (ch->IsAction (PLR_AUTOSAC))
				do_sacrifice (ch, "corpse");
		}

		if (SysData.IsSaveOnKill ())
			save_char_obj (ch);
		return rVICT_DIED;
	}

	if (victim == ch)
		return rNONE;

	// Take care of link dead people.
	if (! npcvict && !victim->GetDesc () && ! victim->IsNoRecall ()) {
		if (number_range (0, victim->GetWait ()) == 0) {
			do_recall (victim, "");
			return rNONE;
		}
	}

	// Wimp out?
	if (npcvict && dam > 0) {
		if ((victim->IsWimpy () && number_bits (1) == 0
		  && victim->GetHp () < victim->GetMaxHp () / 2)
		  || (victim->IsCharmed () && victim->GetMaster ()
		  && victim->GetMaster ()->GetInRoom () != victim->GetInRoom ())) {
			start_fearing (victim, ch);
			stop_hunting (victim);
			do_flee (victim, "");
		}
	}

	if (! npcvict && victim->GetHp () > 0
		&& victim->GetHp () <= victim->GetWimpLevel ()
		&& victim->GetWait () == 0)
			do_flee (victim, "");

	else if (! npcvict && victim->IsAction (PLR_FLEE))
		do_flee (victim, "");

	return rNONE;
}



BOOL is_safe (CCharacter *ch, CCharacter *victim)
{
	// Thx Josh!
	if (ch->GetFightWho () == ch)
		return FALSE;

	if (victim->GetInRoom ()->IsSafe ()) {
		set_char_color (AT_MAGIC, ch);
		ch->SendText ("A magical force prevents you from attacking.\n\r");
		return TRUE;
	}

	if (ch->IsPacifist ()) {			// Fireblade
		set_char_color (AT_MAGIC, ch);
		ch->SendText ("You are a pacifist and will not fight.\n\r");
		return TRUE;
	}

	if (ch->GetLevel () > LEVEL_IMMORTAL)
		return FALSE;

	if (victim->IsNpc () && victim->IsPacifist ()) {	// Gorog
		set_char_color (AT_MAGIC, ch);
		ch->SendTextf ("%s is a pacifist and will not fight.\n\r",
			capitalize (victim->GetShortDescr ()));
		return TRUE;
	}

	if (! ch->IsNpc () && ! victim->IsNpc ()
	  && ch != victim 
	  && victim->GetInRoom ()->GetArea ()->IsNoPkill ()) {
		set_char_color (AT_IMMORT, ch);
		ch->SendText ("The gods have forbidden player killing in this area.\n\r");
		return TRUE;
	}

	if (ch->IsNpc () || victim->IsNpc ())
		return FALSE;

	if (ch->GetAge () < 18 || ch->GetLevel () < 5) {
		set_char_color (AT_WHITE, ch);	
		ch->SendText ("You are not yet ready, needing age or experience, "
			"if not both. \n\r");
		return TRUE;
	}

	if (victim->GetAge () < 18 || victim->GetLevel () < 5) {
		set_char_color (AT_WHITE, ch);
		ch->SendText ("They are yet too young to die.\n\r");
		return TRUE;
	}

	if (ch->GetLevel () - victim->GetLevel () > 5 
	  || victim->GetLevel () - ch->GetLevel () > 5) {
		set_char_color (AT_IMMORT, ch);
		ch->SendText ("The gods do not allow murder when there is such "
			"a difference in level.\n\r");
		return TRUE;
	}

	if (get_timer (victim, TIMER_PKILLED) > 0) {
		set_char_color (AT_GREEN, ch);
		ch->SendText ("That character has died within the last 5 minutes.\n\r");
		return TRUE;
	}

	if (get_timer (ch, TIMER_PKILLED) > 0) {
		set_char_color (AT_GREEN, ch);
		ch->SendText ("You have been killed within the last 5 minutes.\n\r");
		return TRUE;
	}

	return FALSE;
}


// just verify that a corpse looting is legal
BOOL legal_loot (CCharacter *ch, CCharacter *victim)
{
	// anyone can loot mobs
	if (victim->IsNpc ())
		return TRUE;

	// non-charmed mobs can loot anything
	if (ch->IsNpc () && ! ch->GetMaster ())
		return TRUE;

	// members of different clans can loot too! -Thoric
	if (!ch->IsNpc () && !victim->IsNpc () && ch->IsPkiller () 
	  && victim->IsPkiller ()) 
		return TRUE;

	return FALSE;
}


// See if an attack justifies a KILLER flag.
void check_killer (CCharacter *ch, CCharacter *victim)
{
	// NPC's are fair game.
	if (victim->IsNpc ()) {
		if (! ch->IsNpc ()) {
			if (ch->GetPcData ()->GetClan ())
				ch->GetPcData ()->GetClan ()->mkills++;
			ch->GetPcData ()->mkills++;
			ch->GetInRoom ()->GetArea ()->mkills++;

			int	level_ratio = URANGE (1,
				ch->GetLevel () / victim->GetLevel (), 50);

			if (ch->GetPcData ()->deity) {
				if (victim->GetRace () == ch->GetPcData ()->deity->npcrace)
					adjust_favor (ch, 3, level_ratio);
				else
					if (victim->GetRace () == ch->GetPcData ()->deity->npcfoe)
						adjust_favor (ch, 17, level_ratio);
				else
					adjust_favor (ch, 2, level_ratio);
			}
		}
		return;
	}

	// If you kill yourself nothing happens.
	if (ch == victim || ch->GetLevel () >= LEVEL_IMMORTAL)
		return;

	// Any character in the arena is ok to kill.
	// Added pdeath and pkills here
	if (in_arena (ch)) {
		if (! ch->IsNpc () && ! victim->IsNpc ()) {
			ch->GetPcData ()->pkills++;
			victim->GetPcData ()->pdeaths++;
		}
		return;
	}

	// So are killers and thieves.
	if (victim->IsKiller () || victim->IsThief ()) {
		if (! ch->IsNpc ()) {
			if (ch->GetPcData ()->GetClan ())
				ch->GetPcData ()->GetClan ()->pkills++;
			ch->GetPcData ()->pkills++;
			ch->GetInRoom ()->GetArea ()->pkills++;
		}
		return;
	}

	// clan checks					-Thoric
	if (! ch->IsNpc () && ! victim->IsNpc () && ch->IsPkiller ()
	  && victim->IsPkiller ()) {
		// not of same clan? Go ahead and kill!!!
		if (! ch->GetPcData ()->GetClan ()
		  || !victim->GetPcData ()->GetClan () 
		  || (ch->GetPcData ()->GetClan ()->GetType () != CLAN_NOKILL
		  && victim->GetPcData ()->GetClan ()->GetType () != CLAN_NOKILL
		  && ch->GetPcData ()->GetClan () != victim->GetPcData ()->GetClan ())) {
			if (ch->GetPcData ()->GetClan ())
				ch->GetPcData ()->GetClan ()->pkills++;
			ch->GetPcData ()->pkills++;
			ch->SetHp (ch->GetMaxHp ());
			ch->SetMana (ch->GetMaxMana ());
			ch->SetMove (ch->GetMaxMove ());
			if (ch->GetPcData ())
				ch->GetPcData ()->condition [COND_BLOODTHIRST] =
					10 + ch->GetLevel ();
			update_pos (victim);

			if (victim != ch) {
				act (AT_MAGIC, "Bolts of blue energy rise from the corpse, "
					"seeping into $n.", ch, victim->GetName (), NULL, TO_ROOM);
				act (AT_MAGIC, "Bolts of blue energy rise from the corpse, "
					"seeping into you.", ch, victim->GetName (), NULL, TO_CHAR); 
			}
			if (victim->GetPcData ()->GetClan ())
				victim->GetPcData ()->GetClan ()->pdeaths++;
			victim->GetPcData ()->pdeaths++;   
			adjust_favor (victim, 11, 1);
			adjust_favor (ch, 2, 1);
			add_timer (victim, TIMER_PKILLED, 115, NULL, 0);
			WAIT_STATE (victim, 3 * PULSE_VIOLENCE);
			// SET_BIT (victim->act, PLR_PK);
			return;
		}
	}

	// Charm-o-rama.
	if (ch->IsCharmed ()) {
		if (! ch->GetMaster ()) {
			bug ("Check_killer: %s bad AFF_CHARM",
				ch->IsNpc () ? ch->GetShortDescr () : ch->GetName ());
			affect_strip (ch, gsn_charm_person);
			ch->ClrCharmed ();
			return;
		}

		// stop_follower (ch);
		if (ch->GetMaster ())
			check_killer (ch->GetMaster (), victim);
		return;
	}

	// NPC's are cool of course  (as long as not charmed).
	// Hitting yourself is cool too  (bleeding).
	// So is being immortal  (Alander's idea).
	// And current killers stay as they are.
	if (ch->IsNpc ()) {
		if (! victim->IsNpc ()) {
			if (victim->GetPcData ()->GetClan ())
				victim->GetPcData ()->GetClan ()->mdeaths++;
			victim->GetPcData ()->mdeaths++;
			victim->GetInRoom ()->GetArea ()->mdeaths++;

			int level_ratio = URANGE (1,
				ch->GetLevel () / victim->GetLevel (), 50);
			if (victim->GetPcData ()->deity) {
				if (ch->GetRace () == victim->GetPcData ()->deity->npcrace)
					adjust_favor (victim, 12, level_ratio);
				else
					if (ch->GetRace () == victim->GetPcData ()->deity->npcfoe)
						adjust_favor (victim, 15, level_ratio);
				else
					adjust_favor (victim, 11, level_ratio);
			}
		}
		return;
	}

	if (! ch->IsNpc ()) {
		if (ch->GetPcData ()->GetClan ())
			ch->GetPcData ()->GetClan ()->illegal_pk++;
		ch->GetPcData ()->illegal_pk++;
		ch->GetInRoom ()->GetArea ()->illegal_pk++;
	}

	if (! victim->IsNpc ()) {
		if (victim->GetPcData ()->GetClan ())
			victim->GetPcData ()->GetClan ()->pdeaths++;
		victim->GetPcData ()->pdeaths++;
		victim->GetInRoom ()->GetArea ()->pdeaths++;
	}

	if (ch->IsKiller ())
		return;

	set_char_color (AT_WHITE, ch);
	ch->SendText ("A strange feeling grows deep inside you, and a tingle "
		"goes up your spine...\n\r");
	set_char_color (AT_IMMORT, ch);
	ch->SendText ("A deep voice booms inside your head, 'Thou shall now "
		"be known as a deadly murderer!!!'\n\r");
	set_char_color (AT_WHITE, ch);
	ch->SendText ("You feel as if your soul has been revealed for all to "
		"see.\n\r");
	ch->SetActBit (PLR_KILLER);

	if (ch->IsAttacker ())
		ch->ClrAttacker ();
	save_char_obj (ch);
}


// See if an attack justifies a ATTACKER flag.
void check_attacker (CCharacter *ch, CCharacter *victim)
{
	// Made some changes to this function Apr 6/96 to reduce the prolifiration
	// of attacker flags in the realms. -Narn

	// NPC's are fair game.
	// So are killers and thieves.
	if (victim->IsNpc () || victim->IsKiller () || victim->IsThief ())
		return;

	// deadly char check
	if (! ch->IsNpc () && ! victim->IsNpc ()
		&& ch->CanPkill () && victim->CanPkill ())
			return;

	// Charm-o-rama.
	if (ch->IsCharmed ()) {
		if (! ch->GetMaster ()) {
			bug ("Check_attacker: %s bad AFF_CHARM",
				ch->IsNpc () ? ch->GetShortDescr () : ch->GetName ());
			affect_strip (ch, gsn_charm_person);
			ch->ClrCharmed ();
			return;
		}

		// Won't have charmed mobs fighting give the master an attacker 
		// flag.  The killer flag stays in, and I'll put something in 
		// do_murder. -Narn
		// SET_BIT (ch->GetMaster ()->act, PLR_ATTACKER);
		// stop_follower (ch);
		return;
	}

	// NPC's are cool of course  (as long as not charmed).
	// Hitting yourself is cool too  (bleeding).
	// So is being immortal  (Alander's idea).
	// And current killers stay as they are.
	if (ch->IsNpc () || ch == victim || ch->IsImmortal ()
		|| ch->IsAttacker () || ch->IsKiller ())
			return;

	ch->SetAttacker ();
	save_char_obj (ch);
}


// Set position of a victim.
void update_pos (CCharacter *victim)
{
	if (! victim) {
		bug ("update_pos: null victim");
		return;
	}

	if (victim->GetHp () > 0) {
		if (victim->GetPosition () <= POS_STUNNED)
			victim->SetPosition (POS_STANDING);
		if (victim->IsParalysed ())
			victim->SetPosition (POS_STUNNED);
		return;
	}

	if (victim->IsNpc () || victim->GetHp () <= -11) {
		if (victim->mount) {
			act (AT_ACTION, "$n falls from $N.",
				victim, NULL, victim->mount, TO_ROOM);
			victim->mount->ClrActBit (ACT_MOUNTED);
			victim->mount = NULL;
		}
		victim->SetPosition (POS_DEAD);
		return;
	}

	if (victim->GetHp () <= -6)
		victim->SetPosition (POS_MORTAL);
	else if (victim->GetHp () <= -3)
		victim->SetPosition (POS_INCAP);
	else
		victim->SetPosition (POS_STUNNED);

	if (victim->GetPosition () > POS_STUNNED && victim->IsParalysed ())
		victim->SetPosition (POS_STUNNED);

	if (victim->mount) {
		act (AT_ACTION, "$n falls unconscious from $N.",
			victim, NULL, victim->mount, TO_ROOM);
		victim->mount->ClrActBit (ACT_MOUNTED);
		victim->mount = NULL;
	}
}


// Start fights.
void set_fighting (CCharacter *ch, CCharacter *victim)
{
	CFightData	*fight;

	if (ch->GetFightData ()) {
		bug ("Set_fighting: %s -> %s  (already fighting %s)",
			ch->GetName (), victim->GetName (),
			ch->GetFightData ()->who->GetName ());
		return;
	}

	if (ch->IsSleeping ())
		affect_strip (ch, gsn_sleep);

	// Limit attackers -Thoric
	if (victim->num_fighting > max_fight (victim)) {
		ch->SendText ("There are too many people fighting for you to join in.\n\r");
		return;
	}

	fight = new CFightData;
	fight->who = victim;
	fight->xp = (int) (xp_compute (ch, victim) * 0.85);
	fight->align = align_compute (ch, victim);

	if (! ch->IsNpc () && victim->IsNpc ())
		fight->timeskilled = times_killed (ch, victim);

	ch->num_fighting = 1;
	ch->SetFightData (fight);
	victim->num_fighting++;

	if (ch->IsNpc ())
		ch->SetPosition (POS_FIGHTING);
	else switch (ch->GetStyle ()) {
	  case STYLE_EVASIVE: 
		ch->SetPosition (POS_EVASIVE);
		break;
	  case STYLE_DEFENSIVE: 
		ch->SetPosition (POS_DEFENSIVE);
		break;
	  case STYLE_AGGRESSIVE: 
		ch->SetPosition (POS_AGGRESSIVE);
		break;
	  case STYLE_BERSERK: 
		ch->SetPosition (POS_BERSERK);
		break;
	  default:
		ch->SetPosition (POS_FIGHTING);
	}

	if (victim->switched && victim->switched->IsPossessed ()) {
		victim->switched->SendText ("You are disturbed!\n\r");
		do_return (victim->switched, "");
	}
}


void free_fight (CCharacter *ch)
{
	if (! ch) {
		bug ("Free_fight: null ch!");
		return;
	}
	if (ch->GetFightData ()) {
		if (! char_died (ch->GetFightData ()->who))
			--ch->GetFightData ()->who->num_fighting;
		delete ch->GetFightData ();
	}
	ch->SetFightData (NULL);
	if (ch->mount)
		ch->SetPosition (POS_MOUNTED);
	else
		ch->SetPosition (POS_STANDING);

	// Berserk wears off after combat. -- Altrag
	if (ch->IsBeserk ()) {
		affect_strip (ch, gsn_berserk);
		set_char_color (AT_WEAROFF, ch);
		ch->SendText (SkillTable.GetSkill (gsn_berserk)->GetOffMsg ());
		ch->SendText ("\n\r");
	}
}


// Stop fights.
void stop_fighting (CCharacter *ch, BOOL fBoth)
{
	CCharacter	*fch;

	free_fight (ch);
	update_pos (ch);

	if (!fBoth)   // major short cut here by Thoric
		return;

	for (fch = first_char; fch; fch = fch->GetNext ()) {
		if (fch->GetFightWho () == ch) {
			free_fight (fch);
			update_pos (fch);
		}
	}
}



// Improved Death_cry contributed by Diavolo.
// Additional improvement by Thoric  (and removal of turds... sheesh!)  
void death_cry (CCharacter *ch)
{
	CRoomIndexData	*was_in_room;
	char			*msg;
	CExitData		*pexit;
	int				vnum;

	if (! ch) {
		bug ("DEATH_CRY: null ch!");
		return;
	}

	vnum = 0;
	switch (number_bits (4)) {
	  default:
		msg  = "You hear $n's death cry.";
		break;
	  case 0:
		msg  = "$n hits the ground ... DEAD.";
		break;
	  case 1:
		msg  = "$n splatters blood on your armor.";
		break;
	  case 2:
		if (ch->HasBodypart (PART_GUTS)) {
			msg = "$n's guts spill all over the ground.";
			vnum = OBJ_VNUM_SPILLED_GUTS;
		}
		else msg = "$n collapses lifeless to the ground.";
		break;
	  case 3:
		if (ch->HasBodypart (PART_HEAD)) {
			msg = "$n's severed head plops on the ground.";
			vnum = OBJ_VNUM_SEVERED_HEAD;
		}
		else msg = "You hear $n's death cry.";
		break;
	  case 4:
		if (ch->HasBodypart (PART_HEART)) {
			msg = "$n's heart is torn from $s chest.";
			vnum = OBJ_VNUM_TORN_HEART;
		}
		else msg = "$n collapses lifeless to the ground.";
		break;
	  case 5:
		if (ch->HasBodypart (PART_ARMS)) {
			msg = "$n's arm is sliced from $s dead body.";
			vnum = OBJ_VNUM_SLICED_ARM;
		}
		else msg = "You hear $n's death cry.";
		break;
	  case 6:
		if (ch->HasBodypart (PART_LEGS)) {
			msg = "$n's leg is sliced from $s dead body.";
			vnum = OBJ_VNUM_SLICED_LEG;
		}
		else msg = "$n collapses lifeless to the ground.";
		break;
	}

	act (AT_CARNAGE, msg, ch, NULL, NULL, TO_ROOM);

	if (vnum) {
		char		buf [MAX_STRING_LENGTH];
		CObjData	*obj;
		const char	*name;

		name = ch->IsNpc () ? ch->GetShortDescr () : ch->GetName ();
		obj = create_object (OIdxTable.GetObj (vnum), 0);
		obj->timer = number_range (4, 7);

		sprintf (buf, obj->GetShortDescr (), name);
		obj->SetShortDescr (buf);

		sprintf (buf, obj->GetDescription (), name);
		obj->SetDescription (buf);

		obj = obj_to_room (obj, ch->GetInRoom ());
	}

	if (ch->IsNpc ())
		msg = "You hear something's death cry.";
	else
		msg = "You hear someone's death cry.";

	was_in_room = ch->GetInRoom ();
	for (pexit = was_in_room->first_exit; pexit; pexit = pexit->GetNext ()) {
		if (pexit->GetToRoom () && pexit->GetToRoom () != was_in_room) {
			ch->SetInRoom (pexit->GetToRoom ());
			act (AT_CARNAGE, msg, ch, NULL, NULL, TO_ROOM);
		}
	}
	ch->SetInRoom (was_in_room);
}


void raw_kill (CCharacter *ch, CCharacter *victim)
{
	CCharacter	*victmp;

	if (! victim) {
		bug ("raw_kill: null victim!");
		return;
	}
	// backup in case hp goes below 1
	if (!victim->IsAuthed ()) {
		bug ("raw_kill: killing unauthed");
		return;
	}

	stop_fighting (victim, TRUE);

	// Take care of polymorphed chars
	if (victim->IsNpc () && victim->IsPolymorphed ()) {
		victim->GetDesc ()->m_pOriginal->RemoveFromRoom ();
		victim->GetDesc ()->m_pOriginal->SendToRoom (victim->GetInRoom ());
		victmp = victim->GetDesc ()->m_pOriginal;
		do_revert (victim, "");
		raw_kill (ch, victmp);
		return;
	}

	mprog_death_trigger (ch, victim);
	if (char_died (victim))
		return;

	rprog_death_trigger (ch, victim);
	if (char_died (victim))
		return;

	make_corpse (victim, ch);

	if (victim->GetSectorType () == SECT_OCEANFLOOR
	  || victim->GetSectorType () == SECT_UNDERWATER
	  || victim->GetSectorType () == SECT_WATER_SWIM
	  || victim->GetSectorType () == SECT_WATER_NOSWIM)
		act (AT_BLOOD, "$n's blood slowly clouds the surrounding water.",
			victim, NULL, NULL, TO_ROOM);
	else if (victim->GetSectorType () == SECT_AIR)
		act( AT_BLOOD, "$n's blood sprays wildly through the air.",
			victim, NULL, NULL, TO_ROOM);
	else
		make_blood (victim);

	if (victim->IsNpc ()) {
		victim->GetMobIndex ()->killed++;
		extract_char (victim, TRUE);
		victim = NULL;
		return;
	}

	set_char_color (AT_DIEMSG, victim);
	if (victim->GetPcData ()->mdeaths + victim->GetPcData ()->pdeaths < 3)
		do_help (victim, "new_death");
	else
		do_help (victim, "_DIEMSG_");

	extract_char (victim, FALSE);
	if (! victim) {
		bug ("oops! raw_kill: extract_char destroyed pc char");
		return;
	}

	while (! victim->m_AffectList.IsEmpty ())
		affect_remove (victim, victim->m_AffectList.RemoveTail ());

	CRaceData	&Ra = *RaceTable.GetRaceData (victim->GetRace ());
	victim->SetAffectFlags (Ra.GetAffectFlags ());
	victim->SetAttackFlags (Ra.GetAttackFlags ());
	victim->SetDefenseFlags (Ra.GetDefenseFlags ());
	victim->SetResistFlags (0);
	victim->SetSusceptFlags (0);
	victim->SetImmuneFlags (0);
	victim->SetCarryWeight (0);
	victim->SetArmor (100 + Ra.GetAcPlus ());
	victim->mod_str	= 0;
	victim->mod_dex	= 0;
	victim->mod_wis	= 0;
	victim->mod_int	= 0;
	victim->mod_con	= 0;
	victim->mod_cha	= 0;
	victim->mod_lck = 0;
	victim->SetDamroll (0);
	victim->SetHitroll (0);
	victim->SetMentalState (-10);
	victim->SetAlignment (URANGE (-1000, victim->GetAlignment (), 1000));
	victim->saving_poison_death = Ra.GetSavingPoisonDeath ();
	victim->saving_wand = Ra.GetSavingWand ();
	victim->saving_para_petri = Ra.GetSavingParaPetri ();
	victim->saving_breath = Ra.GetSavingBreath ();
	victim->saving_spell_staff = Ra.GetSavingSpellstaff ();
	victim->SetPosition (POS_RESTING);
	victim->SetHp (UMAX (1, victim->GetHp ()));
	victim->SetMana (UMAX (1, victim->GetMana ()));
	victim->SetMove (UMAX (1, victim->GetMove ()));

	// Pardon crimes...						-Thoric
	if (victim->IsAction (PLR_KILLER)) {
		victim->ClrActBit (PLR_KILLER);
		victim->SendText ("The gods have pardoned you for your murderous acts.\n\r");
	}
	if (victim->IsAction (PLR_THIEF)) {
		victim->ClrActBit (PLR_THIEF);
		victim->SendText ("The gods have pardoned you for your thievery.\n\r");
	}
	victim->GetPcData ()->condition [COND_FULL]   = 12;
	victim->GetPcData ()->condition [COND_THIRST] = 12;
	if (victim->IsVampire ())
		victim->GetPcData ()->condition [COND_BLOODTHIRST] =
			victim->GetLevel () / 2;

	if (SysData.IsSaveOnDeath ())
	save_char_obj (victim);
}



void group_gain (CCharacter *ch, CCharacter *victim)
{
	int		xp;
	int		members;

	// Monsters don't get kill xp's or alignment changes.
	// Dying of mortal wounds or poison doesn't give xp to anyone!
	if (ch->IsNpc () || victim == ch)
		return;

	members = 0;
	CCharacter	*gch = ch->GetInRoom ()->first_person;

	for (; gch; gch = gch->GetNextInRoom ()) {
		if (is_same_group (gch, ch))
			members++;
	}

	if (members == 0) {
		bug ("Group_gain: members = 0.");
		members = 1;
	}

	CCharacter	*lch = ch->GetLeader () ? ch->GetLeader () : ch;
	gch = ch->GetInRoom ()->first_person;

	for (; gch; gch = gch->GetNextInRoom ()) {
		CObjData	*obj;

		if (! is_same_group (gch, ch))
			continue;

		if (gch->GetLevel () - lch->GetLevel () >  8) {
			gch->SendText ("You are too high for this group.\n\r");
			continue;
		}

		if (gch->GetLevel () - lch->GetLevel () < -8) {
			gch->SendText ("You are too low for this group.\n\r");
			continue;
		}

		xp = (int) (xp_compute (gch, victim) * 0.1765) / members;
		if (! gch->GetFightData ())
			xp /= 2;
		gch->SetAlignment (align_compute (gch, victim));
		gch->SendTextf ("You receive %d experience points.\n\r", xp);
		gain_exp (gch, xp);

		POSITION	Cpos = ch->GetHeadCarryPos ();
		while (obj = ch->GetNextCarrying (Cpos)) {
			if (obj->wear_loc == WEAR_NONE)
				continue;

			if ((obj->IsAntiEvil () && ch->IsEvil ())
			  || (obj->IsAntiGood () && ch->IsGood ())
			  || (obj->IsAntiNeutral () && ch->IsNeutral ())) {
				act (AT_MAGIC, "You are zapped by $p.", ch, obj, NULL, TO_CHAR);
				act (AT_MAGIC, "$n is zapped by $p.",   ch, obj, NULL, TO_ROOM);

				obj_from_char (obj);
				obj = obj_to_room (obj, ch->GetInRoom ());
				oprog_zap_trigger (ch, obj);			// mudprogs
				if (char_died (ch))
					return;
			}
		}
	}
}


int align_compute (CCharacter *gch, CCharacter *victim)
{
	int		CharAlign = gch->GetAlignment ();
	int		VictAlign = victim->GetAlignment ();

	// 6/12/98 by RCP
	// This provides a sliding scale for alignment adjustment.  For victims
	// with low absolute alignment values, approx 33% of the victims align
	// is applied to the killer.  As victim alignment increases to 1000, the
	// percentage is decreased to 12.5%.
	// Note: All numbers except 200 are * 10 to avoid integer rounding errors
	//       Basic formula is -va / (3 + va/200).
	int	AlignChange = -10 * VictAlign / (30 + abs (10 * VictAlign)/200);

	return URANGE (-1000, CharAlign + AlignChange, 1000);
}


//int align_compute (CCharacter *gch, CCharacter *victim)
//{
//	int		align, newalign;
//
//	align = gch->GetAlignment () - victim->GetAlignment ();
//
//	if (align >  500)
//		newalign = UMIN (gch->GetAlignment () + (align-500)/4, 1000);
//	else if (align < -500)
//		newalign = UMAX (gch->GetAlignment () + (align+500)/4, -1000);
//	else
//		newalign = gch->GetAlignment () - (int) (gch->GetAlignment () / 4);
//
//	return newalign;
//}


// Calculate how much XP gch should gain for killing victim
// Lots of redesigning for new exp system by Thoric
int xp_compute (CCharacter *gch, CCharacter *victim)
{
	int		align;
	int		xp;
	int		xp_ratio;
	int		gchlev = gch->GetLevel ();

	xp = (get_exp_worth (victim)
		* URANGE (0, (victim->GetLevel () - gchlev) + 10, 13)) / 10;
	align = gch->GetAlignment () - victim->GetAlignment ();

	// bonus for attacking opposite alignment
	if (align > 990 || align < -990)
		xp = (xp*5) >> 2;
	else
		// penalty for good attacking same alignment
		if (gch->GetAlignment () > 300 && align < 250)
			xp = (xp*3) >> 2;

	xp = number_range ((xp*3) >> 2, (xp*5) >> 2);

	// get 1/4 exp for players					-Thoric
	if (! victim->IsNpc ())
		xp /= 4;

	// reduce exp for killing the same mob repeatedly		-Thoric
	else if (! gch->IsNpc ()) {
		int times = times_killed (gch, victim);

		if (times >= 20)
			xp = 0;
		else if (times) {
			xp = (xp * (20-times)) / 20;
			if (times > 15)
				xp /= 3;
			else if (times > 10)
				xp >>= 1;
		}
	}

	// semi-intelligent experienced player vs. novice player xp gain
	// "bell curve"ish xp mod by Thoric
	// based on time played vs. level
	if (! gch->IsNpc () && gchlev > 5) {
		xp_ratio = (int) gch->GetPlayed () / gchlev;
		if (xp_ratio > 20000)				// 5/4
			xp = (xp*5) >> 2;
		else if (xp_ratio < 16000)			// 3/4
			xp = (xp*3) >> 2;
		else if (xp_ratio < 10000)			// 1/2
			xp >>= 1;
		else if (xp_ratio < 5000)			// 1/4
			xp >>= 2;
		else if (xp_ratio < 3500)			// 1/8
			xp >>= 3;
		else if (xp_ratio < 2000)			// 1/16
			xp >>= 4;
	}

	// Level based experience gain cap.  Cannot get more experience for
	// a kill than the amount for your current experience level   -Thoric
	// This does not seem realistic to me (RCP)
//	return URANGE (0, xp, exp_level (gch, gchlev+1) -
//		exp_level (gch, gchlev));

	return max (0, xp);
}


char * s_blade_messages [24] =
{
	"miss", "barely scratch", "scratch", "nick", "cut", "hit", "tear",
	"rip", "gash", "lacerate", "hack", "maul", "rend", "decimate",
	"_mangle_", "_devastate_", "_cleave_", "_butcher_", "DISEMBOWEL",
	"DISFIGURE", "GUT", "EVISCERATE", "* SLAUGHTER *", "*** ANNIHILATE ***"
};

char * p_blade_messages [24] =
{
	"misses", "barely scratches", "scratches", "nicks", "cuts", "hits",
	"tears", "rips", "gashes", "lacerates", "hacks", "mauls", "rends",
	"decimates", "_mangles_", "_devastates_", "_cleaves_", "_butchers_",
	"DISEMBOWELS", "DISFIGURES", "GUTS", "EVISCERATES", "* SLAUGHTERS *",
	"*** ANNIHILATES ***"
};

char * s_blunt_messages [24] =
{
	"miss", "barely scuff", "scuff", "pelt", "bruise", "strike", "thrash",
	"batter", "flog", "pummel", "smash", "maul", "bludgeon", "decimate",
	"_shatter_", "_devastate_", "_maim_", "_cripple_", "MUTILATE", "DISFIGURE",
	"MASSACRE", "PULVERIZE", "* OBLITERATE *", "*** ANNIHILATE ***"
};

char * p_blunt_messages [24] =
{
	"misses", "barely scuffs", "scuffs", "pelts", "bruises", "strikes",
	"thrashes", "batters", "flogs", "pummels", "smashes", "mauls",
	"bludgeons", "decimates", "_shatters_", "_devastates_", "_maims_",
	"_cripples_", "MUTILATES", "DISFIGURES", "MASSACRES", "PULVERIZES",
	"* OBLITERATES *", "*** ANNIHILATES ***"
};

char * s_generic_messages [24] =
{
	"miss", "brush", "scratch", "graze", "nick", "jolt", "wound",
	"injure", "hit", "jar", "thrash", "maul", "decimate", "_traumatize_",
	"_devastate_", "_maim_", "_demolish_", "MUTILATE", "MASSACRE",
	"PULVERIZE", "DESTROY", "* OBLITERATE *", "*** ANNIHILATE ***",
	"**** SMITE ****"
};

char * p_generic_messages [24] =
{
	"misses", "brushes", "scratches", "grazes", "nicks", "jolts", "wounds",
	"injures", "hits", "jars", "thrashes", "mauls", "decimates", "_traumatizes_",
	"_devastates_", "_maims_", "_demolishes_", "MUTILATES", "MASSACRES",
	"PULVERIZES", "DESTROYS", "* OBLITERATES *", "*** ANNIHILATES ***",
	"**** SMITES ****"
};

char ** const s_message_table [18] = {
	s_generic_messages,		// hit
	s_blade_messages,		// slice
	s_blade_messages,		// stab
	s_blade_messages,		// slash
	s_blunt_messages,		// whip
	s_blade_messages,		// claw
	s_generic_messages,		// blast
	s_blunt_messages,		// pound
	s_blunt_messages,		// crush
	s_generic_messages,		// grep
	s_blade_messages,		// bite
	s_blade_messages,		// pierce
	s_blunt_messages,		// suction
	s_generic_messages,		// bolt
	s_generic_messages,		// arrow
	s_generic_messages,		// dart
	s_generic_messages,		// stone
	s_generic_messages		// pea
};

char ** const p_message_table [18] = {
	p_generic_messages,		// hit
	p_blade_messages,		// slice
	p_blade_messages,		// stab
	p_blade_messages,		// slash
	p_blunt_messages,		// whip
	p_blade_messages,		// claw
	p_generic_messages,		// blast
	p_blunt_messages,		// pound
	p_blunt_messages,		// crush
	p_generic_messages,		// grep
	p_blade_messages,		// bite
	p_blade_messages,		// pierce
	p_blunt_messages,		// suction
	p_generic_messages,		// bolt
	p_generic_messages,		// arrow
	p_generic_messages,		// dart
	p_generic_messages,		// stone
	p_generic_messages		// pea
};


// Revamped by Thoric to be more realistic
void dam_message (CCharacter *ch, CCharacter *victim, int dam, int dt,
				  CObjData* pObj /* = NULL */)
{
	char		buf1 [256], buf2 [256], buf3 [256];
	const char	*vs;
	const char	*vp;
	const char	*attack;
	char		punct;
	short		dampc;
	CSkill		*skill = NULL;
	BOOL		gcflag = FALSE;
	BOOL		gvflag = FALSE;
	int			d_index, w_index;

	if (! dam)
		dampc = 0;
	else
		dampc = ((dam * 1000) / victim->GetMaxHp ()) +
			(50 - ((victim->GetHp () * 50) / victim->GetMaxHp ()));

	CRoomIndexData	*pWasInRoom = NULL;
	if (ch->GetInRoom () != victim->GetInRoom ()) {
		pWasInRoom = ch->GetInRoom ();
		ch->RemoveFromRoom ();
		ch->SendToRoom (victim->GetInRoom ());
	}

	// Get the weapon index
	if (dt > 0 && dt < SkillTable.GetCount ())
		w_index = 0;
	else if (dt >= TYPE_HIT && dt < TYPE_HIT + DIM (attack_table))
		w_index = dt - TYPE_HIT;
	else {
		bug ("Dam_message: bad dt %d from %s in %d.",
			dt, ch->GetName (), ch->GetInRoom ()->vnum);
		dt = TYPE_HIT;
		w_index = 0;
	}

	// get the damage index
	if (dam == 0)
		d_index = 0;
	else if (dampc < 0)
		d_index = 1;
	else if (dampc <= 100)
		d_index = 1 + dampc/10;
	else if (dampc <= 200)
		d_index = 11 + (dampc - 100) / 20;
	else if (dampc <= 900)
		d_index = 16 + (dampc - 200) / 100;
	else
		d_index = 23;

	// Lookup the damage message
	vs = s_message_table [w_index][d_index];
	vp = p_message_table [w_index][d_index];

	punct = (dampc <= 30) ? '.' : '!';

	if (dam == 0 && (! ch->IsNpc () && ch->IsGagged ()))
		gcflag = TRUE;

	if (dam == 0 && (! victim->IsNpc () && victim->IsGagged ()))
		gvflag = TRUE;

	if (dt >=0 && dt < SkillTable.GetCount ())
		skill = SkillTable.GetSkill (dt);
	if (dt == TYPE_HIT) {
		sprintf (buf1, "$n %s $N%c",  vp, punct);
		sprintf (buf2, "You %s $N%c", vs, punct);
		sprintf (buf3, "$n %s you%c", vp, punct);
	}
	else if (dt > TYPE_HIT && is_wielding_poisoned (ch)) {
		if (dt < TYPE_HIT + DIM (attack_table))
			attack = attack_table [dt - TYPE_HIT];
		else {
			bug ("Dam_message: bad dt %d from %s in %d.", dt,
				ch->GetName (), ch->GetInRoom ()->vnum);
			dt = TYPE_HIT;
			attack = attack_table [0];
		}

		sprintf (buf1, "$n's poisoned %s %s $N%c", attack, vp, punct);
		sprintf (buf2, "Your poisoned %s %s $N%c", attack, vp, punct);
		sprintf (buf3, "$n's poisoned %s %s you%c", attack, vp, punct); 
	} else {
		if (skill) {
			attack = skill->GetDamageMsg ();
			if (dam == 0) {
				BOOL	found = FALSE;

				if (skill->ValidMissCharMsg ()) {
					act (AT_HIT, skill->GetMissCharMsg (), ch, NULL,
						victim, TO_CHAR);
					found = TRUE;
				}
				if (skill->ValidMissVictMsg ()) {
					act (AT_HITME, skill->GetMissVictMsg (), ch, NULL,
						victim, TO_VICT);
					found = TRUE;
				}
				if (skill->ValidMissRoomMsg ()) {
					if (strcmp (skill->GetMissRoomMsg (), "supress"))
						act (AT_ACTION, skill->GetMissRoomMsg (), ch, NULL,
							victim, TO_NOTVICT);
					found = TRUE;
				}
				if (found) {			// miss message already sent
					if (pWasInRoom) {
						ch->RemoveFromRoom ();
						ch->SendToRoom (pWasInRoom);
					}
					return;
				}
			} else {
				if (skill->ValidHitCharMsg ())
					act (AT_HIT, skill->GetHitCharMsg (), ch, NULL,
						victim, TO_CHAR);
				if (skill->ValidHitVictMsg ())
					act (AT_HITME, skill->GetHitVictMsg (), ch, NULL,
						victim, TO_VICT);
				if (skill->ValidHitRoomMsg ())
					act (AT_ACTION, skill->GetHitRoomMsg (), ch, NULL,
						victim, TO_NOTVICT);
			}
		}
		else if (dt >= TYPE_HIT && dt < TYPE_HIT + DIM (attack_table)) {
			if (pObj)
				attack = pObj->GetShortDescr ();
			else
				attack = attack_table [dt - TYPE_HIT];
		} else {
			bug ("Dam_message: bad dt %d from %s in %d.", dt,
				ch->GetName (), ch->GetInRoom ()->vnum);
			dt = TYPE_HIT;
			attack = attack_table [0];
		}

		sprintf (buf1, "$n's %s %s $N%c",  attack, vp, punct);
		sprintf (buf2, "Your %s %s $N%c",  attack, vp, punct);
		sprintf (buf3, "$n's %s %s you%c", attack, vp, punct);
	}


	act (AT_ACTION, buf1, ch, NULL, victim, TO_NOTVICT);
	if (!gcflag) act (AT_HIT, buf2, ch, NULL, victim, TO_CHAR);
	if (!gvflag) act (AT_HITME, buf3, ch, NULL, victim, TO_VICT);

	if (pWasInRoom) {
		ch->RemoveFromRoom ();
		ch->SendToRoom (pWasInRoom);
	}
}


void do_kill (CCharacter *ch, char *argument)
{
	char		arg [MAX_INPUT_LENGTH];
	CCharacter	*victim;

	one_argument (argument, arg);

	if (arg [0] == '\0') {
		ch->SendText ("Kill whom?\n\r");
		return;
	}

	if ((victim = get_char_room (ch, arg)) == NULL) {
		ch->SendText ("They aren't here.\n\r");
		return;
	}

	if (! victim->IsNpc ()) {
		if (! victim->IsKiller () && ! victim->IsThief ()) {
			ch->SendText ("You must MURDER a player.\n\r");
			return;
		}
	}

	if (victim == ch) {
		ch->SendText ("You hit yourself.  Ouch!\n\r");
		multi_hit (ch, ch, TYPE_UNDEFINED);
		return;
	}

	if (is_safe (ch, victim))
		return;

	if (ch->IsCharmed () && ch->GetMaster () == victim) {
		act (AT_PLAIN, "$N is your beloved master.", ch, NULL, victim, TO_CHAR);
		return;
	}

	if (ch->IsFightPosition ()) {
		ch->SendText ("You do the best you can!\n\r");
		return;
	}

	WAIT_STATE (ch, 1 * PULSE_VIOLENCE);
	check_attacker (ch, victim);
	multi_hit (ch, victim, TYPE_UNDEFINED);
}



void do_murde (CCharacter *ch, char *argument)
{
	ch->SendText ("If you want to MURDER, spell it out.\n\r");
	return;
}



void do_murder (CCharacter *ch, char *argument)
{
	char		buf [MAX_STRING_LENGTH];
	char		arg [MAX_INPUT_LENGTH];
	CCharacter	*victim;

	one_argument (argument, arg);

	if (arg [0] == '\0') {
		ch->SendText ("Murder whom?\n\r");
		return;
	}

	if ((victim = get_char_room (ch, arg)) == NULL) {
		ch->SendText ("They aren't here.\n\r");
		return;
	}

	if (victim == ch) {
		ch->SendText ("Suicide is a mortal sin.\n\r");
		return;
	}

	if (is_safe (ch, victim))
		return;

	if (ch->IsCharmed ()) {
		if (ch->GetMaster () == victim) {
			act (AT_PLAIN, "$N is your beloved master.", ch, NULL,
				victim, TO_CHAR);
			return;
		} else {
			if (ch->GetMaster ())
				ch->GetMaster ()->SetAttacker ();
		}
	}

	if (ch->IsFightPosition ()) {
		ch->SendText ("You do the best you can!\n\r");
		return;
	}

	if (! victim->IsNpc () && ch->IsNice ()) {
		ch->SendText ("You feel too nice to do that!\n\r");
		return;
	}
	// if (! victim->IsNpc () && IS_SET (victim->act, PLR_PK))

	if (! victim->IsNpc ()) {
		sprintf (log_buf, "%s: murder %s.", ch->GetName (),
			victim->GetName ());
		gpDoc->LogString (log_buf, LOG_NORMAL, ch->GetLevel ());
	}

	WAIT_STATE (ch, 1 * PULSE_VIOLENCE);
	sprintf (buf, "Help!  I am being attacked by %s!", 
		ch->IsNpc () ? ch->GetShortDescr () : ch->GetName ());
	if (victim->IsPkiller ())
		do_wartalk (victim, buf);
	else
		do_yell (victim, buf);

	check_illegal_pk (ch, victim);
	check_attacker (ch, victim);
	multi_hit (ch, victim, TYPE_UNDEFINED);
}


BOOL in_arena (CCharacter *ch)
{
//	if (IS_SET (ch->in_room->room_flags, ROOM_ARENA))
//		return TRUE;
//	if (IS_SET (ch->in_room->area->flags, AFLAG_FREEKILL))
//		return TRUE;

	if (ch->GetInRoom ()->GetArea ()->m_Filename == "arena.are")
		return TRUE;

	if (ch->GetInRoom ()->vnum < 29 || ch->GetInRoom ()->vnum > 43)
		return FALSE;

	return TRUE;
}


BOOL check_illegal_pk (CCharacter *ch, CCharacter *victim)
{
	if (! victim->IsNpc () && ! ch->IsNpc ()) {
		if ((! victim->IsPkiller () 
		  || ch->GetLevel () - victim->GetLevel () > 10 
		  || ! ch->IsPkiller ()) 
		  && !in_arena (ch)
		  && ch != victim 
		  && ! (ch->IsImmortal () && victim->IsImmortal ())) {
			sprintf (log_buf, "%s performing illegal pkill on %s at %d",
				(ch->IsNpc () ? ch->GetShortDescr () : ch->GetName ()),
				victim->GetName (), victim->GetInRoom ()->vnum);
			gpDoc->LogString (log_buf, LOG_PLAYER);
			to_channel (log_buf, CHANNEL_MONITOR, "Monitor", LEVEL_IMMORTAL);
			return TRUE;
		}
	}
	return FALSE;
}


void do_flee (CCharacter *ch, char *argument)
{
	CRoomIndexData *was_in;
	CRoomIndexData *now_in;
	char buf[MAX_STRING_LENGTH];
	int attempt, los;
	short door;
	CExitData *pexit;

	if (! ch->GetFightWho ()) {
		if (ch->IsFightPosition ()) {
			if (ch->mount)
				ch->SetPosition (POS_MOUNTED);
			else
				ch->SetPosition (POS_STANDING);
		}
		ch->SendText ("You aren't fighting anyone.\n\r");
		return;
	}

	if (ch->IsBeserk ()) {
		ch->SendText ("You aren't thinking very clearly...\n\r");
		return;
	}

	if (ch->GetMove () <= 0) {
		ch->SendText ("You're too exhausted to flee from combat!\n\r");
		return;
	}

	// No fleeing while more aggressive than standard or hurt. - Haus
	if (ch->IsNpc () && ch->GetPosition () < POS_FIGHTING) {
		ch->SendText ("Not right now ...\n\r");
		return;
	} 

	if (ch->IsNpc () && ch->GetPosition () <= POS_SLEEPING)
		return;

	was_in = ch->GetInRoom ();
	for (attempt = 0; attempt < 8; attempt++) {
		door = number_door ();
		if ((pexit = get_exit (was_in, door)) == NULL
			|| !pexit->GetToRoom ()
//			|| IS_SET (pexit->exit_info, EX_NOFLEE)
			|| (pexit->IsClosed () && ! ch->CanPass ())
			|| (ch->IsNpc () && pexit->GetToRoom ()->IsNoMob ()))
				continue;

		affect_strip  (ch, gsn_sneak);
		ch->ClrSneak ();
		if (ch->mount && ch->mount->GetFightData ())
			stop_fighting (ch->mount, TRUE);
		move_char (ch, pexit, 0);
		if ((now_in = ch->GetInRoom ()) == was_in)
			continue;

		ch->SetInRoom (was_in);
		act (AT_FLEE, "$n flees head over heels!", ch, NULL, NULL, TO_ROOM);
		ch->SetInRoom (now_in);
		act (AT_FLEE, "$n glances around for signs of pursuit.", ch,
			NULL, NULL, TO_ROOM);

		if (! ch->IsNpc ()) {
			CCharacter	*wf = ch->GetFightWho ();
			los = (int) ((exp_level (ch, ch->GetLevel ()+1) -
				exp_level (ch, ch->GetLevel ())) * 0.03);
			
			if (! ch->IsImmortal ()) {
				sprintf (buf, "You flee head over heels from combat, "
					"losing %d experience.", los);
				act (AT_FLEE, buf, ch, NULL, NULL, TO_CHAR);
			}
			else
				act (AT_FLEE, "You flee head over heels from combat!",
					ch, NULL, NULL, TO_CHAR);

			gain_exp (ch, 0 - los);
			if (wf && ch->GetPcData ()->deity) {
				int level_ratio =
					URANGE (1, wf->GetLevel () / ch->GetLevel (), 50);
				if (wf && wf->GetRace () ==
					ch->GetPcData ()->deity->npcrace)
						adjust_favor (ch, 1, level_ratio);
				else
					if (wf && wf->GetRace () ==
						ch->GetPcData ()->deity->npcfoe)
							adjust_favor (ch, 16, level_ratio);
				else
					adjust_favor (ch, 0, level_ratio);
			}
		}
		stop_fighting (ch, TRUE);
		return;
	}

	los = (int) ((exp_level (ch, ch->GetLevel ()+1) -
		exp_level (ch, ch->GetLevel ())) * 0.01);
	
	if (! ch->IsImmortal ()) {
		sprintf (buf, "You attempt to flee from combat, losing %d "
			"experience.\n\r", los);
		act (AT_FLEE, buf, ch, NULL, NULL, TO_CHAR);
	}
	else act (AT_FLEE,
		"You attempt to flee from combat, but can't escape!",
		ch, NULL, NULL, TO_CHAR);

	gain_exp (ch, 0 - los);
}



void do_sla (CCharacter *ch, char *argument)
{
	ch->SendText ("If you want to SLAY, spell it out.\n\r");
	return;
}


void do_slay (CCharacter *ch, char *argument)
{
	CCharacter	*victim;
	char		arg [MAX_INPUT_LENGTH];
	char		arg2 [MAX_INPUT_LENGTH];

	argument = one_argument (argument, arg);
	one_argument (argument, arg2);
	if (arg [0] == '\0') {
		ch->SendText ("Slay whom?\n\r");
		return;
	}

	if ((victim = get_char_room (ch, arg)) == NULL) {
		ch->SendText ("They aren't here.\n\r");
		return;
	}

	if (ch == victim) {
		ch->SendText ("Suicide is a mortal sin.\n\r");
		return;
	}

	if (! victim->IsNpc ()
	  && victim->GetTrustLevel () >= ch->GetTrustLevel ()) {
		ch->SendText ("You failed.\n\r");
		return;
	}

	if (! str_cmp (arg2, "immolate")) {
		act (AT_FIRE, "Your fireball turns $N into a blazing inferno.",
			ch, NULL, victim, TO_CHAR   );
		act (AT_FIRE, "$n releases a searing fireball in your direction.",
			ch, NULL, victim, TO_VICT   );
		act (AT_FIRE, "$n points at $N, who bursts into a flaming inferno.",
			ch, NULL, victim, TO_NOTVICT);
	}

	else if (! str_cmp (arg2, "shatter")) {
		act (AT_LBLUE, "You freeze $N with a glance and shatter the "
			"frozen corpse into tiny shards.",  ch, NULL, victim, TO_CHAR);
		act (AT_LBLUE, "$n freezes you with a glance and shatters your "
			"frozen body into tiny shards.", ch, NULL, victim, TO_VICT);
		act (AT_LBLUE, "$n freezes $N with a glance and shatters the "
			"frozen body into tiny shards.",  ch, NULL, victim, TO_NOTVICT);
	}

	else if (!str_cmp (arg2, "demon")) {
		act (AT_IMMORT, "You gesture, and a slavering demon appears.  With "
			"a horrible grin, the",  ch, NULL, victim, TO_CHAR);
		act (AT_IMMORT, "foul creature turns on $N, who screams in panic "
			"before being eaten alive.",  ch, NULL, victim, TO_CHAR);
		act (AT_IMMORT, "$n gestures, and a slavering demon appears.  The "
			"foul creature turns on",  ch, NULL, victim, TO_VICT);
		act (AT_IMMORT, "you with a horrible grin.   You scream in panic "
			"before being eaten alive.",  ch, NULL, victim, TO_VICT);
		act (AT_IMMORT, "$n gestures, and a slavering demon appears.  With "
			"a horrible grin, the",  ch, NULL, victim, TO_NOTVICT);
		act (AT_IMMORT, "foul creature turns on $N, who screams in panic "
			"before being eaten alive.",  ch, NULL, victim, TO_NOTVICT);
	}

	else if (! str_cmp (arg2, "pounce")
	  && ch->GetTrustLevel () >= LEVEL_ASCENDANT) {
		act (AT_BLOOD, "Leaping upon $N with bared fangs, you tear open $S "
			"throat and toss the corpse to the ground...",  ch, NULL, victim, TO_CHAR);
		act (AT_BLOOD, "In a heartbeat, $n rips $s fangs through your "
			"throat!  Your blood sprays and pours to the ground as your "
			"life ends...", ch, NULL, victim, TO_VICT);
		act (AT_BLOOD, "Leaping suddenly, $n sinks $s fangs into $N's "
			"throat.  As blood sprays and gushes to the ground, $n tosses"
			" $N's dying body away.",  ch, NULL, victim, TO_NOTVICT);
	}

	else if (! str_cmp (arg2, "slit")
	  && ch->GetTrustLevel () >= LEVEL_ASCENDANT) {
		act (AT_BLOOD, "You calmly slit $N's throat.", ch, NULL, victim, TO_CHAR);
		act (AT_BLOOD, "$n reaches out with a clawed finger and calmly "
			"slits your throat.", ch, NULL, victim, TO_VICT);
		act (AT_BLOOD, "$n calmly slits $N's throat.", ch, NULL, victim, TO_NOTVICT);
	}

	else if (! str_cmp (arg2, "dog")) {
		act( AT_BLOOD, "You order your dogs to rip $N to shreds.", ch, NULL, victim, TO_CHAR );
		act( AT_BLOOD, "$n orders $s dogs to rip you apart.", ch, NULL, victim, TO_VICT );
		act( AT_BLOOD, "$n orders $s dogs to rip $N to shreds.", ch, NULL, victim, TO_NOTVICT );
	}

	else {
		act (AT_IMMORT, "You slay $N in cold blood!",  ch, NULL, victim, TO_CHAR   );
		act (AT_IMMORT, "$n slays you in cold blood!", ch, NULL, victim, TO_VICT   );
		act (AT_IMMORT, "$n slays $N in cold blood!",  ch, NULL, victim, TO_NOTVICT);
	}

	set_cur_char (victim);
	raw_kill (ch, victim);
}