Lyonesse/bin/
Lyonesse/doc/eng/
Lyonesse/doc/ita/
Lyonesse/lib/
Lyonesse/lib/buildings/
Lyonesse/lib/clans/
Lyonesse/lib/data/
Lyonesse/lib/etc/
Lyonesse/lib/house/
Lyonesse/lib/misc/
Lyonesse/lib/plralias/A-E/
Lyonesse/lib/plralias/F-J/
Lyonesse/lib/plralias/K-O/
Lyonesse/lib/plralias/P-T/
Lyonesse/lib/plralias/U-Z/
Lyonesse/lib/plralias/ZZZ/
Lyonesse/lib/plrobjs/A-E/
Lyonesse/lib/plrobjs/F-J/
Lyonesse/lib/plrobjs/K-O/
Lyonesse/lib/plrobjs/P-T/
Lyonesse/lib/plrobjs/U-Z/
Lyonesse/lib/plrobjs/ZZZ/
Lyonesse/lib/plrsave/A-E/
Lyonesse/lib/plrsave/F-J/
Lyonesse/lib/plrsave/K-O/
Lyonesse/lib/plrsave/P-T/
Lyonesse/lib/plrsave/U-Z/
Lyonesse/lib/plrsave/ZZZ/
Lyonesse/lib/ships/
Lyonesse/lib/stables/
Lyonesse/lib/text/help/
Lyonesse/lib/world/
Lyonesse/lib/world/bld/
Lyonesse/lib/world/ship/
Lyonesse/lib/world/shp/
Lyonesse/lib/world/wls/
Lyonesse/lib/world/wls/Life/
Lyonesse/lib/world/wls/Map/
Lyonesse/log/
/**************************************************************************
 * #   #   #   ##   #  #  ###   ##   ##  ###       http://www.lyonesse.it *
 * #    # #   #  #  ## #  #    #    #    #                                *
 * #     #    #  #  # ##  ##    #    #   ##   ## ##  #  #  ##             *
 * #     #    #  #  # ##  #      #    #  #    # # #  #  #  # #            *
 * ###   #     ##   #  #  ###  ##   ##   ###  #   #  ####  ##    Ver. 1.0 *
 *                                                                        *
 * -Based on CircleMud & Smaug-     Copyright (c) 2001-2002 by Mithrandir *
 *                                                                        *
 * ********************************************************************** */
/* ************************************************************************
*   File: fight.c                                       Part of CircleMUD *
*  Usage: Combat system                                                   *
*                                                                         *
*  All rights reserved.  See license.doc for complete information.        *
*                                                                         *
*  Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
*  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
************************************************************************ */

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

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

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

/* External structures */
extern MESSAGE_LIST			fight_messages[MAX_MESSAGES];
extern CLAN_POLITIC_DATA	politics_data;

extern int pk_allowed;		/* see config.c */
extern int max_exp_gain;	/* see config.c */
extern int max_exp_loss;	/* see config.c */
extern int max_npc_corpse_time, max_pc_corpse_time;

/* External procedures */
ACMD(do_flee);
ACMD(do_say);
OBJ_DATA *get_missile_cont( CHAR_DATA *ch, int type );
char	*fread_action(FILE *fl, int nr);
int		backstab_mult(int level);
int		thaco(int ch_class, int level);
int		ok_damage_shopkeeper(CHAR_DATA *ch, CHAR_DATA *victim);
int		wild_track(CHAR_DATA *ch, CHAR_DATA *vict, bool silent);
int		find_first_step(ROOM_DATA *src, ROOM_DATA *target);
int		kills_limit_xpgain(CHAR_DATA *ch, CHAR_DATA *victim, int exp);
void	start_hunting(CHAR_DATA *ch, CHAR_DATA *vict);
void	diag_char_to_char(CHAR_DATA * i, CHAR_DATA * ch);
void	stop_memorizing(CHAR_DATA *ch);
void	weapon_spells(CHAR_DATA *ch, CHAR_DATA *vict, OBJ_DATA *wpn);
void    brag(CHAR_DATA *ch, CHAR_DATA *victim);

/* local functions */
void perform_group_gain(CHAR_DATA *ch, int base, CHAR_DATA *victim);
void dam_message(int dam, CHAR_DATA *ch, CHAR_DATA *victim, int w_type);
void appear(CHAR_DATA *ch);
void load_messages(void);
void check_killer(CHAR_DATA *ch, CHAR_DATA *vict);
void make_corpse(CHAR_DATA *ch);
void change_alignment(CHAR_DATA *ch, CHAR_DATA *victim);
void death_cry(CHAR_DATA *ch);
void raw_kill(CHAR_DATA *ch);
void die(CHAR_DATA *ch, CHAR_DATA *killer);
void group_gain(CHAR_DATA *ch, CHAR_DATA *victim);
void solo_gain(CHAR_DATA *ch, CHAR_DATA *victim);
char *replace_string(const char *str, const char *weapon_singular, const char *weapon_plural);
void perform_violence(void);
int compute_armor_class(CHAR_DATA *ch);
int compute_thaco(CHAR_DATA *ch, CHAR_DATA *vict);
int get_weapon_prof(CHAR_DATA *ch, OBJ_DATA *wield);

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


/* The Fight related routines */

void appear(CHAR_DATA *ch)
{
	if (affected_by_spell(ch, SPELL_INVISIBLE))
		affect_from_char(ch, SPELL_INVISIBLE);
	
	REMOVE_BIT(AFF_FLAGS(ch), AFF_INVISIBLE | AFF_HIDE);
	
	if (GET_LEVEL(ch) < LVL_IMMORT)
		act("$n slowly fades into existence.", FALSE, ch, 0, 0, TO_ROOM);
	else
		act("You feel a strange presence as $n appears, seemingly from nowhere.",
			FALSE, ch, 0, 0, TO_ROOM);
}


int compute_armor_class(CHAR_DATA *ch)
{
	OBJ_DATA *wielded = GET_EQ(ch, WEAR_WIELD);
	int armorclass = GET_AC(ch);
	
	if (AWAKE(ch))
		armorclass += dex_app[GET_DEX(ch)].defensive * 10;
	
	if (IS_WARRIOR(ch))
		if (wielded && GET_OBJ_TYPE(wielded) == ITEM_WEAPON)
			armorclass += wpn_prof[get_weapon_prof(ch, wielded)].to_ac * 10;

	return (MAX(-100, armorclass));      /* -100 is lowest */
}


void load_messages(void)
{
	FILE *fl;
	MESSAGE_TYPE *messages;
	char chk[128];
	int i, type;
	
	if (!(fl = fopen(MESS_FILE, "r")))
	{
		log("SYSERR: Error reading combat message file %s: %s", MESS_FILE, strerror(errno));
		exit(1);
	}
	for (i = 0; i < MAX_MESSAGES; i++)
	{
		fight_messages[i].a_type			= 0;
		fight_messages[i].number_of_attacks	= 0;
		fight_messages[i].msg				= 0;
	}
	
	fgets(chk, 128, fl);
	while (!feof(fl) && (*chk == '\n' || *chk == '*'))
		fgets(chk, 128, fl);
	
	while (*chk == 'M')
	{
		fgets(chk, 128, fl);
		sscanf(chk, " %d\n", &type);
		for (i = 0; (i < MAX_MESSAGES) && (fight_messages[i].a_type != type) &&
			(fight_messages[i].a_type); i++);
		if (i >= MAX_MESSAGES)
		{
			log("SYSERR: Too many combat messages.  Increase MAX_MESSAGES and recompile.");
			exit(1);
		}
		CREATE(messages, MESSAGE_TYPE, 1);
		fight_messages[i].number_of_attacks++;
		fight_messages[i].a_type = type;
		messages->next = fight_messages[i].msg;
		fight_messages[i].msg = messages;
		
		messages->die_msg.attacker_msg	= fread_action(fl, i);
		messages->die_msg.victim_msg	= fread_action(fl, i);
		messages->die_msg.room_msg		= fread_action(fl, i);
		messages->miss_msg.attacker_msg = fread_action(fl, i);
		messages->miss_msg.victim_msg	= fread_action(fl, i);
		messages->miss_msg.room_msg		= fread_action(fl, i);
		messages->hit_msg.attacker_msg	= fread_action(fl, i);
		messages->hit_msg.victim_msg	= fread_action(fl, i);
		messages->hit_msg.room_msg		= fread_action(fl, i);
		messages->god_msg.attacker_msg	= fread_action(fl, i);
		messages->god_msg.victim_msg	= fread_action(fl, i);
		messages->god_msg.room_msg		= fread_action(fl, i);

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


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


/*
 * clan_at_war
 *
 * FALSE	if ch and vict are NOT members of clans at war
 * TRUE		if ch and vict are members of clans at war or ch or vict are NPCs
 */
bool clan_at_war(CHAR_DATA *ch, CHAR_DATA *vict)
{
	if ( IS_NPC(ch) || IS_NPC(vict) )
		return (TRUE);

	/* Check to see if ch & victim are in clans, and enemies */
	if (GET_CLAN(ch) != -1 && GET_CLAN(vict) != -1 && GET_CLAN(ch) != GET_CLAN(vict))
	{
		if (politics_data.diplomacy[GET_CLAN(ch)][GET_CLAN(vict)] < -450)
			return (TRUE);    
	}

	return (FALSE);
}

/* check to prevent pkilling */
bool pkill_ok(CHAR_DATA *ch, CHAR_DATA *opponent)
{
	if ( ch == opponent )
		return (TRUE);

	if ( !pk_allowed && !IS_NPC(ch) && !IS_NPC(opponent) &&
	     !clan_at_war(ch, opponent) )
		return (FALSE);
	
	return (TRUE);
}

void check_killer(CHAR_DATA *ch, CHAR_DATA *vict)
{
	char buf[256];
	
	if (PLR_FLAGGED(vict, PLR_KILLER) || PLR_FLAGGED(vict, PLR_THIEF))
		return;
	if (PLR_FLAGGED(ch, PLR_KILLER) || IS_NPC(ch) || IS_NPC(vict) || ch == vict)
		return;
	
	SET_BIT(PLR_FLAGS(ch), PLR_KILLER);
	sprintf(buf, "PC Killer bit set on %s for initiating attack on %s at %s.",
		GET_NAME(ch), GET_NAME(vict), ROOM_NAME(vict));
	mudlog(buf, BRF, LVL_IMMORT, TRUE);
	send_to_char("If you want to be a PLAYER KILLER, so be it...\r\n", ch);
}


/* start one char fighting another (yes, it is horrible, I know... )  */
void set_fighting(CHAR_DATA *ch, CHAR_DATA *vict)
{
	if (ch == vict)
		return;
	
	if (FIGHTING(ch))
	{
		core_dump();
		return;
	}
	
	ch->next_fighting	= combat_list;
	combat_list			= ch;
	
	if (AFF_FLAGGED(ch, AFF_SLEEP))
		affect_from_char(ch, SPELL_SLEEP);
	
	FIGHTING(ch)		= vict;
	GET_POS(ch)			= POS_FIGHTING;
	
	/* handle ppl using furniture */
	if ( ch->in_obj )	char_from_obj(ch);
	if ( vict->in_obj )	char_from_obj(vict);

	/* check for memorizing chars.. */
	if ( !IS_NPC(vict) && AFF_FLAGGED(vict, AFF_MEMORIZING) )
		stop_memorizing(vict);

	if (!pkill_ok(ch, vict))
		check_killer(ch, vict);
}



/* remove a char from the list of fighting chars */
void stop_fighting(CHAR_DATA *ch)
{
	CHAR_DATA *temp;
	
	if (ch == next_combat_list)
		next_combat_list = ch->next_fighting;
	
	REMOVE_FROM_LIST(ch, combat_list, next_fighting);
	ch->next_fighting	= NULL;
	FIGHTING(ch)		= NULL;

	GET_POS(ch)			= POS_STANDING;

	update_pos(ch);
}



void make_corpse(CHAR_DATA *ch)
{
	OBJ_DATA *corpse, *o;
	char bufcor[MAX_STRING_LENGTH];
	int i;
	
	corpse = create_obj();
	
	corpse->item_number			= NOTHING;
	corpse->in_room				= NULL;

	sprintf( bufcor, "corpse %s", IS_NPC(ch) ? GET_PC_NAME(ch) : unknown_name(ch) );
	corpse->name				= str_dup(bufcor);
	
	sprintf(buf2, "The corpse of %s is lying here.", unknown_name(ch));
	corpse->description			= str_dup(buf2);
	
	sprintf(buf2, "the corpse of %s", unknown_name(ch));
	corpse->short_description	= str_dup(buf2);
	
	GET_OBJ_TYPE(corpse)		= ITEM_CONTAINER;
	GET_OBJ_WEAR(corpse)		= ITEM_WEAR_TAKE;
	GET_OBJ_EXTRA(corpse)		= ITEM_NODONATE;
	GET_OBJ_VAL(corpse, 0)		= 0;	/* You can't store stuff in a corpse */
	GET_OBJ_VAL(corpse, 3)		= 1;	/* corpse identifier */
	GET_OBJ_WEIGHT(corpse)		= GET_WEIGHT(ch) + IS_CARRYING_W(ch);
	GET_OBJ_RENT(corpse)		= 100000;

	if (IS_NPC(ch))
	{
		GET_OBJ_TIMER(corpse)	= max_npc_corpse_time;
		GET_OBJ_VAL(corpse, 1)	= 0;
	}
	else
	{
		GET_OBJ_TIMER(corpse)	= max_pc_corpse_time;
		GET_OBJ_VAL(corpse, 1)	= GET_IDNUM(ch);
	}
	
	/* transfer character's inventory to the corpse */
	corpse->first_content		= ch->first_carrying;
	corpse->last_content		= ch->last_carrying;

	for (o = corpse->first_content; o; o = o->next_content)
		o->in_obj = corpse;
	object_list_new_owner(corpse, NULL);
	
	/* transfer character's equipment to the corpse */
	for (i = 0; i < NUM_WEARS; i++)
	{
		if (GET_EQ(ch, i))
			obj_to_obj(unequip_char(ch, i), corpse);
	}

	/* make gold coins from gold value (Mob only) */
	if (IS_NPC(ch) && GET_GOLD(ch) > 0)
		create_amount(GET_GOLD(ch), NULL, NULL, corpse, NULL, FALSE);

	ch->first_carrying	= NULL;
	ch->last_carrying	= NULL;
	
	IS_CARRYING_N(ch)	= 0;
	IS_CARRYING_W(ch)	= 0;
	
	obj_to_room(corpse, ch->in_room);
}


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



void death_cry(CHAR_DATA *ch)
{
	EXIT_DATA *pexit;
	
	act("Your blood freezes as you hear $n's death cry.", FALSE, ch, 0, 0, TO_ROOM);
	
	for ( pexit = ch->in_room->first_exit; pexit; pexit = pexit->next )
	{
		if ( valid_exit( pexit ) )
			send_to_room("Your blood freezes as you hear someone's death cry.\r\n",
				     pexit->to_room);
	}
}


void raw_kill(CHAR_DATA *ch)
{
	if (FIGHTING(ch))
		stop_fighting(ch);
	
	while (ch->affected)
		affect_remove(ch, ch->affected);
	
	death_cry(ch);
	
	if (IS_NPC(ch))
	{
		make_corpse(ch);
		extract_char(ch);
	}
	else
	{
		char_to_ghost(ch);
		send_to_char("You feel yourself being pulled into a bright light...\r\n", ch);
		char_from_room(ch);
		char_to_room(ch, get_room(10001));
		GET_HIT(ch)		= GET_MAX_HIT(ch);
		GET_MANA(ch)	= GET_MAX_MANA(ch);
		GET_MOVE(ch)	= GET_MAX_MOVE(ch);
		update_pos(ch);
		look_at_room(ch, 0);
		SET_BIT(PLR_FLAGS(ch), PLR_GHOST);
	}
}



void die(CHAR_DATA *ch, CHAR_DATA *killer)
{
	if (killer && ch != killer)
	{
		/* aggiorniamo i contatori delle uccisioni */
		if (!IS_NPC(ch))
		{
			if (IS_NPC(killer))
				GET_MOB_DEATHS(ch)++;
			else
			{
				GET_PLR_DEATHS(ch)++;
				GET_PLR_KILLS(killer)++;
			}
		}
		else
			if (!IS_NPC(killer))
				GET_MOB_KILLS(killer)++;
	}

	gain_exp(ch, -(GET_EXP(ch) / 5));
	if (!IS_NPC(ch))
		REMOVE_BIT(PLR_FLAGS(ch), PLR_KILLER | PLR_THIEF);
	raw_kill(ch);
}



void perform_group_gain(CHAR_DATA *ch, int base, CHAR_DATA *victim)
{
	int share, exp_after_lim;
	
	if (!IS_NPC(ch) && IS_NPC(victim))
		exp_after_lim = kills_limit_xpgain(ch, victim, base);

	share = MIN(max_exp_gain, MAX(1, exp_after_lim));

	if (share > 1)
	{
		sprintf(buf2, "You receive your share of experience -- %d points.\r\n", share);
		send_to_char(buf2, ch);
	}
	else
		send_to_char("You receive your share of experience -- one measly little point!\r\n", ch);
	
	gain_exp(ch, share);
	change_alignment(ch, victim);
}


void group_gain(CHAR_DATA *ch, CHAR_DATA *victim)
{
	CHAR_DATA *k;
	FOLLOW_DATA *f;
	int tot_members, base, tot_gain;
	
	if (!(k = ch->master))
		k = ch;
	
	if (AFF_FLAGGED(k, AFF_GROUP) && (k->in_room == ch->in_room))
		tot_members = 1;
	else
		tot_members = 0;
	
	for (f = k->followers; f; f = f->next)
	{
		if (AFF_FLAGGED(f->follower, AFF_GROUP) && f->follower->in_room == ch->in_room)
			tot_members++;
	}
	
	/* round up to the next highest tot_members */
	tot_gain = (GET_EXP(victim) / 3) + tot_members - 1;
	
	/* prevent illegal xp creation when killing players */
	if (!IS_NPC(victim))
		tot_gain = MIN(max_exp_loss * 2 / 3, tot_gain);
	
	if (tot_members >= 1)
		base = MAX(1, tot_gain / tot_members);
	else
		base = 0;
	
	if (AFF_FLAGGED(k, AFF_GROUP) && k->in_room == ch->in_room)
		perform_group_gain(k, base, victim);
	
	for (f = k->followers; f; f = f->next)
	{
		if (AFF_FLAGGED(f->follower, AFF_GROUP) && f->follower->in_room == ch->in_room)
			perform_group_gain(f->follower, base, victim);
	}
}


void solo_gain(CHAR_DATA *ch, CHAR_DATA *victim)
{
	int exp;
	
	//exp = MIN(max_exp_gain, GET_EXP(victim) / 3);

	exp = GET_EXP(victim) / 3;
	
	/* Limit up XP gain according on amount of kills of current mob */
	if (!IS_NPC(ch) && IS_NPC(victim))
		exp = kills_limit_xpgain(ch, victim, exp);
	
	exp = MIN(max_exp_gain, exp);
	
	/* Calculate level-difference bonus */
	if (IS_NPC(ch))
		exp += MAX(0, (exp * MIN(4, (GET_LEVEL(victim) - GET_LEVEL(ch)))) / 8);
	else
		exp += MAX(0, (exp * MIN(8, (GET_LEVEL(victim) - GET_LEVEL(ch)))) / 8);
	
	exp = MAX(exp, 1);
	
	if (exp > 1)
	{
		sprintf(buf2, "You receive %d experience points.\r\n", exp);
		send_to_char(buf2, ch);
	}
	else
		send_to_char("You receive one lousy experience point.\r\n", ch);
	
	gain_exp(ch, exp);
	change_alignment(ch, victim);
}


char *replace_string(const char *str, const char *weapon_singular, const char *weapon_plural)
{
	static char buf[256];
	char *cp = buf;
	
	for (; *str; str++)
	{
		if (*str == '#')
		{
			switch (*(++str))
			{
			case 'W':
				for (; *weapon_plural; *(cp++) = *(weapon_plural++));
				break;
			case 'w':
				for (; *weapon_singular; *(cp++) = *(weapon_singular++));
				break;
			default:
				*(cp++) = '#';
				break;
			}
		}
		else
			*(cp++) = *str;
		
		*cp = 0;
	}				/* For */
	
	return (buf);
}


/* message for doing damage with a weapon */
void dam_message(int dam, CHAR_DATA *ch, CHAR_DATA *victim, int w_type)
{
	char *buf;
	int msgnum;
	
	static struct dam_weapon_type
	{
	  const char	*to_room;
	  const char	*to_char;
	  const char	*to_victim;
	} dam_weapons[] =
	{
		/* use #w for singular (i.e. "slash") and #W for plural (i.e. "slashes") */
		{
			"$n tries to #w $N, but misses.",	/* 0: 0     */
			"You try to #w $N, but miss.",
			"$n tries to #w you, but misses."
		},
		{
			"$n tickles $N as $e #W $M.",	/* 1: 1..2  */
			"You tickle $N as you #w $M.",
			"$n tickles you as $e #W you."
		},
		{
			"$n barely #W $N.",		/* 2: 3..4  */
			"You barely #w $N.",
			"$n barely #W you."
		},
		{
			"$n #W $N.",			/* 3: 5..6  */
			"You #w $N.",
			"$n #W you."
		},
		{
			"$n #W $N hard.",			/* 4: 7..10  */
			"You #w $N hard.",
			"$n #W you hard."
		},
		{
			"$n #W $N very hard.",		/* 5: 11..14  */
			"You #w $N very hard.",
			"$n #W you very hard."
		},
		{
			"$n #W $N extremely hard.",	/* 6: 15..19  */
			"You #w $N extremely hard.",
			"$n #W you extremely hard."
		},
		{
			"$n massacres $N to small fragments with $s #w.",	/* 7: 19..23 */
			"You massacre $N to small fragments with your #w.",
			"$n massacres you to small fragments with $s #w."
		},
		{
			"$n OBLITERATES $N with $s deadly #w!!",	/* 8: > 23   */
			"You OBLITERATE $N with your deadly #w!!",
			"$n OBLITERATES you with $s deadly #w!!"
		}
	};
	

	w_type -= TYPE_HIT;		/* Change to base of table with text */
	
	if		(dam == 0)	msgnum = 0;
	else if (dam <= 2)	msgnum = 1;
	else if (dam <= 4)	msgnum = 2;
	else if (dam <= 6)	msgnum = 3;
	else if (dam <= 10)	msgnum = 4;
	else if (dam <= 14)	msgnum = 5;
	else if (dam <= 19)	msgnum = 6;
	else if (dam <= 23)	msgnum = 7;
	else				msgnum = 8;
	
	/* damage message to onlookers */
	buf = replace_string(dam_weapons[msgnum].to_room,
		attack_hit_text[w_type].singular, attack_hit_text[w_type].plural);
	act(buf, FALSE, ch, NULL, victim, TO_NOTVICT);
	
	/* damage message to damager */
	send_to_char(CCYEL(ch, C_CMP), ch);
	buf = replace_string(dam_weapons[msgnum].to_char,
		attack_hit_text[w_type].singular, attack_hit_text[w_type].plural);
	act(buf, FALSE, ch, NULL, victim, TO_CHAR);
	send_to_char(CCNRM(ch, C_CMP), ch);
	
	/* damage message to damagee */
	send_to_char(CCRED(victim, C_CMP), victim);
	buf = replace_string(dam_weapons[msgnum].to_victim,
		attack_hit_text[w_type].singular, attack_hit_text[w_type].plural);
	act(buf, FALSE, ch, NULL, victim, TO_VICT | TO_SLEEP);
	send_to_char(CCNRM(victim, C_CMP), victim);
}


/*
 *  message for doing damage with a spell or skill
 *  C3.0: Also used for weapon damage on miss and death blows
 */
int skill_message(int dam, CHAR_DATA *ch, CHAR_DATA *vict, int attacktype)
{
	MESSAGE_TYPE *msg;
	OBJ_DATA *weap = GET_EQ(ch, WEAR_WIELD);
	int i, j, nr;
	
	for (i = 0; i < MAX_MESSAGES; i++)
	{
		if (fight_messages[i].a_type == attacktype)
		{
			nr = dice(1, fight_messages[i].number_of_attacks);
			for (j = 1, msg = fight_messages[i].msg; (j < nr) && msg; j++)
				msg = msg->next;
			
			if (!IS_NPC(vict) && (GET_LEVEL(vict) >= LVL_IMMORT))
			{
				act(msg->god_msg.attacker_msg, FALSE, ch, weap, vict, TO_CHAR);
				act(msg->god_msg.victim_msg, FALSE, ch, weap, vict, TO_VICT);
				act(msg->god_msg.room_msg, FALSE, ch, weap, vict, TO_NOTVICT);
			}
			else if (dam != 0)
			{
				/*
				 * Don't	send redundant color codes for TYPE_SUFFERING & other types
				 * of damage without attacker_msg.
				 */
				if (GET_POS(vict) == POS_DEAD)
				{
					if (msg->die_msg.attacker_msg)
					{
						send_to_char(CCYEL(ch, C_CMP), ch);
						act(msg->die_msg.attacker_msg, FALSE, ch, weap, vict, TO_CHAR);
						send_to_char(CCNRM(ch, C_CMP), ch);
					}
					
					send_to_char(CCRED(vict, C_CMP), vict);
					act(msg->die_msg.victim_msg, FALSE, ch, weap, vict, TO_VICT | TO_SLEEP);
					send_to_char(CCNRM(vict, C_CMP), vict);
					
					act(msg->die_msg.room_msg, FALSE, ch, weap, vict, TO_NOTVICT);
				}
				else
				{
					if (msg->hit_msg.attacker_msg)
					{
						send_to_char(CCYEL(ch, C_CMP), ch);
						act(msg->hit_msg.attacker_msg, FALSE, ch, weap, vict, TO_CHAR);
						send_to_char(CCNRM(ch, C_CMP), ch);
					}
					
					send_to_char(CCRED(vict, C_CMP), vict);
					act(msg->hit_msg.victim_msg, FALSE, ch, weap, vict, TO_VICT | TO_SLEEP);
					send_to_char(CCNRM(vict, C_CMP), vict);
					
					act(msg->hit_msg.room_msg, FALSE, ch, weap, vict, TO_NOTVICT);
				}
			}
			else if (ch != vict)	/* Dam == 0 */
			{
				if (msg->miss_msg.attacker_msg)
				{
					send_to_char(CCYEL(ch, C_CMP), ch);
					act(msg->miss_msg.attacker_msg, FALSE, ch, weap, vict, TO_CHAR);
					send_to_char(CCNRM(ch, C_CMP), ch);
				}
				
				send_to_char(CCRED(vict, C_CMP), vict);
				act(msg->miss_msg.victim_msg, FALSE, ch, weap, vict, TO_VICT | TO_SLEEP);
				send_to_char(CCNRM(vict, C_CMP), vict);
				
				act(msg->miss_msg.room_msg, FALSE, ch, weap, vict, TO_NOTVICT);
			}
			return (1);
		}
	}
	return (0);
}

/*
 * Alert: As of bpl14, this function returns the following codes:
 *	< 0	Victim died.
 *	= 0	No damage.
 *	> 0	How much damage done.
 */
int damage(CHAR_DATA *ch, CHAR_DATA *victim, int dam, int attacktype)
{
	ROOM_DATA *vict_room = NULL;
	bool missile = FALSE;

	if (GET_POS(victim) <= POS_DEAD)
	{
		/* This is "normal"-ish now with delayed extraction. -gg 3/15/2001 */
		if (PLR_FLAGGED(victim, PLR_NOTDEADYET) || MOB_FLAGGED(victim, MOB_NOTDEADYET))
			return (-1);
		
		log("SYSERR: Attempt to damage corpse '%s' in room #%d by '%s'.",
			GET_NAME(victim), victim->in_room->number, GET_NAME(ch));
		die(victim, NULL);
		return (-1);			/* -je, 7/7/92 */
	}
	
	if (ch->in_room != victim->in_room)
	{
		missile = TRUE;
		vict_room = victim->in_room;
	}

	/* peaceful rooms */
	if (ch != victim && ROOM_FLAGGED(ch->in_room, ROOM_PEACEFUL))
	{
		send_to_char("This room just has such a peaceful, easy feeling...\r\n", ch);
		return (0);
	}
	
	/* shopkeeper protection */
	if (!ok_damage_shopkeeper(ch, victim))
		return (0);
	
	/* You can't damage an immortal! */
	if (IS_IMMORTAL(victim))
		dam = 0;
	
	if (victim != ch && !missile)
	{
		/* Start the attacker fighting the victim */
		if (GET_POS(ch) > POS_STUNNED && (FIGHTING(ch) == NULL))
			set_fighting(ch, victim);
		
		/* Start the victim fighting the attacker */
		if (GET_POS(victim) > POS_STUNNED && (FIGHTING(victim) == NULL))
		{
			set_fighting(victim, ch);
			if (MOB_FLAGGED(victim, MOB_MEMORY) && !IS_NPC(ch))
				remember(victim, ch);
		}
	}
	
	/* If you attack a pet, it hates your guts */
	if (victim->master == ch)
		stop_follower(victim);
	
	/* If the attacker is invisible, he becomes visible */
	if (AFF_FLAGGED(ch, AFF_INVISIBLE | AFF_HIDE))
		appear(ch);
	
	/* Cut damage in half if victim has sanct, to a minimum 1 */
	if (AFF_FLAGGED(victim, AFF_SANCTUARY) && dam >= 2)
		dam /= 2;
	
	/* Check for PK if this is not a PK MUD */
	if ( !pkill_ok(ch, victim) )
	{
		check_killer(ch, victim);
		if (PLR_FLAGGED(ch, PLR_KILLER) && (ch != victim))
			dam = 0;
	}
	
	/* Set the maximum damage per round and subtract the hit points */
	dam = MAX(MIN(dam, 200), 0);
	GET_HIT(victim) -= dam;
	
	/* Being hit destroys equipment... */
	if (dam > 0)
		check_damage_obj(victim, NULL, 7);

	update_pos(victim);
	
	/*
	 * skill_message sends a message from the messages file in lib/misc.
	 * dam_message just sends a generic "You hit $n extremely hard.".
	 * skill_message is preferable to dam_message because it is more
	 * descriptive.
	 * 
	 * If we are _not_ attacking with a weapon (i.e. a spell), always use
	 * skill_message. If we are attacking with a weapon: If this is a miss or a
	 * death blow, send a skill_message if one exists; if not, default to a
	 * dam_message. Otherwise, always send a dam_message.
	 */
	if (attacktype != -1)
	{
	if (!IS_WEAPON(attacktype) || missile)
		skill_message(dam, ch, victim, attacktype);
	else
	{
		if (GET_POS(victim) == POS_DEAD || dam == 0)
		{
			if (!skill_message(dam, ch, victim, attacktype))
				dam_message(dam, ch, victim, attacktype);
		}
		else
			dam_message(dam, ch, victim, attacktype);
	}
	}
	
	/* Use send_to_char -- act() doesn't send message if you are DEAD. */
	switch (GET_POS(victim))
	{
	case POS_MORTALLYW:
		act("$n is mortally wounded, and will die soon, if not aided.", TRUE, victim, 0, 0, TO_ROOM);
		send_to_char("You are mortally wounded, and will die soon, if not aided.\r\n", victim);
		break;
	case POS_INCAP:
		act("$n is incapacitated and will slowly die, if not aided.", TRUE, victim, 0, 0, TO_ROOM);
		send_to_char("You are incapacitated an will slowly die, if not aided.\r\n", victim);
		break;
	case POS_STUNNED:
		act("$n is stunned, but will probably regain consciousness again.", TRUE, victim, 0, 0, TO_ROOM);
		send_to_char("You're stunned, but will probably regain consciousness again.\r\n", victim);
		break;
	case POS_DEAD:
		act("$n is dead!  R.I.P.", FALSE, victim, 0, 0, TO_ROOM);
		send_to_char("You are dead!  Sorry...\r\n", victim);
		break;
		
	default:			/* >= POSITION SLEEPING */
		if (dam > (GET_MAX_HIT(victim) / 4))
			send_to_char("That really did HURT!\r\n", victim);
		
		if (GET_HIT(victim) < (GET_MAX_HIT(victim) / 4))
		{
			sprintf(buf2, "%sYou wish that your wounds would stop BLEEDING so much!%s\r\n",
				CCRED(victim, C_SPR), CCNRM(victim, C_SPR));
			send_to_char(buf2, victim);
			if (ch != victim && MOB_FLAGGED(victim, MOB_WIMPY))
				do_flee(victim, NULL, 0, 0);
		}
		if (!IS_NPC(victim) && GET_WIMP_LEV(victim) && (victim != ch) &&
			GET_HIT(victim) < GET_WIMP_LEV(victim) && GET_HIT(victim) > 0)
		{
			send_to_char("You wimp out, and attempt to flee!\r\n", victim);
			do_flee(victim, NULL, 0, 0);
		}
		break;
	}
	
	/* Help out poor linkless people who are attacked */
	if (victim != ch && !IS_NPC(victim) && !(victim->desc) && GET_POS(victim) > POS_STUNNED)
	{
		do_flee(victim, NULL, 0, 0);
		if (!FIGHTING(victim))
		{
			act("$n is rescued by divine forces.", FALSE, victim, 0, 0, TO_ROOM);
			GET_WAS_IN(victim) = victim->in_room;
			char_from_room(victim);
			char_to_room(victim, get_room(0));
		}
	}
	
	/* stop someone from fighting if they're stunned or worse */
	if (GET_POS(victim) <= POS_STUNNED && FIGHTING(victim) != NULL)
		stop_fighting(victim);
	
	/* Uh oh.  Victim died. */
	if (GET_POS(victim) == POS_DEAD)
	{
		if (ch != victim && (IS_NPC(victim) || victim->desc))
		{
			if (AFF_FLAGGED(ch, AFF_GROUP))
				group_gain(ch, victim);
			else
				solo_gain(ch, victim);
		}
		
		if (ch != victim && !IS_NPC(victim))
		{
			sprintf(buf2, "%s killed by %s at %s", GET_NAME(victim), GET_NAME(ch), ROOM_NAME(victim));
			mudlog(buf2, BRF, LVL_IMMORT, TRUE);
			if (IS_NPC(ch))
				brag(ch, victim);
			if (MOB_FLAGGED(ch, MOB_MEMORY))
				forget(ch, victim);
		}
		die(victim, ch);
		return (-1);
	}
	/* mob reaction to a ranged attack */
	else if (victim != ch && missile)
	{	
		if (IS_NPC(victim) && !FIGHTING(victim) && GET_POS(victim) > POS_STUNNED)
		{
			/* can remember so charge! */
			if (MOB_FLAGGED(victim, MOB_MEMORY))
			{
				remember(victim, ch);
				sprintf(buf, "$n bellows in pain!");
				act(buf, FALSE, victim, 0, 0, TO_ROOM);

				if (GET_POS(victim) == POS_STANDING)
				{
					int dir, moved = 0;

					if (IN_WILD(victim))
						dir = wild_track(ch, victim, TRUE);
					else
						dir = find_first_step(victim->in_room, ch->in_room);
					
					if (dir != NOWHERE)
					{
						if (!do_simple_move(victim, dir, 1))
							act("$n stumbles while trying to run!", FALSE, victim, 0, 0, TO_ROOM);
						else
							moved = 1;
					}

					if (!moved && MOB_FLAGGED(victim, MOB_HUNTER))
						start_hunting(victim, ch);
				}
			}
			/* can't remember so try to run away if not already fleed */
			else
			{
				if (vict_room == victim->in_room)
					do_flee(victim, NULL, 0, 0);
			}
		}
	}

	return (dam);
}


/*
 * Calculate the THAC0 of the attacker.
 *
 * 'victim' currently isn't used but you could use it for special cases like
 * weapons that hit evil creatures easier or a weapon that always misses
 * attacking an animal.
 */
int compute_thaco(CHAR_DATA *ch, CHAR_DATA *victim)
{
	int calc_thaco;
	
	if (!IS_NPC(ch))
		calc_thaco = thaco(GET_CLASS(ch), GET_LEVEL(ch));
	else		/* THAC0 for monsters is set in the HitRoll */
		calc_thaco = 20;

	calc_thaco -= str_app[GET_STR(ch)].tohit;
	calc_thaco -= GET_HITROLL(ch);
	calc_thaco -= (GET_INT(ch) - 13) / 1.5;	/* Intelligence helps! */
	calc_thaco -= (GET_WIS(ch) - 13) / 1.5;	/* So does wisdom */
	
	return (calc_thaco);
}


void hit(CHAR_DATA *ch, CHAR_DATA *victim, int type)
{
	OBJ_DATA *wielded = GET_EQ(ch, WEAR_WIELD);
	int w_type, victim_ac, calc_thaco, dam, diceroll, ret;
	int wpnprof;
	
	/* Do some sanity checking, just in case.. */
	if ( !ch || !victim )
		return;

	/* Do some sanity checking, in case someone flees, etc. */
	if (ch->in_room != victim->in_room)
	{
		if (FIGHTING(ch) && FIGHTING(ch) == victim)
			stop_fighting(ch);
		return;
	}
	
	/* Find the weapon type (for display purposes only) */
	if (wielded && GET_OBJ_TYPE(wielded) == ITEM_WEAPON)
		w_type = GET_OBJ_VAL(wielded, 3) + TYPE_HIT;
	else
	{
		if (IS_NPC(ch) && GET_ATTACK(ch) != 0)
			w_type = GET_ATTACK(ch) + TYPE_HIT;
		else
			w_type = TYPE_HIT;
	}
	
	/* Weapon proficiencies */
	if (IS_WARRIOR(ch) && wielded && GET_OBJ_TYPE(wielded) == ITEM_WEAPON)
		wpnprof = get_weapon_prof(ch, wielded);
	else
		wpnprof = 0;

	/* Calculate chance of hit. Lower THAC0 is better for attacker. */
	calc_thaco = compute_thaco(ch, victim);
	calc_thaco -= wpn_prof[wpnprof].to_hit;
	
	/* Calculate the raw armor including magic armor.  Lower AC is better for defender. */
	victim_ac = compute_armor_class(victim) / 10;

	if ( RIDING(victim) )
		victim_ac -= 5;
	
	/* roll the die and take your chances... */
	diceroll = number(1, 20);

	/*
	 * Decide whether this is a hit or a miss.
	 *
	 *  Victim asleep = hit, otherwise:
	 *     1   = Automatic miss.
	 *   2..19 = Checked vs. AC.
	 *    20   = Automatic hit.
	 */
	if (diceroll == 20 || !AWAKE(victim))
		dam = TRUE;
	else if (diceroll == 1)
		dam = FALSE;
	else
		dam = (calc_thaco - diceroll <= victim_ac);
	
	if (!dam)
	{
		damage(ch, victim, 0, type == SKILL_BACKSTAB ? SKILL_BACKSTAB : w_type);
		return;
	}

	/* okay, we know the guy has been hit.  now calculate damage. */
	
	/* Start with the damage bonuses: the damroll and strength apply */
	dam = str_app[GET_STR(ch)].todam;
	dam += GET_DAMROLL(ch);
	
	/* Maybe holding arrow? */
	if (wielded && GET_OBJ_TYPE(wielded) == ITEM_WEAPON)
	{
		/* Add weapon-based damage if a weapon is being wielded */
		dam += dice(GET_OBJ_VAL(wielded, 1), GET_OBJ_VAL(wielded, 2));
		dam += wpn_prof[wpnprof].to_dam;
	}
	else
	{
		/* If no weapon, add bare hand damage instead */
		if (IS_NPC(ch))
			dam += dice(GET_NDD(ch), GET_SDD(ch));
		else
			dam += number(0, 2);	/* Max 2 bare hand damage for players */
	}
	
	if ( RIDING(ch) )
		dam += MAX(10, GET_DAMROLL(ch)/10);
	
	/*
	 * Include a damage multiplier if victim isn't ready to fight:
	 *
	 * Position sitting  1.33 x normal
	 * Position resting  1.66 x normal
	 * Position sleeping 2.00 x normal
	 * Position stunned  2.33 x normal
	 * Position incap    2.66 x normal
	 * Position mortally 3.00 x normal
	 *
	 * Note, this is a hack because it depends on the particular
	 * values of the POSITION_XXX constants.
	 */
	if (GET_POS(victim) < POS_FIGHTING)
		dam *= 1 + (POS_FIGHTING - GET_POS(victim)) / 3;
	
	/* at least 1 hp damage min per hit */
	dam = MAX(1, dam);
	
	if (type == SKILL_BACKSTAB)
		ret = damage(ch, victim, dam * backstab_mult(GET_LEVEL(ch)), SKILL_BACKSTAB);
	else
		ret = damage(ch, victim, dam, w_type);
	
	/* fireshield case */
	if ( ret != -1 && type >= TYPE_HIT )
	{
		if (AFF_FLAGGED(victim, AFF_FIRESHIELD) && !AFF_FLAGGED(ch, AFF_FIRESHIELD))
			damage(victim, ch, dam / 2, SPELL_FIREBALL);
	}
	
	/* check for weapon being damaged */
	if (wielded && GET_OBJ_TYPE(wielded) == ITEM_WEAPON)
	{
		/* call for a magical effect from the weapon */
		if (FIGHTING(ch) && victim && FIGHTING(ch) == victim)
			weapon_spells(ch, victim, wielded);
		
		/* check for weapon being damaged */
		check_damage_obj(ch, wielded, 3);
	}
}


bool valid_target(CHAR_DATA *ch, CHAR_DATA *vict)
{
	if ( !ch || !vict )
		return (FALSE);

	if ( MOB_FLAGGED(ch, MOB_NOTDEADYET) )
		return (FALSE);
	if ( MOB_FLAGGED(vict, MOB_NOTDEADYET) )
		return (FALSE);

	if ( PLR_FLAGGED(ch, PLR_NOTDEADYET) )
		return (FALSE);
	if ( PLR_FLAGGED(vict, PLR_NOTDEADYET) )
		return (FALSE);

	return (TRUE);
}



/* control the fights going on.  Called every 2 seconds from comm.c. */
void perform_violence(void)
{
	CHAR_DATA *ch;
	OBJ_DATA *wielded;
	sh_int num_of_attacks = 1, loop_attacks;
	
	for (ch = combat_list; ch; ch = next_combat_list)
	{
		next_combat_list = ch->next_fighting;
		
		if (FIGHTING(ch) == NULL || ch->in_room != FIGHTING(ch)->in_room || !valid_target(ch, FIGHTING(ch)))
		{
			stop_fighting(ch);
			continue;
		}

		if (IS_NPC(ch))
		{
			if (GET_MOB_WAIT(ch) > 0)
			{
				GET_MOB_WAIT(ch) -= PULSE_VIOLENCE;
				continue;
			}
			GET_MOB_WAIT(ch) = 0;
			if (GET_POS(ch) < POS_FIGHTING)
			{
				GET_POS(ch) = POS_FIGHTING;
				act("$n scrambles to $s feet!", TRUE, ch, 0, 0, TO_ROOM);
			}
		}
		
		if (GET_POS(ch) < POS_FIGHTING)
		{
			send_to_char("You can't fight while sitting!!\r\n", ch);
			continue;
		}
		
		/* reset to 1 attack */
		num_of_attacks = 1;
		
		if (IS_WARRIOR(ch))
		{
			wielded = GET_EQ(ch, WEAR_WIELD);
			if (wielded && GET_OBJ_TYPE(wielded) == ITEM_WEAPON)
				num_of_attacks += wpn_prof[get_weapon_prof(ch, wielded)].num_of_attacks;
		}
		else
			num_of_attacks += MAX(1, ((int) GET_LEVEL(ch) / 10) - 1);
		
		for (loop_attacks = 0; loop_attacks < num_of_attacks && valid_target(ch, FIGHTING(ch)); loop_attacks++)
			hit(ch, FIGHTING(ch), TYPE_UNDEFINED);
		
		if (valid_target(ch, FIGHTING(ch)))
			diag_char_to_char(FIGHTING(ch), ch);

		if (MOB_FLAGGED(ch, MOB_SPEC) && GET_MOB_SPEC(ch) && !MOB_FLAGGED(ch, MOB_NOTDEADYET))
		{
			char actbuf[MAX_INPUT_LENGTH] = "";

			(GET_MOB_SPEC(ch)) (ch, ch, 0, actbuf);
		}
	}
}

/* Weapon Proficiecies */
int get_weapon_prof(CHAR_DATA *ch, OBJ_DATA *wield)
{
	int bonus = 0, learned = 0, type = -1;
	
	/* No mobs and non-warriors here */
	if (!IS_WARRIOR(ch))
		return (bonus);
	
	/* Check for proficiencies only if characters could have learned it */
	if (GET_LEVEL(ch) < PROF_LVL_START)
		return (bonus);
	
	switch (GET_OBJ_VAL(wield, 3) + TYPE_HIT)
	{
	case TYPE_SLASH:
		type = SKILL_WEAPON_SWORDS;
		break;
		
	case TYPE_STING:
	case TYPE_PIERCE:
	case TYPE_STAB:
		type = SKILL_WEAPON_DAGGERS;
		break;
		
	case TYPE_THRASH:
	case TYPE_WHIP:
		type = SKILL_WEAPON_WHIPS;
		break;
		
	case TYPE_CLAW:
		type = SKILL_WEAPON_TALONOUS_ARMS;
		break;
		
	case TYPE_BLUDGEON:
	case TYPE_MAUL:
	case TYPE_POUND:
	case TYPE_CRUSH:
		type = SKILL_WEAPON_BLUDGEONS;
		break;
		
	case TYPE_HIT:
	case TYPE_PUNCH:
	case TYPE_BITE:
	case TYPE_BLAST:
		type = SKILL_WEAPON_EXOTICS;
		break;
		
	default:
		type = -1;
		break;
	}
	
	if (type != -1)
	{
		learned = GET_SKILL(ch, type);

		if		(learned == 0)		bonus = 10;
		else if (learned <= 20)		bonus = 1;
		else if (learned <= 40)		bonus = 2;
		else if (learned <= 60)		bonus = 3;
		else if (learned <= 80)		bonus = 4;
		else if (learned <= 85)		bonus = 5;
		else if (learned <= 90)		bonus = 6;
		else if (learned <= 95)		bonus = 7;
		else if (learned <= 99)		bonus = 8;
		else						bonus = 9;
	}

	return (bonus);
}

/* ******************************************************************** */
/* Ranged Weapons functions                                             */
/* ******************************************************************** */

void strike_missile(CHAR_DATA *ch, CHAR_DATA *tch, OBJ_DATA *missile, int dir, int attacktype)
{
	ROOM_DATA *vict_room = NULL;
	int dam, alive;
	char victname[MAX_STRING_LENGTH];
	
	dam = dice(GET_OBJ_VAL(missile, 1), GET_OBJ_VAL(missile, 2));
	dam += GET_OBJ_VAL(missile, 3);
	
	strcpy(victname, PERS(tch, ch));
	vict_room = tch->in_room;
	
	alive = damage(ch, tch, dam, attacktype);
	
	send_to_char("&b&2You hit!&0\r\n", ch);
	sprintf(buf, "%s flies in from the %s and %s %s.", missile->short_description,
		dirs[rev_dir[dir]], (alive == -1 ? "kills" : "strikes"), victname);
	send_to_room(buf, vict_room);
	if (alive >= 0 )
	{
		sprintf(buf, "&b&3$P flies in from the %s and hits YOU!&0", dirs[rev_dir[dir]]);
		act(buf, FALSE, tch, 0, missile, TO_CHAR);
	}
	return;
} 


void miss_missile(CHAR_DATA *ch, CHAR_DATA *tch, OBJ_DATA *missile, int dir, int attacktype)
{
	sprintf(buf, "$P flies in from the %s and hits the ground!", dirs[rev_dir[dir]]);
	act(buf, FALSE, tch, 0, missile, TO_ROOM);
	act(buf, FALSE, tch, 0, missile, TO_CHAR);
	send_to_char("You missed!\r\n", ch);
}


void fire_missile(CHAR_DATA *ch, char *arg1, OBJ_DATA *missile, int pos, int range, EXIT_DATA *pexit)
{
	ROOM_DATA *nextroom;
	CHAR_DATA *vict;
	bool shot = FALSE;
	int attacktype, distance, dir;
	
	if ( !pexit )
		return;

	if ( !*arg1 )
	{
		send_to_char("Shoot who?\r\n", ch );
		return;
	}

	dir = pexit->vdir;

	if (ROOM_FLAGGED(ch->in_room, ROOM_PEACEFUL))
	{
		send_to_char("This room just has such a peaceful, easy feeling...\r\n", ch);
		return;
	}
	
	if ((nextroom = pexit->to_room) == NULL || !valid_exit(pexit))
	{
		send_to_char("Can't find your target!\r\n", ch);
		return;
	}

	for (distance = 1; nextroom != NULL && distance <= range; distance++)
	{
		for (vict = nextroom->people; vict; vict = vict->next_in_room)
		{
			if ((isname(arg1, take_name(vict, ch))) && (CAN_SEE(ch, vict)))
				break;
		}
		
		if (vict)
		{
			if (missile && ROOM_FLAGGED(vict->in_room, ROOM_PEACEFUL))
			{
				send_to_char("Nah.  Leave them in peace.\r\n", ch);
				return;
			}
			
			switch (GET_OBJ_TYPE(missile))
			{
			case ITEM_MISSILE:
				act("$n aims and fires!", TRUE, ch, 0, 0, TO_ROOM);
				send_to_char("You aim and fire!\r\n", ch);
				switch ( GET_OBJ_VAL(missile, 0) )
				{
				default:	attacktype = TYPE_UNDEFINED;		break;
				case 1:		attacktype = SKILL_WEAPON_BOW;		break;
				case 2:		attacktype = SKILL_WEAPON_SLING;	break;
				case 3:		attacktype = SKILL_WEAPON_CROSSBOW;	break;
				}
				break;
			default:
				attacktype = TYPE_UNDEFINED;
				break;
			}
			
			if (attacktype != TYPE_UNDEFINED)
				shot = success(ch, vict, attacktype, 0);
			else
				shot = FALSE;
			
			if (shot)
				strike_missile(ch, vict, missile, dir, attacktype);
			else
				miss_missile(ch, vict, missile, dir, attacktype);

			extract_obj(missile);
			
			WAIT_STATE(ch, PULSE_VIOLENCE);
			return; 
		} 
		
		nextroom = find_room(pexit->to_room, dir);
	}
	
	send_to_char("Can't find your target!\r\n", ch);

	/* put the missile back into container.. sigh! */
	if (missile)
	{
		OBJ_DATA *cont;
		
		cont = get_missile_cont( ch, GET_OBJ_VAL(missile, 0) );
		
		if ( !cont )
			extract_obj(missile);
		else
		{
			obj_from_obj(missile);
			obj_to_obj(missile, cont);
		}
	}
}