zim/area/
zim/bin/
zim/clans/plists/
zim/corefiles/
zim/doc/muddy/
zim/gods/
zim/log/
zim/player/
zim/skill_tree/
zim/tmp/
/*
 * $Id: handler.c 998 2007-01-21 21:23:49Z zsuzsu $
 */

/***************************************************************************
 *     ANATOLIA 2.1 is copyright 1996-1997 Serdar BULUT, Ibrahim CANPUNAR  *	
 *     ANATOLIA has been brought to you by ANATOLIA consortium		   *
 *	 Serdar BULUT {Chronos}		bulut@rorqual.cc.metu.edu.tr       *
 *	 Ibrahim Canpunar  {Asena}	canpunar@rorqual.cc.metu.edu.tr    *	
 *	 Murat BICER  {KIO}		mbicer@rorqual.cc.metu.edu.tr	   *	
 *	 D.Baris ACAR {Powerman}	dbacar@rorqual.cc.metu.edu.tr	   *	
 *     By using this code, you have agreed to follow the terms of the      *
 *     ANATOLIA license, in the file Anatolia/anatolia.licence             *	
 ***************************************************************************/

/**************************************************************************r
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
 *                                                                         *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
 *  Chastain, Michael Quan, and Mitchell Tse.                              *
 *                                                                         *
 *  In order to use any part of this Merc Diku Mud, you must comply with   *
 *  both the original Diku license in 'license.doc' as well the Merc       *
 *  license in 'license.txt'.  In particular, you may not remove either of *
 *  these copyright notices.                                               *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 ***************************************************************************/

/***************************************************************************
*	ROM 2.4 is copyright 1993-1995 Russ Taylor			   *
*	ROM has been brought to you by the ROM consortium		   *
*	    Russ Taylor (rtaylor@pacinfo.com)				   *
*	    Gabrielle Taylor (gtaylor@pacinfo.com)			   *
*	    Brian Moore (rom@rom.efn.org)				   *
*	By using this code, you have agreed to follow the terms of the	   *
*	ROM license, in the file Rom24/doc/rom.license			   *
***************************************************************************/

#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "merc.h"
#include "debug.h"
#include "obj_prog.h"
#include "raffects.h"
#include "fight.h"
#include "quest.h"
#include "db/db.h"
#include "olc/olc.h"
#include "db/lang.h"
#include "waffects.h"
#include "stats.h"

DECLARE_DO_FUN(do_raffects);
DECLARE_DO_FUN(do_return);
DECLARE_DO_FUN(do_say);
DECLARE_DO_FUN(do_track);
DECLARE_DO_FUN(do_look);

/*
 * Local functions.
 */
void affect_modify(CHAR_DATA * ch, AFFECT_DATA * paf, bool fAdd);
int age_to_num(int age);
int strip_from_container(CHAR_DATA * victim, OBJ_DATA * container,
			 OBJ_INDEX_DATA * pObj);


/*
 * Check if the character has met their tolerance for alchohol
 * tolerance is affected by con, race, size and the drinking skill
 *
 * returns 
 *     PC: TRUE if tolerance is exceeded
 *    NPC: TRUE if affected by intoxication     
 *
 * by Zsuzsu
 */
bool is_drunk(CHAR_DATA * ch)
{
	if (IS_NPC(ch))
		return is_affected(ch, gsn_intoxication);

	return ch->pcdata->condition[COND_DRUNK] > drunk_tolerance(ch);
}

int drunk_tolerance(CHAR_DATA * ch)
{
	int tolerance = 10;

	if (IS_NPC(ch))
		return is_affected(ch, gsn_intoxication);

	tolerance += UMAX(-3, ((get_curr_stat(ch, STAT_CON) - 50) / 2));

	switch (ch->race) {
	case RACE_DWARF:
	case RACE_DUERGAR:
		tolerance += 10;
		break;
	}

	switch (ch->size) {
	case SIZE_TINY:
		tolerance -= 5;
		break;
	case SIZE_SMALL:
		tolerance -= 3;
		break;
	case SIZE_LARGE:
		tolerance += 5;
		break;
	case SIZE_HUGE:
		tolerance += 10;
		break;
	case SIZE_GIANT:
		tolerance += 15;
		break;
	case SIZE_GARGANTUAN:
		tolerance += 30;
		break;

	case SIZE_MEDIUM:
	default:
		tolerance += 0;

	}

	tolerance += get_skill(ch, gsn_drinking) / 3;

	tolerance = URANGE(2, tolerance, 95);

	return tolerance;
}

/* friend stuff -- for NPC's mostly */
bool is_friend(CHAR_DATA * ch, CHAR_DATA * victim)
{
	flag64_t off;
	if (is_same_group(ch, victim))
		return TRUE;

	if (!IS_NPC(ch))
		return FALSE;

	off = ch->pIndexData->off_flags;
	if (!IS_NPC(victim)) {
		if (IS_SET(off, ASSIST_PLAYERS))
			return TRUE;
		else
			return FALSE;
	}

	if (IS_AFFECTED(ch, AFF_CHARM))
		return FALSE;

	if (IS_SET(off, ASSIST_ALL))
		return TRUE;

	if (ch->group && ch->group == victim->group)
		return TRUE;

	if (IS_SET(off, ASSIST_VNUM)
	    && ch->pIndexData == victim->pIndexData)
		return TRUE;

	if (IS_SET(off, ASSIST_RACE) && ch->race == victim->race)
		return TRUE;

	if (IS_SET(off, ASSIST_ALIGN)
	    && !IS_SET(ch->pIndexData->act, ACT_NOALIGN)
	    && !IS_SET(victim->pIndexData->act, ACT_NOALIGN)
	    && ((IS_GOOD(ch) && IS_GOOD(victim)) ||
		(IS_EVIL(ch) && IS_EVIL(victim)) ||
		(IS_NEUTRAL(ch) && IS_NEUTRAL(victim))))
		return TRUE;

	return FALSE;
}

/*
 * Room record:
 * For less than 5 people in room create a new record.
 * Else use the oldest one.
 */

void room_record(const char *name, ROOM_INDEX_DATA * room, int door)
{
	ROOM_HISTORY_DATA *rec;
	int i;

	for (i = 0, rec = room->history; i < 5 && rec != NULL;
	     i++, rec = rec->next);

	if (i < 5) {
		rec = calloc(1, sizeof(*rec));
		rec->next = room->history;
		if (rec->next != NULL)
			rec->next->prev = rec;
		room->history = rec;
		rec->name = NULL;
	} else {
		rec = room->history->next->next->next->next;
		rec->prev->next = NULL;
		rec->next = room->history;
		rec->next->prev = rec;
		room->history = rec;
	}
	rec->prev = NULL;

	if (rec->name) {
		free_string(rec->name);
	}


	rec->name = str_dup(name);
	rec->went = door;
}

/* returns number of people on an object */
int count_users(OBJ_DATA * obj)
{
	CHAR_DATA *fch;
	int count = 0;

	if (obj->in_room == NULL)
		return 0;

	for (fch = obj->in_room->people; fch != NULL; fch = fch->next_in_room)
		if (fch->on == obj)
			count++;

	return count;
}

int floating_time(OBJ_DATA * obj)
{
	int ftime;

	ftime = 0;
	switch (obj->pIndexData->item_type) {
	default:
		break;
	case ITEM_KEY:
		ftime = 1;
		break;
	case ITEM_ARMOR:
		ftime = 2;
		break;
	case ITEM_TREASURE:
		ftime = 2;
		break;
	case ITEM_PILL:
		ftime = 2;
		break;
	case ITEM_POTION:
		ftime = 3;
		break;
	case ITEM_TRASH:
		ftime = 3;
		break;
	case ITEM_FOOD:
		ftime = 4;
		break;
	case ITEM_CONTAINER:
		ftime = 5;
		break;
	case ITEM_CORPSE_NPC:
		ftime = 10;
		break;
	case ITEM_CORPSE_PC:
		ftime = 10;
		break;
	}
	ftime = number_fuzzy(ftime);

	return (ftime < 0 ? 0 : ftime);
}

/* for immunity, vulnerabiltiy, and resistant
   the 'globals' (magic and weapons) may be overriden
   three other cases -- wood, silver, and iron -- are checked in fight.c */

int check_immune(CHAR_DATA * ch, int dam_type)
{
	int immune, def;
	int bit;

	immune = -1;
	def = IS_NORMAL;

	if (dam_type == DAM_NONE)
		return immune;

	if (dam_type <= 3) {
		if (IS_SET(ch->imm_flags, IMM_WEAPON))
			def = IS_IMMUNE;
		else if (IS_SET(ch->res_flags, RES_WEAPON))
			def = IS_RESISTANT;
		else if (IS_SET(ch->vuln_flags, VULN_WEAPON))
			def = IS_VULNERABLE;
	} else {		/* magical attack */

		if (IS_SET(ch->imm_flags, IMM_MAGIC))
			def = IS_IMMUNE;
		else if (IS_SET(ch->res_flags, RES_MAGIC))
			def = IS_RESISTANT;
		else if (IS_SET(ch->vuln_flags, VULN_MAGIC))
			def = IS_VULNERABLE;
	}

	/* set bits to check -- VULN etc. must ALL be the same or this will fail */
	switch (dam_type) {
	case (DAM_BASH):
		bit = IMM_BASH;
		break;
	case (DAM_PIERCE):
		bit = IMM_PIERCE;
		break;
	case (DAM_SLASH):
		bit = IMM_SLASH;
		break;
	case (DAM_FIRE):
		bit = IMM_FIRE;
		break;
	case (DAM_COLD):
		bit = IMM_COLD;
		break;
	case (DAM_LIGHTNING):
		bit = IMM_LIGHTNING;
		break;
	case (DAM_ACID):
		bit = IMM_ACID;
		break;
	case (DAM_POISON):
		bit = IMM_POISON;
		break;
	case (DAM_NEGATIVE):
		bit = IMM_NEGATIVE;
		break;
	case (DAM_HOLY):
		bit = IMM_HOLY;
		break;
	case (DAM_ENERGY):
		bit = IMM_ENERGY;
		break;
	case (DAM_MENTAL):
		if (IS_IMMORTAL(ch))
			return IS_IMMUNE;
		bit = IMM_MENTAL;
		break;
	case (DAM_DISEASE):
		bit = IMM_DISEASE;
		break;
	case (DAM_DROWNING):
		bit = IMM_DROWNING;
		break;
	case (DAM_LIGHT):
		bit = IMM_LIGHT;
		break;
	case (DAM_CHARM):
		if (IS_IMMORTAL(ch))
			return IS_IMMUNE;
		bit = IMM_CHARM;
		break;
	case (DAM_SOUND):
		bit = IMM_SOUND;
		break;
	default:
		return def;
	}

	if (IS_SET(ch->imm_flags, bit))
		immune = IS_IMMUNE;
	else if (IS_SET(ch->res_flags, bit) && immune != IS_IMMUNE)
		immune = IS_RESISTANT;
	else if (IS_SET(ch->vuln_flags, bit)) {
		if (immune == IS_IMMUNE)
			immune = IS_RESISTANT;
		else if (immune == IS_RESISTANT)
			immune = IS_NORMAL;
		else
			immune = IS_VULNERABLE;
	}

	if (!IS_NPC(ch) && get_curr_stat(ch, STAT_CHA) < 45
	    && dam_type == DAM_CHARM)
		immune = IS_VULNERABLE;

	if (immune == -1)
		return def;
	else
		return immune;
}

void reset_obj_affects(CHAR_DATA * ch, OBJ_DATA * obj, AFFECT_DATA * af)
{
	for (; af != NULL; af = af->next) {
		int mod = af->modifier;

		switch (af->location) {
		case APPLY_MANA:
			ch->max_mana -= mod;
			break;
		case APPLY_HIT:
			ch->max_hit -= mod;
			break;
		case APPLY_MOVE:
			ch->max_move -= mod;
			break;
		}
	}
}

/* used to de-screw characters */
void reset_char(CHAR_DATA * ch)
{
	int loc, mod, stat;
	OBJ_DATA *obj;
	AFFECT_DATA *af;
	int i;

	if (IS_NPC(ch))
		return;

	if (ch->pcdata->perm_hit == 0
	    || ch->pcdata->perm_mana == 0
	    || ch->pcdata->perm_move == 0 || ch->pcdata->last_level == 0) {
		/* do a FULL reset */
		for (loc = 0; loc < MAX_WEAR; loc++) {
			obj = get_eq_char(ch, loc);
			if (obj == NULL)
				continue;
			if (!IS_SET(obj->extra_flags, ITEM_ENCHANTED))
				reset_obj_affects(ch, obj,
						  obj->pIndexData->affected);
			reset_obj_affects(ch, obj, obj->affected);
		}

		/* now reset the permanent stats */
		ch->pcdata->perm_hit = ch->max_hit;
		ch->pcdata->perm_mana = ch->max_mana;
		ch->pcdata->perm_move = ch->max_move;
		ch->pcdata->last_level = ch->played / 3600;
		if (ch->pcdata->true_sex < 0 || ch->pcdata->true_sex > 2) {
			if (ch->sex > 0 && ch->sex < 3)
				ch->pcdata->true_sex = ch->sex;
			else
				ch->pcdata->true_sex = 0;
		}
	}

	/* now restore the character to his/her true condition */
	for (stat = 0; stat < MAX_STATS; stat++)
		ch->mod_stat[stat] = 0;

	if (ch->pcdata->true_sex < 0 || ch->pcdata->true_sex > 2)
		ch->pcdata->true_sex = 0;
	ch->sex = ch->pcdata->true_sex;
	ch->max_hit = ch->pcdata->perm_hit;
	ch->max_mana = ch->pcdata->perm_mana;
	ch->max_move = ch->pcdata->perm_move;

	for (i = 0; i < 4; i++)
		ch->armor[i] = 100;

	ch->hitroll = 0;
	ch->damroll = 0;
	ch->saving_throw = 0;
	ch->drain_level = 0;

	/* now start adding back the effects */
	for (loc = 0; loc < MAX_WEAR; loc++) {
		obj = get_eq_char(ch, loc);
		if (obj == NULL)
			continue;
		for (i = 0; i < 4; i++)
			ch->armor[i] -= apply_ac(ch, obj, loc, i);

		if (!IS_SET(obj->extra_flags, ITEM_ENCHANTED))
			for (af = obj->pIndexData->affected; af != NULL;
			     af = af->next) {
				mod = af->modifier;
				switch (af->location) {
				case APPLY_STR:
					ch->mod_stat[STAT_STR] += mod;
					break;
				case APPLY_DEX:
					ch->mod_stat[STAT_DEX] += mod;
					break;
				case APPLY_INT:
					ch->mod_stat[STAT_INT] += mod;
					break;
				case APPLY_WIS:
					ch->mod_stat[STAT_WIS] += mod;
					break;
				case APPLY_CON:
					ch->mod_stat[STAT_CON] += mod;
					break;
				case APPLY_CHA:
					ch->mod_stat[STAT_CHA] += mod;
					break;
				case APPLY_LCK:
					ch->mod_stat[STAT_LCK] += mod;
					break;

				case APPLY_MANA:
					ch->max_mana += mod;
					break;
				case APPLY_HIT:
					ch->max_hit += mod;
					break;
				case APPLY_MOVE:
					ch->max_move += mod;
					break;
				case APPLY_AGE:
					ch->played += age_to_num(mod);
					break;
				case APPLY_AC:
					for (i = 0; i < 4; i++)
						ch->armor[i] += mod;
					break;
				case APPLY_HITROLL:
					ch->hitroll += mod;
					break;
				case APPLY_DAMROLL:
					ch->damroll += mod;
					break;
				case APPLY_SIZE:
					ch->size += mod;
					break;
				case APPLY_LEVEL:
					ch->drain_level += mod;
					break;
				case APPLY_SAVES:
					ch->saving_throw += mod;
					break;
				case APPLY_SAVING_ROD:
					ch->saving_throw += mod;
					break;
				case APPLY_SAVING_PETRI:
					ch->saving_throw += mod;
					break;
				case APPLY_SAVING_BREATH:
					ch->saving_throw += mod;
					break;
				case APPLY_SAVING_SPELL:
					ch->saving_throw += mod;
					break;
				}
			}

		for (af = obj->affected; af != NULL; af = af->next) {
			mod = af->modifier;
			switch (af->location) {
			case APPLY_STR:
				ch->mod_stat[STAT_STR] += mod;
				break;
			case APPLY_DEX:
				ch->mod_stat[STAT_DEX] += mod;
				break;
			case APPLY_INT:
				ch->mod_stat[STAT_INT] += mod;
				break;
			case APPLY_WIS:
				ch->mod_stat[STAT_WIS] += mod;
				break;
			case APPLY_CON:
				ch->mod_stat[STAT_CON] += mod;
				break;
			case APPLY_LCK:
				ch->mod_stat[STAT_LCK] += mod;
				break;

			case APPLY_MANA:
				ch->max_mana += mod;
				break;
			case APPLY_HIT:
				ch->max_hit += mod;
				break;
			case APPLY_MOVE:
				ch->max_move += mod;
				break;
			case APPLY_AGE:
				ch->played += age_to_num(mod);
				break;

			case APPLY_AC:
				for (i = 0; i < 4; i++)
					ch->armor[i] += mod;
				break;
			case APPLY_HITROLL:
				ch->hitroll += mod;
				break;
			case APPLY_DAMROLL:
				ch->damroll += mod;
				break;
			case APPLY_SIZE:
				ch->size += mod;
				break;
			case APPLY_LEVEL:
				ch->drain_level += mod;
				break;
			case APPLY_SAVES:
				ch->saving_throw += mod;
				break;
			case APPLY_SAVING_ROD:
				ch->saving_throw += mod;
				break;
			case APPLY_SAVING_PETRI:
				ch->saving_throw += mod;
				break;
			case APPLY_SAVING_BREATH:
				ch->saving_throw += mod;
				break;
			case APPLY_SAVING_SPELL:
				ch->saving_throw += mod;
				break;
			}
		}
	}

	/* now add back spell effects */
	for (af = ch->affected; af != NULL; af = af->next) {
		mod = af->modifier;
		switch (af->location) {
		case APPLY_STR:
			ch->mod_stat[STAT_STR] += mod;
			break;
		case APPLY_DEX:
			ch->mod_stat[STAT_DEX] += mod;
			break;
		case APPLY_INT:
			ch->mod_stat[STAT_INT] += mod;
			break;
		case APPLY_WIS:
			ch->mod_stat[STAT_WIS] += mod;
			break;
		case APPLY_CON:
			ch->mod_stat[STAT_CON] += mod;
			break;
		case APPLY_CHA:
			ch->mod_stat[STAT_CHA] += mod;
			break;
		case APPLY_LCK:
			ch->mod_stat[STAT_LCK] += mod;
			break;

		case APPLY_MANA:
			ch->max_mana += mod;
			break;
		case APPLY_HIT:
			ch->max_hit += mod;
			break;
		case APPLY_MOVE:
			ch->max_move += mod;
			break;

		case APPLY_AC:
			for (i = 0; i < 4; i++)
				ch->armor[i] += mod;
			break;
		case APPLY_HITROLL:
			ch->hitroll += mod;
			break;
		case APPLY_DAMROLL:
			ch->damroll += mod;
			break;
		case APPLY_SIZE:
			ch->size += mod;
			break;
		case APPLY_LEVEL:
			ch->drain_level += mod;
			break;
		case APPLY_SAVES:
			ch->saving_throw += mod;
			break;
		case APPLY_SAVING_ROD:
			ch->saving_throw += mod;
			break;
		case APPLY_SAVING_PETRI:
			ch->saving_throw += mod;
			break;
		case APPLY_SAVING_BREATH:
			ch->saving_throw += mod;
			break;
		case APPLY_SAVING_SPELL:
			ch->saving_throw += mod;
			break;
		}
	}
	/* make sure sex is RIGHT! */
	if (ch->sex < 0 || ch->sex > 2)
		ch->sex = ch->pcdata->true_sex;

}

/*
 * Retrieve a character's age.
 */
int get_age(CHAR_DATA * ch)
{
	return !IS_NPC(ch)
	    ? race_lookup(ch->race)->pcdata->age_modifier
	    + (ch->played + (int) (current_time - ch->logon)) / 72000 : 42;
}

int age_to_num(int age)
{
	return age * 72000;
}

/*
 * Retrieve a character's carry capacity.
 */
int can_carry_n(CHAR_DATA * ch)
{
	if (IS_IMMORTAL(ch))
		return 1000;

	if (IS_NPC(ch)
	    && IS_SET(ch->pIndexData->act, ACT_PET)
	    && !IS_SET(ch->pIndexData->attr_flags, MOB_ATTR_PET_ARMORED)
	    && !IS_SET(ch->pIndexData->attr_flags, MOB_ATTR_PET_WEAPONED))
		return 0;

	return MAX_WEAR + get_curr_stat(ch, STAT_DEX)/4 - 10 + ch->size;
}


/*---------------------------------------------------------------------------
 * name list stuff
 *
 * name list is simply string of names
 * separated by spaces. if name contains spaces itself it is enclosed
 * in single quotes
 *
 */

/*
 * See if a string is one of the names of an object.
 */
bool is_name_raw(const char *str, const char *namelist,
		 int (*cmpfun) (const char *, const char *))
{
	char name[MAX_INPUT_LENGTH], part[MAX_INPUT_LENGTH];
	const char *list, *string;

	if (IS_NULLSTR(namelist) || IS_NULLSTR(str))
		return FALSE;

	if (!str_cmp(namelist, "all"))
		return TRUE;

	string = str;
	/* we need ALL parts of string to match part of namelist */
	for (;;) {		/* start parsing string */
		str = one_argument(str, part, sizeof(part));

		if (part[0] == '\0')
			return TRUE;

		/* check to see if this is part of namelist */
		list = namelist;
		for (;;) {	/* start parsing namelist */
			list = one_argument(list, name, sizeof(name));
			if (name[0] == '\0')	/* this name was not found */
				return FALSE;

			if (!cmpfun(string, name))
				return TRUE;	/* full pattern match */

			if (!cmpfun(part, name))
				break;
		}
	}
}

bool is_name(const char *str, const char *namelist)
{
	return is_name_raw(str, namelist, str_prefix);
}

void cat_name(char *buf, const char *name, size_t len)
{
	bool have_spaces = strpbrk(name, " \t") != NULL;

	if (buf[0])
		strnzcat(buf, len, " ");
	if (have_spaces)
		strnzcat(buf, len, "'");
	strnzcat(buf, len, name);
	if (have_spaces)
		strnzcat(buf, len, "'");
}

/* 
 * name_edit flags
 */
#define NE_F_DELETE	(A)	/* delete name if found         */
#define NE_F_ADD	(B)	/* add name if not found        */

/*
 * name_edit - edit 'name' according to 'flags' in name list pointed by 'nl'
 *             if ch == NULL name_edit will be silent
 *	       (and 'editor_name' is not used)
 * Return values: TRUE  - name was found in namelist
 *		  FALSE - name was not found
 *
 */
bool name_edit(const char **nl, const char *name, int flags,
	       CHAR_DATA * ch, const char *editor_name)
{
	bool found = FALSE;
	const char *p = *nl;
	char buf[MAX_STRING_LENGTH];

	if (name == NULL || name[0] == '\0')
		return FALSE;

	buf[0] = '\0';
	for (;;) {
		char arg[MAX_STRING_LENGTH];

		p = first_arg(p, arg, sizeof(arg), FALSE);

		if (arg[0] == '\0')
			break;

		if (!str_cmp(name, arg)) {
			found = TRUE;
			if (IS_SET(flags, NE_F_DELETE))
				continue;
		}

		cat_name(buf, arg, sizeof(buf));
	}

	if (!found) {
		if (!IS_SET(flags, NE_F_ADD))
			return found;

		if (strlen(buf) + strlen(name) + 4 > MAX_STRING_LENGTH) {
			if (ch)
				char_printf(ch, "%s: name list too long\n",
					    editor_name);
			return found;
		}
		cat_name(buf, name, sizeof(buf));
		if (ch)
			char_printf(ch, "%s: %s: name added.\n",
				    editor_name, name);
	} else {
		if (!IS_SET(flags, NE_F_DELETE))
			return found;

		if (ch)
			char_printf(ch, "%s: %s: name removed.\n",
				    editor_name, name);
	}

	free_string(*nl);
	*nl = str_dup(buf);
	return found;
}

bool name_add(const char **nl, const char *name,
	      CHAR_DATA * ch, const char *editor_name)
{
	return name_edit(nl, name, NE_F_ADD, ch, editor_name);
}

bool is_name_empty(const char *nl)
{
	return nl == NULL || nl[0] == '\0';
}

bool name_find(const char *nl, const char *name)
{
	bool found = FALSE;
	const char *p = nl;
	char buf[MAX_STRING_LENGTH];

	if (name == NULL || name[0] == '\0')
		return FALSE;

	buf[0] = '\0';
	for (;;) {
		char arg[MAX_STRING_LENGTH];

		p = first_arg(p, arg, sizeof(arg), FALSE);

		if (arg[0] == '\0')
			break;

		if (!str_cmp(name, arg)) {
			found = TRUE;
		}

		/*      cat_name(buf, arg, sizeof(buf)); */
	}
	return found;
}

bool name_transfer(const char **src, const char **dest)
{
	char name[MAX_STRING_LENGTH];
	bool found = FALSE;
	*src = first_arg(*src, name, sizeof(name), FALSE);
	for (; name[0]; *src = first_arg(*src, name, sizeof(name), FALSE)) {
		name_add(dest, name, NULL, NULL);
		name_delete(src, name, NULL, NULL);
		found = TRUE;
	}
	return found;
}

bool name_delete(const char **nl, const char *name,
		 CHAR_DATA * ch, const char *editor_name)
{
	return name_edit(nl, name, NE_F_DELETE, ch, editor_name);
}

bool name_toggle(const char **nl, const char *name,
		 CHAR_DATA * ch, const char *editor_name)
{
	if (!str_cmp(name, "all")) {
		free_string(*nl);
		*nl = str_dup(name);
		if (ch)
			char_printf(ch, "%s: name list set to ALL.\n",
				    editor_name);
		return TRUE;
	}

	if (!str_cmp(name, "none")) {
		free_string(*nl);
		*nl = str_empty;
		if (ch)
			char_printf(ch, "%s: name list reset.\n", editor_name);
		return TRUE;
	}

	if (!str_cmp(*nl, "all")) {
		free_string(*nl);
		*nl = str_empty;
	}

	return name_edit(nl, name, NE_F_ADD | NE_F_DELETE, ch, editor_name);
}

/* enchanted stuff for eq */
void affect_enchant(OBJ_DATA * obj)
{
	/* okay, move all the old flags into new vectors if we have to */
	if (!IS_SET(obj->extra_flags, ITEM_ENCHANTED)) {
		AFFECT_DATA *paf, *af_new;
		SET_BIT(obj->extra_flags, ITEM_ENCHANTED);

		for (paf = obj->pIndexData->affected;
		     paf != NULL; paf = paf->next) {
			af_new = aff_new();

			af_new->next = obj->affected;
			obj->affected = af_new;

			af_new->where = paf->where;
			af_new->type = UMAX(0, paf->type);
			af_new->level = paf->level;
			af_new->duration = paf->duration;
			af_new->location = paf->location;
			af_new->modifier = paf->modifier;
			af_new->bitvector = paf->bitvector;
		}
	}
}


/*
 * Apply or remove an affect to a character.
 */
void affect_modify(CHAR_DATA * ch, AFFECT_DATA * paf, bool fAdd)
{
	OBJ_DATA *wield, *obj2;
	int mod, i;

	mod = paf->modifier;
	if (fAdd) {
		switch (paf->where) {
		case TO_AFFECTS:
			SET_BIT(ch->affected_by, paf->bitvector);
			break;
		case TO_IMMUNE:
			SET_BIT(ch->imm_flags, paf->bitvector);
			break;
		case TO_RESIST:
			SET_BIT(ch->res_flags, paf->bitvector);
			break;
		case TO_VULN:
			SET_BIT(ch->vuln_flags, paf->bitvector);
			break;
		}
	} else {
		switch (paf->where) {
		case TO_AFFECTS:
			REMOVE_BIT(ch->affected_by, paf->bitvector);
			break;
		case TO_IMMUNE:
			REMOVE_BIT(ch->imm_flags, paf->bitvector);
			break;
		case TO_RESIST:
			REMOVE_BIT(ch->res_flags, paf->bitvector);
			break;
		case TO_VULN:
			REMOVE_BIT(ch->vuln_flags, paf->bitvector);
			break;
		}
		mod = 0 - mod;
	}

	switch (paf->location) {
	case APPLY_NONE:
	case APPLY_CLASS:
	case APPLY_HEIGHT:
	case APPLY_WEIGHT:
	case APPLY_GOLD:
	case APPLY_EXP:
	case APPLY_SEX:
	case APPLY_SPELL_AFFECT:
		break;

	case APPLY_STR:
		ch->mod_stat[STAT_STR] += mod;
		break;
	case APPLY_DEX:
		ch->mod_stat[STAT_DEX] += mod;
		break;
	case APPLY_INT:
		ch->mod_stat[STAT_INT] += mod;
		break;
	case APPLY_WIS:
		ch->mod_stat[STAT_WIS] += mod;
		break;
	case APPLY_CON:
		ch->mod_stat[STAT_CON] += mod;
		break;
	case APPLY_CHA:
		ch->mod_stat[STAT_CHA] += mod;
		break;

	case APPLY_LCK:
		ch->mod_stat[STAT_LCK] += mod;
		break;

	case APPLY_MANA:
		ch->max_mana += mod;
		break;
	case APPLY_HIT:
		ch->max_hit += mod;
		break;
	case APPLY_MOVE:
		ch->max_move += mod;
		break;

	case APPLY_HITROLL:
		ch->hitroll += mod;
		break;
	case APPLY_DAMROLL:
		ch->damroll += mod;
		break;
	case APPLY_LEVEL:
		ch->drain_level += mod;
		break;

	case APPLY_SIZE:
		ch->size += mod;
		break;
	case APPLY_AGE:
		ch->played += age_to_num(mod);
		break;

	case APPLY_AC:
		for (i = 0; i < 4; i++)
			ch->armor[i] += mod;
		break;

	case APPLY_SAVES:
		ch->saving_throw += mod;
		break;
	case APPLY_SAVING_ROD:
		ch->saving_throw += mod;
		break;
	case APPLY_SAVING_PETRI:
		ch->saving_throw += mod;
		break;
	case APPLY_SAVING_BREATH:
		ch->saving_throw += mod;
		break;
	case APPLY_SAVING_SPELL:
		ch->saving_throw += mod;
		break;

	case APPLY_RACE:{
			int from;
			int to;
			race_t *rto;
			race_t *rfrom;

			if (fAdd) {
				from = ORG_RACE(ch);
				to = ch->race = paf->modifier;
			} else {
				from = ch->race;
				to = ch->race = ORG_RACE(ch);
			}

			rfrom = race_lookup(from);
			rto = race_lookup(to);
			if (!rfrom || !rto || !rfrom->pcdata || !rto->pcdata)
				return;

			REMOVE_BIT(ch->affected_by, rfrom->aff);
			SET_BIT(ch->affected_by, rto->aff);
			affect_check(ch, TO_AFFECTS, rfrom->aff);

			REMOVE_BIT(ch->imm_flags, rfrom->imm);
			SET_BIT(ch->imm_flags, rto->imm);
			affect_check(ch, TO_IMMUNE, rfrom->imm);

			REMOVE_BIT(ch->res_flags, rfrom->res);
			SET_BIT(ch->res_flags, rto->res);
			affect_check(ch, TO_RESIST, rfrom->res);

			REMOVE_BIT(ch->vuln_flags, rfrom->vuln);
			SET_BIT(ch->vuln_flags, rto->vuln);
			affect_check(ch, TO_VULN, rfrom->vuln);

			ch->form = rto->form;
			ch->parts = rto->parts;
			ch->size = rto->pcdata->size;
			update_skills(ch);
			break;
		}
	default:
		if (IS_NPC(ch)) {
			log_printf("affect_modify: vnum %d: in room %d: "
				   "unknown location %d.",
				   ch->pIndexData->vnum,
				   ch->in_room ? ch->in_room->vnum : -1,
				   paf->location);
		} else {
			log_printf("affect_modify: %s: unknown location %d.",
				   ch->name, paf->location);
		}
		return;

	}

	/*
	 * Check for weapon wielding.
	 * Guard against recursion (for weapons with affects).
	 */
	if (!IS_NPC(ch) && (wield = get_eq_char(ch, WEAR_WIELD)) != NULL
	    && get_obj_weight(wield) >
	    (get_stat_str_max_wield_weight(ch, HAND_PRIMARY))) {
		static int depth;

		if (depth == 0) {
			depth++;
			act("You drop $p.", ch, wield, NULL, TO_CHAR);
			act("$n drops $p.", ch, wield, NULL, TO_ROOM);
			obj_from_char(wield);
			obj_to_room(wield, ch->in_room);

			if ((obj2 = get_eq_char(ch, WEAR_SECOND_WIELD)) != NULL) {
				act("You wield his second weapon as your first!", ch, NULL, NULL, TO_CHAR);
				act("$n wields his second weapon as first!", ch,
				    NULL, NULL, TO_ROOM);
				unequip_char(ch, obj2);
				equip_char(ch, obj2, WEAR_WIELD);
			}
			depth--;
		}
	}
}


/* find an effect in an affect list */
AFFECT_DATA *affect_find(AFFECT_DATA * paf, int sn)
{
	AFFECT_DATA *paf_find;

	for (paf_find = paf; paf_find != NULL; paf_find = paf_find->next) {
		if (paf_find->type == sn)
			return paf_find;
	}

	return NULL;
}

AFFECT_DATA *affect_find_most_powerful(AFFECT_DATA * paf, int sn)
{
	AFFECT_DATA *paf_find;
	AFFECT_DATA *paf_highest = NULL;

	for (paf_find = paf; paf_find != NULL; paf_find = paf_find->next) {
		if (paf_find->type == sn) {
			if (paf_highest && paf_highest->level > paf_find->level)
				continue;
			else
				paf_highest = paf_find;
		}
	}
	return paf_highest;
}

void affect_check_list(CHAR_DATA * ch, AFFECT_DATA * paf,
		       int where, flag64_t vector)
{
	for (; paf; paf = paf->next)
		if ((where < 0 || paf->where == where)
		    && (paf->bitvector & vector))
			switch (paf->where) {
			case TO_AFFECTS:
				SET_BIT(ch->affected_by, paf->bitvector);
				break;
			case TO_IMMUNE:
				SET_BIT(ch->imm_flags, paf->bitvector);
				break;
			case TO_RESIST:
				SET_BIT(ch->res_flags, paf->bitvector);
				break;
			case TO_VULN:
				SET_BIT(ch->vuln_flags, paf->bitvector);
				break;
			}
}

/* fix object affects when removing one */
void affect_check(CHAR_DATA * ch, int where, flag64_t vector)
{
	OBJ_DATA *obj;

	if (where == TO_OBJECT || where == TO_WEAPON || vector == 0)
		return;

	affect_check_list(ch, ch->affected, where, vector);
	for (obj = ch->carrying; obj != NULL; obj = obj->next_content) {
		if (obj->wear_loc == -1 || obj->wear_loc == WEAR_STUCK_IN)
			continue;
		affect_check_list(ch, obj->affected, where, vector);

		if (IS_SET(obj->extra_flags, ITEM_ENCHANTED))
			continue;

		affect_check_list(ch, obj->pIndexData->affected, where, vector);
	}
}

/*
 * Give an affect to a char.
 */
void affect_to_char(CHAR_DATA * ch, AFFECT_DATA * paf)
{
	AFFECT_DATA *paf_new;

	paf_new = aff_new();

	*paf_new = *paf;
	paf_new->next = ch->affected;
	ch->affected = paf_new;

	affect_modify(ch, paf_new, TRUE);
}

/* give an affect to an object */
void affect_to_obj(OBJ_DATA * obj, AFFECT_DATA * paf)
{
	AFFECT_DATA *paf_new;

	paf_new = aff_new();
	*paf_new = *paf;
	paf_new->next = obj->affected;
	obj->affected = paf_new;

	/* apply any affect vectors to the object's extra_flags */
	if (paf->bitvector)
		switch (paf->where) {
		case TO_OBJECT:
			SET_BIT(obj->extra_flags, paf->bitvector);
			break;
		case TO_WEAPON:
			if (obj->pIndexData->item_type == ITEM_WEAPON)
				SET_BIT(obj->value[ITEM_WEAPON_FLAGS], 
					paf->bitvector);
			break;
		}
}



/*
 * Remove an affect from a char.
 */
void affect_remove(CHAR_DATA * ch, AFFECT_DATA * paf)
{
	int where;
	int vector;

	if (ch->affected == NULL) {
		bug("Affect_remove: no affect.", 0);
		return;
	}

	affect_modify(ch, paf, FALSE);
	where = paf->where;
	vector = paf->bitvector;

	if (paf == ch->affected)
		ch->affected = paf->next;
	else {
		AFFECT_DATA *prev;

		for (prev = ch->affected; prev; prev = prev->next) {
			if (prev->next == paf) {
				prev->next = paf->next;
				break;
			}
		}

		if (prev == NULL) {
			bug("Affect_remove: cannot find paf.", 0);
			return;
		}
	}

	aff_free(paf);
	affect_check(ch, where, vector);
}

void affect_remove_obj(OBJ_DATA * obj, AFFECT_DATA * paf)
{
	int where, vector;

	if (obj->affected == NULL)
		return;

	if (obj->carried_by != NULL && obj->wear_loc != -1)
		affect_modify(obj->carried_by, paf, FALSE);

	where = paf->where;
	vector = paf->bitvector;

	/* remove flags from the object if needed */
	if (paf->bitvector)
		switch (paf->where) {
		case TO_OBJECT:
			REMOVE_BIT(obj->extra_flags, paf->bitvector);
			break;
		case TO_WEAPON:
			if (obj->pIndexData->item_type == ITEM_WEAPON)
				REMOVE_BIT(obj->value[ITEM_WEAPON_FLAGS],
					paf->bitvector);
			break;
		}

	if (paf == obj->affected)
		obj->affected = paf->next;
	else {
		AFFECT_DATA *prev;

		for (prev = obj->affected; prev != NULL; prev = prev->next) {
			if (prev->next == paf) {
				prev->next = paf->next;
				break;
			}
		}

		if (prev == NULL) {
			bug("Affect_remove_object: cannot find paf.", 0);
			return;
		}
	}

	aff_free(paf);

	if (obj->carried_by != NULL && obj->wear_loc != -1)
		affect_check(obj->carried_by, where, vector);
}

/*
 * Strip all affects of a given sn.
 */
void affect_strip(CHAR_DATA * ch, int sn)
{
	AFFECT_DATA *paf;
	AFFECT_DATA *paf_next;

	for (paf = ch->affected; paf; paf = paf_next) {
		paf_next = paf->next;
		if (paf->type == sn)
			affect_remove(ch, paf);
	}
}

/*
 * strip all affects which affect given bitvector
 */
void affect_bit_strip(CHAR_DATA * ch, int where, flag64_t bits)
{
	AFFECT_DATA *paf;
	AFFECT_DATA *paf_next;

	for (paf = ch->affected; paf; paf = paf_next) {
		paf_next = paf->next;
		if (paf->where == where && (paf->bitvector & bits))
			affect_remove(ch, paf);
	}
}

/*
 * Return true if a char is affected by a spell.
 */
bool is_affected(CHAR_DATA * ch, int sn)
{
	AFFECT_DATA *paf;

	for (paf = ch->affected; paf; paf = paf->next)
		if (paf->type == sn)
			return TRUE;

	return FALSE;
}

bool is_bit_affected(CHAR_DATA * ch, int where, flag64_t bits)
{
	AFFECT_DATA *paf;

	for (paf = ch->affected; paf; paf = paf->next)
		if (paf->where == where && (paf->bitvector & bits))
			return TRUE;

	return FALSE;
}

bool has_obj_affect(CHAR_DATA * ch, int vector)
{
	OBJ_DATA *obj;

	for (obj = ch->carrying; obj; obj = obj->next_content) {
		AFFECT_DATA *paf;

		if (obj->wear_loc == -1 || obj->wear_loc == WEAR_STUCK_IN)
			continue;

		for (paf = obj->affected; paf; paf = paf->next)
			if (paf->bitvector & vector)
				return TRUE;

		if (IS_SET(obj->extra_flags, ITEM_ENCHANTED))
			continue;

		for (paf = obj->pIndexData->affected; paf; paf = paf->next)
			if (paf->bitvector & vector)
				return TRUE;
	}
	return FALSE;
}

/*
 * Add or enhance an affect.
 */
void affect_join(CHAR_DATA * ch, AFFECT_DATA * paf)
{
	AFFECT_DATA *paf_old;
	bool found;

	found = FALSE;
	for (paf_old = ch->affected; paf_old != NULL; paf_old = paf_old->next) {
		if (paf_old->type == paf->type) {
			paf->level = (paf->level += paf_old->level) / 2;
			paf->duration += paf_old->duration;
			paf->modifier += paf_old->modifier;
			affect_remove(ch, paf_old);
			break;
		}
	}

	affect_to_char(ch, paf);
	return;
}

/*
 * Move a char out of a room.
 */
void char_from_room(CHAR_DATA * ch)
{
	OBJ_DATA *obj;
	ROOM_INDEX_DATA *prev_room = ch->in_room;

	if (ch->in_room == NULL) {
		bug("Char_from_room: NULL.", 0);
		return;
	}

	if (!IS_NPC(ch)) {
		--ch->in_room->area->nplayer;

		/* how many invaders in a clanned area */
		if (!IS_IMMORTAL(ch)
		    && ch->in_room->area->clan != CLAN_FREEMAN
		    && ch->clan != ch->in_room->area->clan)
			ch->in_room->area->interlopers--;
	}

	if ((obj = get_eq_char(ch, WEAR_LIGHT)) != NULL
	    && obj->pIndexData->item_type == ITEM_LIGHT
	    && obj->value[ITEM_LIGHT_DURATION] != 0 && ch->in_room->light > 0)
		--ch->in_room->light;

	if (ch == ch->in_room->people)
		ch->in_room->people = ch->next_in_room;
	else {
		CHAR_DATA *prev;

		for (prev = ch->in_room->people; prev;
		     prev = prev->next_in_room) {
			if (prev->next_in_room == ch) {
				prev->next_in_room = ch->next_in_room;
				break;
			}
		}

		if (prev == NULL)
			bug("Char_from_room: ch not found.", 0);
	}

	ch->in_room = NULL;
	ch->next_in_room = NULL;
	ch->on = NULL;		/* sanity check! */

	if (MOUNTED(ch)) {
		ch->mount->riding = FALSE;
		ch->riding = FALSE;
	}

	if (RIDDEN(ch)) {
		ch->mount->riding = FALSE;
		ch->riding = FALSE;
	}

	if (prev_room && prev_room->affected_by)
		raffect_back_char(prev_room, ch);

	return;
}

/*
 * Move a char into a room.
 */
void char_to_room(CHAR_DATA * ch, ROOM_INDEX_DATA * pRoomIndex)
{
	OBJ_DATA *obj;

	if (pRoomIndex == NULL) {
		ROOM_INDEX_DATA *room;

		bug("Char_to_room: NULL.", 0);

		if ((room = get_room_index(ROOM_VNUM_TEMPLE)) != NULL)
			char_to_room(ch, room);

		return;
	}

	ch->in_room = pRoomIndex;
	ch->next_in_room = pRoomIndex->people;
	pRoomIndex->people = ch;

	if (!IS_NPC(ch)) {
		if (ch->in_room->area->empty) {
			ch->in_room->area->empty = FALSE;
			ch->in_room->area->age = 0;
		}
		++ch->in_room->area->nplayer;

		/* how many invaders in a clanned area */
		if (!IS_IMMORTAL(ch)
		    && ch->in_room->area->clan != CLAN_FREEMAN
		    && ch->clan != ch->in_room->area->clan)
			ch->in_room->area->interlopers++;
	}

	if ((obj = get_eq_char(ch, WEAR_LIGHT)) != NULL
	    && obj->pIndexData->item_type == ITEM_LIGHT 
	    && obj->value[ITEM_LIGHT_DURATION] != 0)
		++ch->in_room->light;

	while (IS_AFFECTED(ch, AFF_PLAGUE)) {
		AFFECT_DATA *af, plague;
		CHAR_DATA *vch;

		for (af = ch->affected; af != NULL; af = af->next) {
			if (af->type == gsn_plague)
				break;
		}

		if (af == NULL) {
			REMOVE_BIT(ch->affected_by, AFF_PLAGUE);
			break;
		}

		if (af->level == 1)
			break;

		plague.where = TO_AFFECTS;
		plague.type = gsn_plague;
		plague.level = af->level - 1;
		plague.duration = number_range(1, 2 * plague.level);
		plague.location = APPLY_STR;
		plague.modifier = -5;
		plague.bitvector = AFF_PLAGUE;

		for (vch = ch->in_room->people; vch != NULL;
		     vch = vch->next_in_room) {
			if (!saves_spell(plague.level - 2, vch, DAM_DISEASE)
			    && !IS_IMMORTAL(vch) &&
			    !IS_AFFECTED(vch, AFF_PLAGUE)
			    && number_bits(6) == 0) {
				char_puts("You feel hot and feverish.\n", vch);
				act("$n shivers and looks very ill.", vch, NULL,
				    NULL, TO_ROOM);
				affect_join(vch, &plague);
			}
		}
		break;
	}

	if (ch->in_room->affected_by) {
		if (IS_IMMORTAL(ch))
			do_raffects(ch, str_empty);
		else
			raffect_to_char(ch->in_room, ch);
	}

	if (ch->desc && OLCED(ch) && IS_EDIT(ch, ED_ROOM))
		roomed_edit_room(ch, pRoomIndex, TRUE);
}

/*
 * Give an obj to a char.
 */
void obj_to_char(OBJ_DATA * obj, CHAR_DATA * ch)
{
	obj->next_content = ch->carrying;
	ch->carrying = obj;
	obj->carried_by = ch;
	obj->in_room = NULL;
	obj->in_obj = NULL;
	ch->carry_number += get_obj_number(obj);
	ch->carry_weight += get_obj_weight(obj);
}

/*
 * medals are a different object type and use
 * different commands to give them to players.
 * This is not done in obj_to_char() because
 * we want a player to be able to award other
 * players these medals.
 *
 * by Zsuzsu
 */
void medal_to_char(OBJ_DATA * obj, CHAR_DATA * ch)
{
	if (IS_NPC(ch)) {
		BUG("medal_to_char: tried to give to NPC [%s] %s",
			ch->name, obj->name);
		return;
	}
	obj->next_content = ch->pcdata->medals;
	ch->pcdata->medals = obj;
	obj->carried_by = ch;
	obj->in_room = NULL;
	obj->in_obj = NULL;
	obj->wear_loc = WEAR_MEDAL;
}

void award_to_char(OBJ_DATA * obj, CHAR_DATA * ch)
{
	if (IS_NPC(ch)) {
		BUG("award_to_char: tried to give to NPC [%s] %s",
			ch->name, obj->name);
		return;
	}
	obj->next_content = ch->pcdata->awards;
	ch->pcdata->awards = obj;
	obj->carried_by = ch;
	obj->in_room = NULL;
	obj->in_obj = NULL;
	obj->wear_loc = WEAR_AWARD;
}

/*
 * Take an obj from its character.
 */
void obj_from_char(OBJ_DATA * obj)
{
	CHAR_DATA *ch;
	bool weightless = FALSE;

	if ((ch = obj->carried_by) == NULL) {
		bug("Obj_from_char: null ch.", 0);
		log_printf("Name %s", obj->name);
		return;
	}

	if (obj->wear_loc != WEAR_NONE)
		unequip_char(ch, obj);

	if (ch->carrying == obj)
		ch->carrying = obj->next_content;
	else if (!IS_NPC(ch) && ch->pcdata->medals == obj)
		ch->pcdata->medals = obj->next_content;
	else if (!IS_NPC(ch) && ch->pcdata->awards == obj)
		ch->pcdata->awards = obj->next_content;
	else {
		OBJ_DATA *prev;

		for (prev = ch->carrying; prev; prev = prev->next_content) {
			if (prev->next_content == obj) {
				prev->next_content = obj->next_content;
				break;
			}
		}

		if (prev == NULL) {
			for (prev = ch->pcdata->medals; prev;
			     prev = prev->next_content) {
				if (prev->next_content == obj) {
					prev->next_content = obj->next_content;
					weightless = TRUE;
					break;
				}
			}
		}

		if (prev == NULL) {
			for (prev = ch->pcdata->awards; prev;
			     prev = prev->next_content) {
				if (prev->next_content == obj) {
					prev->next_content = obj->next_content;
					weightless = TRUE;
					break;
				}
			}
		}

		if (prev == NULL)
			BUG("Obj_from_char: obj not in list [%d] %s.",
			    obj->pIndexData->vnum, obj->name);
	}

	obj->carried_by = NULL;
	obj->next_content = NULL;
	if (!weightless) {
		ch->carry_number -= get_obj_number(obj);
		ch->carry_weight -= get_obj_weight(obj);
	}
}

/*
 * Find the ac value of an obj, including position effect.
 */
int apply_ac(CHAR_DATA *ch, OBJ_DATA * obj, int iWear, int type)
{
	int ac = 0;
	if (obj->pIndexData->item_type != ITEM_ARMOR)
		return 0;

	switch (iWear) {
	case WEAR_BODY:		ac = 3 * obj->value[type];	break;
	case WEAR_HEAD:		ac = 2 * obj->value[type];	break;
	case WEAR_LEGS:		ac = 2 * obj->value[type];	break;
	case WEAR_FEET:		ac = obj->value[type];		break;
	case WEAR_HANDS:	ac = obj->value[type];		break;
	case WEAR_ARMS:		ac = obj->value[type];		break;
	case WEAR_SHIELD:	ac = 2 * obj->value[type];	break;
	case WEAR_FINGER_L:	ac = 0;				break;
	case WEAR_FINGER_R:	ac = 0;				break;
	case WEAR_NECK_1:	ac = obj->value[type];		break;
	case WEAR_NECK_2:	ac = obj->value[type];		break;
	case WEAR_ABOUT:	ac = 2 * obj->value[type];	break;
	case WEAR_WAIST: 	ac = obj->value[type];		break;
	case WEAR_WRIST_L:	ac = obj->value[type];		break;
	case WEAR_WRIST_R:	ac = obj->value[type];		break;
	case WEAR_HOLD:		ac = obj->value[type];		break;
	}

	/* modify AC for items to simulate armor types not set by builders */
	if (obj->value[ITEM_ARMOR_TYPE] == 0)
		ac = ac * class_lookup(ch->class)->ac_rate / 100;

	return ac;
}

/*
 * Find a piece of eq on a character.
 */
OBJ_DATA *get_eq_char(CHAR_DATA * ch, int iWear)
{
	OBJ_DATA *obj;

	if (ch == NULL)
		return NULL;

	for (obj = ch->carrying; obj != NULL; obj = obj->next_content)
		if (obj->wear_loc == iWear)
			return obj;

	return NULL;
}

/*
 * Equip a char with an obj. Return obj on success. Otherwise returns NULL.
 */
OBJ_DATA *equip_char(CHAR_DATA * ch, OBJ_DATA * obj, int iWear)
{
	AFFECT_DATA *paf;
	int i;

	if (iWear == WEAR_STUCK_IN) {
		obj->wear_loc = iWear;
		return obj;
	}

	if (get_eq_char(ch, iWear)) {
		if (IS_NPC(ch)) {
			log_printf("equip_char: vnum %d: in_room %d: "
				   "obj vnum %d: location %s: "
				   "already equipped.",
				   ch->pIndexData->vnum,
				   ch->in_room ? ch->in_room->vnum : -1,
				   obj->pIndexData->vnum,
				   flag_string(wear_loc_flags, iWear));
		} else {
			log_printf("equip_char: %s: location %s: "
				   "already equipped.",
				   ch->name,
				   flag_string(wear_loc_flags, iWear));
		}
		return NULL;
	}

	if ((IS_OBJ_STAT(obj, ITEM_ANTI_EVIL) && IS_EVIL(ch))
	    || (IS_OBJ_STAT(obj, ITEM_ANTI_GOOD) && IS_GOOD(ch))
	    || (IS_OBJ_STAT(obj, ITEM_ANTI_NEUTRAL) && IS_NEUTRAL(ch))) {
		/*
		 * Thanks to Morgenes for the bug fix here!
		 */
		act("You are zapped by $p and drop it.", ch, obj, NULL,
		    TO_CHAR);
		act("$n is zapped by $p and drops it.", ch, obj, NULL, TO_ROOM);
		obj_from_char(obj);
		obj_to_room(obj, ch->in_room);
		return NULL;
	}

	for (i = 0; i < 4; i++)
		ch->armor[i] -= apply_ac(ch, obj, iWear, i);
	obj->wear_loc = iWear;

	if (!IS_SET(obj->extra_flags, ITEM_ENCHANTED))
		for (paf = obj->pIndexData->affected; paf; paf = paf->next)
			if (paf->location != APPLY_SPELL_AFFECT)
				affect_modify(ch, paf, TRUE);
	for (paf = obj->affected; paf; paf = paf->next)
		if (paf->location == APPLY_SPELL_AFFECT)
			affect_to_char(ch, paf);
		else
			affect_modify(ch, paf, TRUE);

	if (obj->pIndexData->item_type == ITEM_LIGHT
	    && obj->value[ITEM_LIGHT_DURATION] != 0 && ch->in_room != NULL)
		++ch->in_room->light;

	oprog_call(OPROG_WEAR, obj, ch, NULL);
	return obj;
}

void strip_obj_affects(CHAR_DATA * ch, OBJ_DATA * obj, AFFECT_DATA * paf)
{
	AFFECT_DATA *lpaf_next = NULL;
	AFFECT_DATA *lpaf = NULL;

	for (; paf != NULL; paf = paf->next) {
		if (paf->location == APPLY_SPELL_AFFECT) {
			for (lpaf = ch->affected; lpaf != NULL;
			     lpaf = lpaf_next) {
				lpaf_next = lpaf->next;
				if ((lpaf->type == paf->type)
				    && (lpaf->level == paf->level)
				    && (lpaf->location == APPLY_SPELL_AFFECT)) {
					affect_remove(ch, lpaf);
					lpaf_next = NULL;
				}
			}
		} else {
			affect_modify(ch, paf, FALSE);
			affect_check(ch, paf->where, paf->bitvector);
		}
	}
}


/*
 * Unequip a char with an obj.
 */
void unequip_char(CHAR_DATA * ch, OBJ_DATA * obj)
{
	int i;

	if (obj->wear_loc == WEAR_NONE) {
		bug("Unequip_char: already unequipped.", 0);
		return;
	}

	if (obj->wear_loc == WEAR_STUCK_IN) {
		obj->wear_loc = WEAR_NONE;
		return;
	}

	for (i = 0; i < 4; i++)
		ch->armor[i] += apply_ac(ch, obj, obj->wear_loc, i);
	obj->wear_loc = -1;

	if (!IS_SET(obj->extra_flags, ITEM_ENCHANTED))
		strip_obj_affects(ch, obj, obj->pIndexData->affected);
	strip_obj_affects(ch, obj, obj->affected);

	if (obj->pIndexData->item_type == ITEM_LIGHT
	    && obj->value[ITEM_LIGHT_DURATION] != 0
	    && ch->in_room != NULL && ch->in_room->light > 0)
		--ch->in_room->light;

	oprog_call(OPROG_REMOVE, obj, ch, NULL);
}

/*
 * Count occurrences of an obj in a list.
 */
int count_obj_list(OBJ_INDEX_DATA * pObjIndex, OBJ_DATA * list)
{
	OBJ_DATA *obj;
	int nMatch;

	nMatch = 0;
	for (obj = list; obj != NULL; obj = obj->next_content) {
		if (obj->pIndexData == pObjIndex)
			nMatch++;
	}

	return nMatch;
}

/*
 * Move an obj out of a room.
 */
void obj_from_room(OBJ_DATA * obj)
{
	ROOM_INDEX_DATA *in_room;
	CHAR_DATA *ch;

	if ((in_room = obj->in_room) == NULL) {
		bug("obj_from_room: NULL.", 0);
		return;
	}

	for (ch = in_room->people; ch != NULL; ch = ch->next_in_room)
		if (ch->on == obj)
			ch->on = NULL;

	if (obj == in_room->contents)
		in_room->contents = obj->next_content;
	else {
		OBJ_DATA *prev;

		for (prev = in_room->contents; prev; prev = prev->next_content) {
			if (prev->next_content == obj) {
				prev->next_content = obj->next_content;
				break;
			}
		}

		if (prev == NULL) {
			bug("Obj_from_room: obj not found.", 0);
			return;
		}
	}

	obj->in_room = NULL;
	obj->next_content = NULL;
	return;
}

/*
 * Move an obj into a room.
 */
void obj_to_room(OBJ_DATA * obj, ROOM_INDEX_DATA * pRoomIndex)
{
	obj->next_content = pRoomIndex->contents;
	pRoomIndex->contents = obj;
	obj->in_room = pRoomIndex;
	obj->carried_by = NULL;
	obj->in_obj = NULL;

	if (IS_WATER(pRoomIndex)) {
		if (may_float(obj))
			obj->water_float = -1;
		else
			obj->water_float = floating_time(obj);
	}
}

/*
 * Move an object into an object.
 */
void obj_to_obj(OBJ_DATA * obj, OBJ_DATA * obj_to)
{
	if (obj == obj_to) {
		log_printf("obj_to_obj: obj == obj_to (vnum %d)",
			   obj->pIndexData->vnum);
		return;
	}

	if (!obj_to) {
		bug("obj_to_obj: obj(num %d) but obj_to is NULL",
		    obj->pIndexData->vnum);
		return;
	}

	obj->next_content = obj_to->contains;
	obj_to->contains = obj;
	obj->in_obj = obj_to;
	obj->in_room = NULL;
	obj->carried_by = NULL;
	if (IS_SET(obj_to->pIndexData->extra_flags, ITEM_PIT))
		obj->cost = 0;

	for (; obj_to != NULL; obj_to = obj_to->in_obj) {
		if (obj_to->carried_by != NULL) {
/*	    obj_to->carried_by->carry_number += get_obj_number(obj); */
			obj_to->carried_by->carry_weight += get_obj_weight(obj)
			    * WEIGHT_MULT(obj_to) / 100;
		}
	}
}

/*
 * Move an object out of an object.
 */
void obj_from_obj(OBJ_DATA * obj)
{
	OBJ_DATA *obj_from;

	if ((obj_from = obj->in_obj) == NULL) {
		bug("Obj_from_obj: null obj_from.", 0);
		return;
	}

	if (obj == obj_from->contains)
		obj_from->contains = obj->next_content;
	else {
		OBJ_DATA *prev;

		for (prev = obj_from->contains; prev; prev = prev->next_content) {
			if (prev->next_content == obj) {
				prev->next_content = obj->next_content;
				break;
			}
		}

		if (prev == NULL) {
			bug("Obj_from_obj: obj not found.", 0);
			return;
		}
	}

	obj->next_content = NULL;
	obj->in_obj = NULL;

	for (; obj_from != NULL; obj_from = obj_from->in_obj) {
		if (obj_from->carried_by != NULL)
/*	    obj_from->carried_by->carry_number -= get_obj_number(obj); */
			obj_from->carried_by->carry_weight -=
			    get_obj_weight(obj) * WEIGHT_MULT(obj_from) / 100;
	}
}

/*
 * Extract an obj from the world.
 */
void extract_obj(OBJ_DATA * obj, int flags)
{
	OBJ_DATA *obj_content;
	OBJ_DATA *obj_next;

	if (obj->extracted) {
		log_printf("extract_obj: %s, vnum %d: already extracted",
			   obj->name, obj->pIndexData->vnum);
		return;
	} else
		obj->extracted = TRUE;

	/*
	if (IS_SET(obj->extra_flags, ITEM_CLAN))
		return;
	*/

	for (obj_content = obj->contains; obj_content; obj_content = obj_next) {
		obj_next = obj_content->next_content;

		if (!IS_SET(flags, XO_F_NORECURSE)) {
			extract_obj(obj_content, flags);
			continue;
		}

		obj_from_obj(obj_content);
		if (obj->in_room)
			obj_to_room(obj_content, obj->in_room);
		else if (obj->carried_by)
			obj_to_char(obj_content, obj->carried_by);
		else if (obj->in_obj)
			obj_to_obj(obj_content, obj->in_obj);
		else
			extract_obj(obj_content, 0);
	}

	if (obj->in_room)
		obj_from_room(obj);
	else if (obj->carried_by)
		obj_from_char(obj);
	else if (obj->in_obj)
		obj_from_obj(obj);

	if (obj->pIndexData->vnum == OBJ_VNUM_MAGIC_JAR) {
		CHAR_DATA *wch;

		for (wch = char_list; wch && !IS_NPC(wch); wch = wch->next) {
			if (is_name(obj->name, wch->name)) {
				REMOVE_BIT(wch->state_flags, STATE_NOEXP);
				char_puts("Now you catch your spirit.\n", wch);
				break;
			}
		}
	}

	if (object_list == obj)
		object_list = obj->next;
	else {
		OBJ_DATA *prev;

		for (prev = object_list; prev != NULL; prev = prev->next) {
			if (prev->next == obj) {
				prev->next = obj->next;
				break;
			}
		}

		if (prev == NULL) {
			bug("Extract_obj: obj %d not found.",
			    obj->pIndexData->vnum);
			return;
		}
	}

	if (!IS_SET(flags, XO_F_NOCOUNT))
		--obj->pIndexData->count;
	free_obj(obj);
}

/*
 * Extract a char from the world.
 */
void extract_char(CHAR_DATA * ch, int flags)
{
	CHAR_DATA *wch;
	OBJ_DATA *obj;
	OBJ_DATA *obj_next;
	OBJ_DATA *wield;
	int extract_obj_flags;

	if (!IS_SET(flags, XC_F_INCOMPLETE)) {
		/*
		 * only for total extractions should it check
		 */
		if (ch->extracted) {
			/*
			 * if it's already been extracted,
			 * something bad is going on
			 */
			log_printf("Warning! Extraction of %s.", ch->name);
			return;
		} else {
			/*
			 * if it hasn't been extracted yet, now
			 * it's being extracted.
			 */
			ch->extracted = TRUE;
		}
	}

	nuke_pets(ch);

	if (!IS_SET(flags, XC_F_INCOMPLETE))
		die_follower(ch);

	stop_fighting(ch, TRUE);

	if ((wield = get_eq_char(ch, WEAR_WIELD)) != NULL)
		unequip_char(ch, wield);

	extract_obj_flags = (IS_SET(flags, XC_F_COUNT) ? 0 : XO_F_NOCOUNT);
	for (obj = ch->carrying; obj != NULL; obj = obj_next) {
		obj_next = obj->next_content;
		extract_obj(obj, extract_obj_flags);
	}

	if (ch->in_room)
		char_from_room(ch);

	if (IS_SET(flags, XC_F_INCOMPLETE)) {
		char_to_room(ch, get_altar(ch)->room);
		return;
	}

	if (IS_NPC(ch))
		--ch->pIndexData->count;

	if (ch->desc != NULL && ch->desc->original != NULL) {
		do_return(ch, str_empty);
		ch->desc = NULL;
	}

	for (wch = char_list; wch; wch = wch->next) {
		if (wch->reply == ch)
			wch->reply = NULL;
		if (wch->mprog_target == ch)
			wch->mprog_target = NULL;
	}

	if (ch == char_list) {
		char_list = ch->next;
		if (ch == char_list_lastpc)
			char_list_lastpc = NULL;
	} else {
		CHAR_DATA *prev;

		for (prev = char_list; prev; prev = prev->next) {
			if (prev->next == ch)
				break;
		}

		if (!prev) {
			bug("Extract_char: char not found.", 0);
			return;
		}

		prev->next = ch->next;
		if (ch == char_list_lastpc)
			char_list_lastpc = prev;
	}

	if (ch->desc)
		ch->desc->character = NULL;
	free_char(ch);
}

/*
 * Find a char in the room.
 */
CHAR_DATA *get_char_room_raw(CHAR_DATA * ch, const char *name, uint * number,
			     ROOM_INDEX_DATA * room)
{
	CHAR_DATA *rch;
	bool ugly;

	if (!str_cmp(name, "self"))
		return ch;

	ugly = !str_cmp(name, "ugly");

	if (!room) {
		BUG("get_char_room_raw: got NULL room (ch:%s, name:%s)",
			ch->name, name);
		return NULL;
	}

	for (rch = room->people; rch; rch = rch->next_in_room) {
		CHAR_DATA *vch;

		if (!can_see(ch, rch))
			continue;

		if (ugly && *number == 1 && is_affected(rch, gsn_vampire))
			return rch;

		vch = (is_affected(rch, gsn_doppelganger) &&
		       (IS_NPC(ch) || !IS_SET(ch->conf_flags, PLR_CONF_HOLYLIGHT))) ?
		    rch->doppel : rch;
		if (name[0] && !is_name(name, vch->name))
			continue;

		if (!--(*number))
			return rch;
	}

	return NULL;
}

/*
 * Find a char in the room.
 */
CHAR_DATA *get_char_room(CHAR_DATA * ch, const char *argument)
{
	char arg[MAX_INPUT_LENGTH];
	uint number;

	number = number_argument(argument, arg, sizeof(arg));
	if (!number)
		return NULL;

	return get_char_room_raw(ch, arg, &number, ch->in_room);
}

CHAR_DATA *get_char_area(CHAR_DATA * ch, const char *argument)
{
	char arg[MAX_INPUT_LENGTH];
	CHAR_DATA *ach;
	uint number;

	number = number_argument(argument, arg, sizeof(arg));
	if (!number)
		return NULL;

	if ((ach = get_char_room_raw(ch, arg, &number, ch->in_room)))
		return ach;

	if (arg[0] == '\0')
		return NULL;

	for (ach = char_list; ach; ach = ach->next) {
		if (!ach->in_room || ach->in_room == ch->in_room)
			continue;

		if (ach->in_room->area != ch->in_room->area || !can_see(ch, ach)
		    || !is_name(arg, ach->name))
			continue;

		if (!--number)
			return ach;
	}
	return NULL;
}

/*
 * Find a char in the world.
 */
CHAR_DATA *get_char_world(CHAR_DATA * ch, const char *argument)
{
	char arg[MAX_INPUT_LENGTH];
	CHAR_DATA *wch;
	uint number;

	number = number_argument(argument, arg, sizeof(arg));
	if (!number)
		return NULL;

	if ((wch = get_char_room_raw(ch, arg, &number, ch->in_room)))
		return wch;

	if (arg[0] == '\0')
		return NULL;

	for (wch = char_list; wch; wch = wch->next) {
		if (!wch->in_room
		    || wch->in_room == ch->in_room
		    || (ch != NULL && !can_see(ch, wch))
		    || !is_name(arg, wch->name))
			continue;

		if (!--number)
			return wch;
	}

	return NULL;
}

/*
 * gets a character, but doen't check to see if the
 * character can been seen.  Use sparingly.
 */
CHAR_DATA *get_char_world_unrestricted(const char *argument)
{
	char arg[MAX_INPUT_LENGTH];
	CHAR_DATA *wch;
	uint number;

	number = number_argument(argument, arg, sizeof(arg));
	if (!number)
		return NULL;

	if (arg[0] == '\0')
		return NULL;

	for (wch = char_list; wch; wch = wch->next) {
		if (!wch->in_room || !is_name(arg, wch->name))
			continue;

		if (!--number)
			return wch;
	}

	return NULL;
}

int opposite_door(int door)
{
	int opdoor;

	switch (door) {
	case 0:
		opdoor = 2;
		break;
	case 1:
		opdoor = 3;
		break;
	case 2:
		opdoor = 0;
		break;
	case 3:
		opdoor = 1;
		break;
	case 4:
		opdoor = 5;
		break;
	case 5:
		opdoor = 4;
		break;
	default:
		opdoor = -1;
		break;
	}

	return opdoor;
}

CHAR_DATA *find_char(CHAR_DATA * ch, const char *argument, int door, int range)
{
	EXIT_DATA *pExit, *bExit;
	ROOM_INDEX_DATA *dest_room = ch->in_room;
	ROOM_INDEX_DATA *back_room;
	CHAR_DATA *target;
	uint number;
	int opdoor;
	char arg[MAX_INPUT_LENGTH];

	number = number_argument(argument, arg, sizeof(arg));
	if (!number)
		return NULL;

	if ((target = get_char_room_raw(ch, arg, &number, dest_room)))
		return target;

	if ((opdoor = opposite_door(door)) == -1) {
		bug("In find_char wrong door: %d", door);
		char_puts("You don't see that there.\n", ch);
		return NULL;
	}

	while (range > 0) {
		range--;

		/* find target room */
		back_room = dest_room;
		if ((pExit = dest_room->exit[door]) == NULL
		    || (dest_room = pExit->to_room.r) == NULL)
			break;
		if (IS_SET(pExit->exit_info, EX_CLOSED))
			break;

		if (IS_SET(pExit->exit_info, EX_NORANGE)) {
			char_puts("You can't get a clear shot from here.\n",
				ch);
			return NULL;
		}

		/* if there isn't a bi-directional link */
		if ((bExit = dest_room->exit[opdoor]) == NULL
		    || bExit->to_room.r != back_room) {
			char_puts("The path you choose prevents your power "
				  "to pass.\n", ch);
			return NULL;
		}

		if ((target = get_char_room_raw(ch, arg, &number, dest_room)))
			return target;
	}

	char_puts("You don't see that there.\n", ch);
	return NULL;
}

int check_exit(char *arg)
{
	int door = -1;

	if (!str_cmp(arg, "n") || !str_cmp(arg, "north"))
		door = 0;
	else if (!str_cmp(arg, "e") || !str_cmp(arg, "east"))
		door = 1;
	else if (!str_cmp(arg, "s") || !str_cmp(arg, "south"))
		door = 2;
	else if (!str_cmp(arg, "w") || !str_cmp(arg, "west"))
		door = 3;
	else if (!str_cmp(arg, "u") || !str_cmp(arg, "up"))
		door = 4;
	else if (!str_cmp(arg, "d") || !str_cmp(arg, "down"))
		door = 5;

	return door;
}

/*
 * Find a char for range casting.
 * argument must specify target in form '[d.][n.]name' where
 * 'd' - direction
 * 'n' - number
 */
CHAR_DATA *get_char_spell(CHAR_DATA * ch, const char *argument,
			  int *door, int range)
{
	char buf[MAX_INPUT_LENGTH];
	char *p;

	p = strchr(argument, '.');
	if (!p) {
		*door = -1;
		return get_char_room(ch, argument);
	}

	strnzncpy(buf, sizeof(buf), argument, p - argument);
	if ((*door = check_exit(buf)) < 0)
		return get_char_room(ch, argument);

	return find_char(ch, p + 1, *door, range);
}

/*
 * Find some object with a given index data.
 * Used by area-reset 'P' command.
 */
OBJ_DATA *get_obj_type(OBJ_INDEX_DATA * pObjIndex)
{
	OBJ_DATA *obj;

	for (obj = object_list; obj; obj = obj->next)
		if (obj->pIndexData == pObjIndex)
			return obj;

	return NULL;
}

/*
 * flags for get_obj_list_raw
 */
enum {
	GETOBJ_F_WEAR_ANY,	/* any obj->wear_loc                         */
	GETOBJ_F_WEAR_NONE,	/* obj->wear_loc == WEAR_NONE (in inventory) */
	GETOBJ_F_WEAR,		/* obj->wear_loc != WEAR_NONE (worn)         */
};

/*
 * Find an obj in a list.
 */
OBJ_DATA *get_obj_list_raw(CHAR_DATA * ch, const char *name, uint * number,
			   OBJ_DATA * list, int flags)
{
	OBJ_DATA *obj;

	for (obj = list; obj; obj = obj->next_content) {

		if (!can_see_obj(ch, obj)
		    || !is_name(name, obj->name))
			continue;

		switch (flags) {
		case GETOBJ_F_WEAR_NONE:
			if (obj->wear_loc != WEAR_NONE)
				continue;
			break;

		case GETOBJ_F_WEAR:
			if (obj->wear_loc == WEAR_NONE)
				continue;
			break;
		}

		if (!--(*number))
			return obj;
	}

	return NULL;
}

/*
 * Find an obj in the room or in eq/inventory.
 */
OBJ_DATA *get_obj_here_raw(CHAR_DATA * ch, const char *name, uint * number)
{
	OBJ_DATA *obj;

/* search in player's inventory */
	obj = get_obj_list_raw(ch, name, number, ch->carrying,
			       GETOBJ_F_WEAR_NONE);
	if (obj)
		return obj;

/* search in player's eq */
	obj = get_obj_list_raw(ch, name, number, ch->carrying, GETOBJ_F_WEAR);
	if (obj)
		return obj;

/* search in room contents */
	obj = get_obj_list_raw(ch, name, number, ch->in_room->contents,
			       GETOBJ_F_WEAR_ANY);
	if (obj)
		return obj;

	return NULL;
}

/*
 * Find an obj in a list.
 */
OBJ_DATA *get_obj_list(CHAR_DATA * ch, const char *argument, OBJ_DATA * list)
{
	char arg[MAX_INPUT_LENGTH];
	uint number;

	number = number_argument(argument, arg, sizeof(arg));
	if (!number || arg[0] == '\0')
		return NULL;

	return get_obj_list_raw(ch, arg, &number, list, GETOBJ_F_WEAR_ANY);
}

/*
 * Find an obj in player's inventory.
 */
OBJ_DATA *get_obj_carry(CHAR_DATA * ch, const char *argument)
{
	char arg[MAX_INPUT_LENGTH];
	uint number;

	number = number_argument(argument, arg, sizeof(arg));
	if (!number || arg[0] == '\0')
		return NULL;

	return get_obj_list_raw(ch, arg, &number, ch->carrying,
				GETOBJ_F_WEAR_NONE);
}

/*
 * Find an obj in player's medal list.
 */
OBJ_DATA *get_obj_medal(CHAR_DATA * ch, const char *argument)
{
	char arg[MAX_INPUT_LENGTH];
	uint number;

	if (IS_NPC(ch))
		return NULL;

	number = number_argument(argument, arg, sizeof(arg));
	if (!number || arg[0] == '\0')
		return NULL;

	return get_obj_list_raw(ch, arg, &number, ch->pcdata->medals,
				GETOBJ_F_WEAR_ANY);
}

/*
 * Find an obj in player's award list.
 */
OBJ_DATA *get_obj_award(CHAR_DATA * ch, const char *argument)
{
	char arg[MAX_INPUT_LENGTH];
	uint number;

	if (IS_NPC(ch))
		return NULL;

	number = number_argument(argument, arg, sizeof(arg));
	if (!number || arg[0] == '\0')
		return NULL;

	return get_obj_list_raw(ch, arg, &number, ch->pcdata->awards,
				GETOBJ_F_WEAR_ANY);
}

/*
 * Find an obj in player's equipment.
 */
OBJ_DATA *get_obj_wear(CHAR_DATA * ch, const char *argument)
{
	char arg[MAX_INPUT_LENGTH];
	uint number;

	number = number_argument(argument, arg, sizeof(arg));
	if (!number || arg[0] == '\0')
		return NULL;

	return get_obj_list_raw(ch, arg, &number, ch->carrying, GETOBJ_F_WEAR);
}

/*
 * Find an obj in the room or in inventory.
 */
OBJ_DATA *get_obj_here(CHAR_DATA * ch, const char *argument)
{
	char arg[MAX_INPUT_LENGTH];
	uint number;

	number = number_argument(argument, arg, sizeof(arg));
	if (!number || arg[0] == '\0')
		return NULL;

	return get_obj_here_raw(ch, arg, &number);
}

OBJ_DATA *get_obj_room(CHAR_DATA * ch, const char *argument)
{
	OBJ_DATA *obj;
	CHAR_DATA *vch;
	char arg[MAX_INPUT_LENGTH];
	uint number;

	number = number_argument(argument, arg, sizeof(arg));
	if (!number || arg[0] == '\0')
		return NULL;

	if ((obj = get_obj_here_raw(ch, arg, &number)))
		return obj;

	for (vch = ch->in_room->people; vch; vch = vch->next_in_room) {
		/*
		 * search in the vch's inventory
		 */
		obj = get_obj_list_raw(ch, arg, &number, vch->carrying,
				       GETOBJ_F_WEAR_NONE);
		if (obj)
			return obj;

		/*
		 * search in the vch's eq
		 */
		obj = get_obj_list_raw(ch, arg, &number, vch->carrying,
				       GETOBJ_F_WEAR);
		if (obj)
			return obj;
	}

	return NULL;
}

/*
 * Find an obj in the world.
 */
OBJ_DATA *get_obj_world(CHAR_DATA * ch, const char *argument)
{
	char arg[MAX_INPUT_LENGTH];
	OBJ_DATA *obj;
	uint number;

	number = number_argument(argument, arg, sizeof(arg));
	if (!number || arg[0] == '\0')
		return NULL;

	if ((obj = get_obj_here_raw(ch, arg, &number)))
		return obj;

	for (obj = object_list; obj; obj = obj->next)
		if (can_see_obj(ch, obj)
		    && obj->carried_by != ch && is_name(arg, obj->name)
		    && !--number)
			return obj;

	return NULL;
}

/*
 * deduct cost from a character
 */
void deduct_cost(CHAR_DATA * ch, uint cost)
{
	/*
	 * price in silver. MUST BE signed for proper exchange operations
	 */
	int silver = UMIN(ch->silver, cost);
	int gold = 0;

	if (silver < cost) {
		gold = (cost - silver + 99) / 100;
		silver = cost - 100 * gold;
	}

	if (ch->gold < gold) {
		log_printf("deduct cost: %s: ch->gold (%d) < gold (%d)",
			   ch->name, ch->gold, gold);
		ch->gold = gold;
	}

	if (ch->silver < silver) {
		log_printf("deduct cost: %s: ch->silver (%d) < silver (%d)",
			   ch->name, ch->silver, silver);
		ch->silver = silver;
	}

	ch->gold -= gold;
	ch->silver -= silver;
}

static inline void
money_form(int lang, char *buf, size_t len, int num, const char *name)
{
	char tmp[MAX_STRING_LENGTH];

	if (num < 0)
		return;

	strnzcpy(tmp, sizeof(tmp),
		 word_form(GETMSG(name, lang), 1, lang, RULES_CASE));
	strnzcpy(buf, len, word_form(tmp, num, lang, RULES_QTY));
}

struct _data {
	int num1;
	const char *name1;
	int num2;
	const char *name2;
};

static void money_cb(int lang, const char **p, void *arg)
{
	char buf1[MAX_STRING_LENGTH];
	char buf2[MAX_STRING_LENGTH];

	const char *q;
	struct _data *d = (struct _data *) arg;

	if (IS_NULLSTR(*p))
		return;

	money_form(lang, buf1, sizeof(buf1), d->num1, d->name1);
	money_form(lang, buf2, sizeof(buf2), d->num2, d->name2);

	q = str_printf(*p, d->num1, buf1, d->num2, buf2);
	free_string(*p);
	*p = q;
}

static void
money_descr(mlstring ** descr,
	    int num1, const char *name1, int num2, const char *name2)
{
	struct _data data;

	data.num1 = num1;
	data.num2 = num2;
	data.name1 = name1;
	data.name2 = name2;

	mlstr_for_each(descr, &data, money_cb);
}

/*
 * Create a 'money' obj.
 */
OBJ_DATA *create_money(int gold, int silver)
{
	OBJ_INDEX_DATA *pObjIndex;
	OBJ_DATA *obj;

	if (gold < 0 || silver < 0 || (gold == 0 && silver == 0)) {
		log_printf("create_money: gold %d, silver %d", gold, silver);
		gold = UMAX(1, gold);
		silver = UMAX(1, silver);
	}

	if (gold == 0 && silver == 1)
		obj = create_obj(get_obj_index(OBJ_VNUM_SILVER_ONE), 0);
	else if (gold == 1 && silver == 0)
		obj = create_obj(get_obj_index(OBJ_VNUM_GOLD_ONE), 0);
	else if (silver == 0) {
		pObjIndex = get_obj_index(OBJ_VNUM_GOLD_SOME);
		obj = create_obj(pObjIndex, 0);
		money_descr(&obj->short_descr, gold, "gold coins", -1, NULL);
		obj->value[ITEM_MONEY_GOLD] = gold;
		obj->cost = 100 * gold;
		obj->weight = gold / 5;
	} else if (gold == 0) {
		pObjIndex = get_obj_index(OBJ_VNUM_SILVER_SOME);
		obj = create_obj(pObjIndex, 0);
		money_descr(&obj->short_descr,
			    silver, "silver coins", -1, NULL);
		obj->value[ITEM_MONEY_SILVER] = silver;
		obj->cost = silver;
		obj->weight = silver / 20;
	} else {
		pObjIndex = get_obj_index(OBJ_VNUM_COINS);
		obj = create_obj(pObjIndex, 0);
		money_descr(&obj->short_descr,
			    silver, "silver coins", gold, "gold coins");
		obj->value[ITEM_MONEY_SILVER] = silver;
		obj->value[ITEM_MONEY_GOLD] = gold;
		obj->cost = 100 * gold + silver;
		obj->weight = gold / 5 + silver / 20;
	}

	return obj;
}


/*
 * Return # of objects which an object counts as.
 * Thanks to Tony Chamberlain for the correct recursive code here.
 */
int get_obj_number(OBJ_DATA * obj)
{
	int number;
/* 
	if (obj->pIndexData->item_type == ITEM_CONTAINER || obj->pIndexData->item_type == ITEM_MONEY
	||  obj->pIndexData->item_type == ITEM_GEM || obj->pIndexData->item_type == ITEM_JEWELRY)
	    number = 0;
*/
	if (obj->pIndexData->item_type == ITEM_MONEY)
		number = 0;
	else
		number = 1;

/* 
	for (obj = obj->contains; obj != NULL; obj = obj->next_content)
	    number += get_obj_number(obj);
*/
	return number;
}

int get_obj_realnumber(OBJ_DATA * obj)
{
	int number = 1;

	for (obj = obj->contains; obj != NULL; obj = obj->next_content)
		number += get_obj_number(obj);

	return number;
}

/*
 * Return weight of an object, including weight of contents.
 */
int get_obj_weight(OBJ_DATA * obj)
{
	int weight;
	OBJ_DATA *tobj;

	weight = obj->weight;
	for (tobj = obj->contains; tobj != NULL; tobj = tobj->next_content)
		weight += get_obj_weight(tobj) * WEIGHT_MULT(obj) / 100;

	return weight;
}

int get_true_weight(OBJ_DATA * obj)
{
	int weight;

	weight = obj->weight;
	for (obj = obj->contains; obj != NULL; obj = obj->next_content)
		weight += get_obj_weight(obj);

	return weight;
}

/*
 * True if room is dark.
 */
bool room_is_dark(CHAR_DATA * ch)
{
	ROOM_INDEX_DATA *pRoomIndex = ch->in_room;

	if (pRoomIndex == NULL)
		return FALSE;

	if (!IS_NPC(ch) && IS_SET(ch->conf_flags, PLR_CONF_HOLYLIGHT))
		return FALSE;

	if (is_affected(ch, gsn_vampire))
		return FALSE;

	if (IS_SET(ch->state_flags, STATE_GHOST))
		return FALSE;

	if (IS_AFFECTED(ch, AFF_DARK_VISION))
		return FALSE;

	if (pRoomIndex->light > 0)
		return FALSE;

	if (IS_SET(pRoomIndex->room_flags, ROOM_DARK))
		return TRUE;

	if (IS_ROOM_AFFECTED(pRoomIndex, RAFF_MIDNIGHT))
		return TRUE;

	if (pRoomIndex->sector_type == SECT_INSIDE
	    || pRoomIndex->sector_type == SECT_CITY)
		return FALSE;

	if (weather_info.sunlight == SUN_SET
	    || weather_info.sunlight == SUN_DARK)
		return TRUE;

	return FALSE;
}

bool room_dark(ROOM_INDEX_DATA * pRoomIndex)
{
	if (pRoomIndex->light > 0)
		return FALSE;

	if (IS_SET(pRoomIndex->room_flags, ROOM_DARK))
		return TRUE;

	if (pRoomIndex->sector_type == SECT_INSIDE
	    || pRoomIndex->sector_type == SECT_CITY)
		return FALSE;

	if (weather_info.sunlight == SUN_SET
	    || weather_info.sunlight == SUN_DARK)
		return TRUE;

	return FALSE;
}


bool is_room_owner(CHAR_DATA * ch, ROOM_INDEX_DATA * room)
{
	if (room->owner == NULL || room->owner[0] == '\0')
		return FALSE;

	return is_name(ch->name, room->owner);
}

/*
 * True if room is private.
 */
bool room_is_private(ROOM_INDEX_DATA * pRoomIndex)
{
	CHAR_DATA *rch;
	int count;

/*
	if (pRoomIndex->owner != NULL && pRoomIndex->owner[0] != '\0')
		return TRUE;
*/
	count = 0;
	for (rch = pRoomIndex->people; rch != NULL; rch = rch->next_in_room)
		count++;

	if (IS_SET(pRoomIndex->room_flags, ROOM_PRIVATE) && count >= 2)
		return TRUE;

	if (IS_SET(pRoomIndex->room_flags, ROOM_SOLITARY) && count >= 1)
		return TRUE;

	if (IS_SET(pRoomIndex->room_flags, ROOM_IMP_ONLY))
		return TRUE;

	return FALSE;
}

/* visibility on a room -- for entering and exits */
bool can_see_room(CHAR_DATA * ch, ROOM_INDEX_DATA * pRoomIndex)
{
	if (IS_SET(pRoomIndex->room_flags, ROOM_IMP_ONLY)
	    && !IS_TRUSTED(ch, IMPLEMENTOR))
		return FALSE;

	if (IS_SET(pRoomIndex->room_flags, ROOM_GODS_ONLY)
	    && !IS_TRUSTED(ch, GOD))
		return FALSE;

	if (IS_SET(pRoomIndex->room_flags, ROOM_HEROES_ONLY)
	    && !IS_TRUSTED(ch, HERO))
		return FALSE;

	if (IS_SET(pRoomIndex->room_flags, ROOM_NEWBIES_ONLY)
	    && ch->level > LEVEL_NEWBIE && !IS_IMMORTAL(ch))
		return FALSE;

	return TRUE;
}

/*
 * True if char can see victim.
 */
bool can_see(CHAR_DATA * ch, CHAR_DATA * victim)
{
/* RT changed so that WIZ_INVIS has levels */
	if (ch == victim)
		return TRUE;

	if (ch == NULL || victim == NULL) {
		log("can_see: NULL ch or victim");
		return FALSE;
	}

	/* Rulers always see their Stalkers */
	if (!IS_NPC(ch) && HAS_SKILL(ch, gsn_ruler_badge)
	    && IS_NPC(victim) && victim->pIndexData->vnum == MOB_VNUM_STALKER)
		return TRUE;

	if (!IS_NPC(victim) && !IS_TRUSTED(ch, victim->invis_level))
		return FALSE;

	if (IS_CLAN_GUARD(ch))
		return TRUE;

	if (!IS_TRUSTED(ch, victim->incog_level)
	    && ch->in_room != victim->in_room)
		return FALSE;

	if (!IS_NPC(ch) && IS_SET(ch->conf_flags, PLR_CONF_HOLYLIGHT))
		return TRUE;

	if (IS_NPC(victim) && !IS_TRUSTED(ch, victim->invis_level)) {
		AREA_DATA *pArea = area_vnum_lookup(victim->pIndexData->vnum);
		if (pArea == NULL || !IS_BUILDER(ch, pArea))
			return FALSE;
	}

	if (IS_AFFECTED(ch, AFF_BLIND))
		return FALSE;

	if (ch->in_room == NULL)
		return FALSE;

	if (IS_AFFECTED(victim, AFF_FADE)
	    && !IS_AFFECTED(ch, AFF_DETECT_FADE)
	    && victim->fighting == NULL)
		return FALSE;

	if (is_affected(ch, gsn_bush_sense)
	    && victim->in_room != NULL
	    && (victim->in_room->sector_type == SECT_FOREST
		|| victim->in_room->sector_type == SECT_HILLS
		|| victim->in_room->sector_type == SECT_MOUNTAIN))
		return TRUE;

	if (room_is_dark(ch) && !IS_AFFECTED(ch, AFF_INFRARED))
		return FALSE;

	if (IS_AFFECTED(victim, AFF_INVIS)
	    && !IS_AFFECTED(ch, AFF_DETECT_INVIS))
		return FALSE;

	if (get_invis_level(victim) > get_detect_invis_level(ch))
		return FALSE;

	if (IS_AFFECTED(victim, AFF_IMP_INVIS)
	    && !IS_AFFECTED(ch, AFF_DETECT_IMP_INVIS))
		return FALSE;

	if (IS_AFFECTED(victim, AFF_CAMOUFLAGE) &&
	    !IS_AFFECTED(ch, AFF_ACUTE_VISION))
		return FALSE;

	if (IS_AFFECTED(victim, AFF_HIDE)
	    && !IS_AFFECTED(ch, AFF_DETECT_HIDDEN)
	    && victim->fighting == NULL)
		return FALSE;


	return TRUE;
}

int get_invis_level(CHAR_DATA * ch)
{
	AFFECT_DATA *paff;
	int level = 0, skill;
	bool is_mass = FALSE;

	if (!IS_AFFECTED(ch, AFF_INVIS))
		return 0;

	paff = affect_find_most_powerful(ch->affected, gsn_invisibility);
	if (!paff) {
		paff = affect_find_most_powerful(ch->affected, gsn_mass_invis);
		is_mass = (paff != NULL);
	}

	if (!paff) {
		if (IS_NPC(ch))
			level = ch->level - 10;
		else
			level = ch->level;
	} else {
		if (IS_NPC(ch))
			level = paff->level;
		else {
			if (is_mass)
				skill = get_skill(ch, gsn_mass_invis);
			else
				skill = get_skill(ch, gsn_invisibility);

			level = paff->level + (5 * skill / 100);
			DEBUG(DEBUG_INVIS,
				"get_invis_level: mass(%s), skill:%d,"
				" paff:%d, level:%d",
				(is_mass) ? "true" : "false", 
				skill, 
				paff->level, level);
		}
	}

	return UMAX(1, level);
}

int get_detect_invis_level(CHAR_DATA * ch)
{
	AFFECT_DATA *paff;
	int level = 0, skill;

	if (!IS_AFFECTED(ch, AFF_DETECT_INVIS))
		return 0;

	paff = affect_find_most_powerful(ch->affected, gsn_detect_invisibility);

	if (!paff) {
		if (IS_NPC(ch))
			level = ch->level + 3 + (ch->level / 10);
		else
			level = ch->level;
	} else {
		if (IS_NPC(ch))
			level = paff->level;
		else {
			skill = get_skill(ch, gsn_detect_invisibility);
			level = paff->level + (3 * skill / 100)
			    + ((get_curr_stat(ch, STAT_INT) - 50) / 2);
		}
	}
	level = level + (LEVEL(ch) / 10);

	return UMAX(1, level);
}

/*
 * True if char can see obj.
 */
bool can_see_obj(CHAR_DATA * ch, OBJ_DATA * obj)
{
	if (!IS_NPC(ch) && IS_SET(ch->conf_flags, PLR_CONF_HOLYLIGHT))
		return TRUE;

	if (IS_SET(obj->extra_flags, ITEM_VIS_DEATH))
		return FALSE;

	if (IS_AFFECTED(ch, AFF_BLIND) && !IS_OBJ_STAT(obj, ITEM_HUM))
		return FALSE;

	if (obj->pIndexData->item_type == ITEM_LIGHT 
	&& obj->value[ITEM_LIGHT_DURATION] != 0)
		return TRUE;

	if (IS_SET(obj->extra_flags, ITEM_INVIS)
	    && !IS_AFFECTED(ch, AFF_DETECT_INVIS))
		return FALSE;

	if (IS_OBJ_STAT(obj, ITEM_GLOW)
	    || IS_OBJ_STAT(obj, ITEM_HUM))
		return TRUE;

	if (room_is_dark(ch) && !IS_AFFECTED(ch, AFF_INFRARED))
		return FALSE;

	return TRUE;
}

/*
 * True if char can drop obj.
 */
bool can_drop_obj(CHAR_DATA * ch, OBJ_DATA * obj)
{
	if (!IS_SET(obj->extra_flags, ITEM_NODROP))
		return TRUE;

	if (IS_IMMORTAL(ch))
		return TRUE;

	return FALSE;
}

int isn_dark_safe(CHAR_DATA * ch)
{
	CHAR_DATA *rch;
	OBJ_DATA *light;
	int light_exist;

	if (!is_affected(ch, gsn_vampire)
	    || IS_SET(ch->in_room->room_flags, ROOM_DARK))
		return 0;

	if (ch->in_room->sector_type != SECT_INSIDE
	&& (weather_info.sunlight == SUN_LIGHT 
	|| weather_info.sunlight == SUN_RISE))
		return 2;

	light_exist = 0;
	for (rch = ch->in_room->people; rch; rch = rch->next_in_room) {
		if ((light = get_eq_char(rch, WEAR_LIGHT))
		    && IS_OBJ_STAT(light, ITEM_MAGIC)
		    && light->value[ITEM_LIGHT_DURATION] > 0) {
			light_exist = 1;
			break;
		}
	}

	return light_exist;
}

int count_charmed(CHAR_DATA * ch)
{
	CHAR_DATA *gch;
	int count = 0;

	for (gch = char_list; gch != NULL; gch = gch->next) {
		if (IS_AFFECTED(gch, AFF_CHARM) && gch->master == ch)
			count++;
	}

	if (count >= MAX_CHARM(ch)) {
		char_puts
		    ("You are already controlling as many charmed mobs as you can!\n",
		     ch);
		return count;
	}
	return 0;
}

/*
 * add_mind - remember 'str' in mind buffer of 'ch'
 *	      remember the place to return in mind buffer
 */
void add_mind(CHAR_DATA * ch, const char *str)
{
	if (!IS_NPC(ch) || ch->in_room == NULL)
		return;

	if (ch->in_mind == NULL)
		/* remember a place to return */
		ch->in_mind = str_printf("%d", ch->in_room->vnum);

	if (!is_name(str, ch->in_mind)) {
		const char *p = ch->in_mind;
		ch->in_mind = str_printf("%s %s", ch->in_mind, str);
		free_string(p);
	}
}

/*
 * remove_mind - remove 'str' from mind buffer of 'ch'
 *		 if it was the last revenge - return home
 */
void remove_mind(CHAR_DATA * ch, const char *str)
{
	char buf[MAX_STRING_LENGTH];
	char buff[MAX_STRING_LENGTH];
	char arg[MAX_INPUT_LENGTH];
	const char *mind = ch->in_mind;

	if (!IS_NPC(ch) || ch->in_room == NULL
	    || mind == NULL || !is_name(str, mind))
		return;

	buf[0] = '\0';
	do {
		mind = one_argument(mind, arg, sizeof(arg));
		if (!is_name(str, arg)) {
			if (buf[0] == '\0')
				strnzcpy(buf, sizeof(buf), arg);
			else {
				snprintf(buff, sizeof(buff), "%s %s", buf, arg);
				strnzcpy(buf, sizeof(buf), buff);
			}
		}
	}
	while (mind[0] != '\0');

	free_string(ch->in_mind);
	if (is_number(buf)) {
		do_say(ch, "At last, I took my revenge!");
		back_home(ch);
		ch->in_mind = NULL;
	} else
		ch->in_mind = str_dup(buf);
}

void back_home(CHAR_DATA * ch)
{
	ROOM_INDEX_DATA *location;
	char arg[MAX_INPUT_LENGTH];

	if (!IS_NPC(ch) || ch->in_mind == NULL)
		return;

	one_argument(ch->in_mind, arg, sizeof(arg));
	if ((location = find_location(ch, arg)) == NULL) {
		DEBUG(DEBUG_MOB_AI,
			"back_home: %s[%d] in room %d can't find %s (%s) and is lost.",
			ch->name, 
			ch->pIndexData->vnum,
			(ch->in_room) ? ch->in_room->vnum : 0, arg, 
			ch->in_mind);
		return;
	}

	if (ch->fighting == NULL && location != ch->in_room) {
		act("$n prays for transportation.", ch, NULL, NULL, TO_ROOM);
		recall(ch, location);
	}
}

void path_to_track(CHAR_DATA * ch, CHAR_DATA * victim, int door)
{
	ROOM_INDEX_DATA *temp;
	EXIT_DATA *pExit;
	int opdoor;
	int range = 0;

	SET_FIGHT_TIME(ch);
	if (!IS_NPC(victim))
		SET_FIGHT_TIME(victim);

	if (IS_NPC(victim) && victim->position != POS_DEAD) {
		victim->last_fought = ch;

		if ((opdoor = opposite_door(door)) == -1) {
			bug("In path_to_track wrong door: %d", door);
			return;
		}
		temp = ch->in_room;
		while (1) {
			range++;
			if (victim->in_room == temp)
				break;
			if ((pExit = temp->exit[door]) == NULL
			    || (temp = pExit->to_room.r) == NULL) {
				bug("In path_to_track: couldn't calculate range %d", range);
				return;
			}
			if (range > 100) {
				bug("In path_to_track: range exceeded 100", 0);
				return;
			}
		}

		temp = victim->in_room;
		while (--range > 0) {
			room_record(ch->name, temp, opdoor);
			if ((pExit = temp->exit[opdoor]) == NULL
			    || (temp = pExit->to_room.r) == NULL) {
				log_printf
				    ("[*****] Path to track: Range: %d Room: %d opdoor:%d",
				     range, temp->vnum, opdoor);
				return;
			}
		}
		do_track(victim, str_empty);
	}
}

int pk_range(int level)
{
	return UMAX(4, level / 7 + 2);
}

bool in_PK(CHAR_DATA * ch, CHAR_DATA * victim)
{
	WORLD_AFFECT_DATA *waff = NULL;
	WORLD_AFFECT_DATA *vwaff = NULL;
	int mod = 0;

	if (IS_NPC(ch) || IS_NPC(victim))
		return TRUE;

	if (IS_NEWBIE(ch) || IS_NEWBIE(victim))
		return FALSE;

	if (victim->level < MIN_PK_LEVEL || ch->level < MIN_PK_LEVEL)
		return FALSE;

	if (ch_waffected(ch, WAFF_FFA) && ch_waffected(victim, WAFF_FFA))
		return TRUE;

	if (IS_SET(ch->in_room->room_flags, ROOM_FFA)
	    && IS_SET(victim->in_room->room_flags, ROOM_FFA))
		return TRUE;

	/* if on the person's PK_OK list */
	if ((ch->level > victim->level 
	   && name_find(ch->pcdata->pk_ok_list, victim->name))
	|| (victim->level > ch->level
	   && name_find(victim->pcdata->pk_ok_list, ch->name)))
		return TRUE;

	/* modifier for the PK range */
	if ((waff = ch_waffected(ch, WAFF_PK_RANGE))
	&& (vwaff = ch_waffected(victim, WAFF_PK_RANGE)))
		mod = UMIN(waff->modifier, vwaff->modifier);

	/* level adjustment */
	if (ch != victim && !IS_IMMORTAL(ch)
	    && (ch->level >= (victim->level + pk_range(ch->level)+ mod) ||
		ch->level <= (victim->level - pk_range(ch->level) - mod))
	    && (victim->level >= (ch->level + pk_range(victim->level) + mod) ||
		victim->level <= (ch->level - pk_range(victim->level) - mod)))
		return FALSE;

	return TRUE;
}

bool can_gate(CHAR_DATA * ch, CHAR_DATA * victim)
{
	if (victim == ch
	    || ch->fighting != NULL
	    || victim->in_room == NULL || !can_see_room(ch, victim->in_room)
	    || IS_SET(ch->in_room->room_flags, ROOM_SAFE | ROOM_NORECALL |
		      ROOM_PEACE | ROOM_NOSUMMON)
	    || IS_SET(victim->in_room->room_flags, ROOM_SAFE | ROOM_NORECALL |
		      ROOM_PEACE | ROOM_NOSUMMON)
	    || IS_SET(ch->in_room->area->flags, AREA_CLOSED)
	    || IS_SET(victim->in_room->area->flags, AREA_CLOSED)
	    || room_is_private(victim->in_room)
	    || IS_SET(victim->imm_flags, IMM_SUMMON))
		return FALSE;

	if (IS_NPC(victim))
		return TRUE;

	if (((!in_PK(ch, victim) ||
	      ch->in_room->area != victim->in_room->area) &&
	     IS_SET(victim->conf_flags, PLR_CONF_NOSUMMON))
	    || victim->level > LEVEL_HERO || !guild_ok(ch, victim->in_room))
		return FALSE;

	return TRUE;
}

void transfer_char(CHAR_DATA * ch, CHAR_DATA * vch, ROOM_INDEX_DATA * to_room,
		   const char *msg_out,
		   const char *msg_travel, const char *msg_in)
{
	ROOM_INDEX_DATA *was_in = ch->in_room;

	if (ch != vch)
		act_puts(msg_travel, vch, NULL, ch, TO_VICT, POS_DEAD);

	char_from_room(ch);

	act(msg_out, was_in->people, NULL, ch, TO_ALL);
	act(msg_in, to_room->people, NULL, ch, TO_ALL);

	char_to_room(ch, to_room);

	if (is_affected(ch, gsn_rnet_trap)) {
		affect_strip(ch, gsn_rnet_trap);
		char_puts
		    ("The blood rushes to your feet as you are freed of the net!\n",
		     ch);
	}
	if (!JUST_KILLED(ch))
		do_look(ch, "auto");
}

void recall(CHAR_DATA * ch, ROOM_INDEX_DATA * location)
{
	transfer_char(ch, NULL, location,
		      "$N disappears.", NULL, "$N appears in the room.");
}

void look_at(CHAR_DATA * ch, ROOM_INDEX_DATA * room)
{
	ROOM_INDEX_DATA *was_in = ch->in_room;
	OBJ_DATA *obj;
	bool adjust_light = FALSE;

	if ((obj = get_eq_char(ch, WEAR_LIGHT))
	    && obj->pIndexData->item_type == ITEM_LIGHT 
	    && obj->value[ITEM_LIGHT_DURATION]) {
		adjust_light = TRUE;
		room->light++;
	}

	ch->in_room = room;
	do_look(ch, str_empty);
	ch->in_room = was_in;

	if (adjust_light)
		room->light--;
}

/* random room generation procedure */
ROOM_INDEX_DATA *get_random_room(CHAR_DATA * ch, AREA_DATA * area)
{
	int min_vnum;
	int max_vnum;
	ROOM_INDEX_DATA *room;
	int i = 0;

	if (!area) {
		min_vnum = 1;
		max_vnum = top_vnum_room;
	} else {
		min_vnum = area->min_vnum;
		max_vnum = area->max_vnum;
	}

	for (i = 0; i < 1000; i++) {
		room = get_room_index(number_range(min_vnum, max_vnum));

		if (!room)
			continue;

		if (can_see_room(ch, room)
		    && !room_is_private(room)
		    && !IS_SET(room->room_flags, ROOM_SAFE | ROOM_PEACE)
		    && !(IS_SET(room->area->flags, AREA_CLOSED
				| AREA_NOSUMMON | AREA_CLANHALL
				| AREA_PLAYERHOUSE) && !area)
		    && !IS_SET(room->room_flags, ROOM_NORECALL | ROOM_NOSUMMON)


		    && (!IS_NPC(ch) ||
			!IS_AGGRO(ch, NULL) ||
			!IS_SET(room->room_flags, ROOM_LAW)))
			break;
	}

	return room;
}

const char *PERS2(CHAR_DATA * ch, CHAR_DATA * looker, flag32_t flags)
{
	if (is_affected(ch, gsn_doppelganger)
	    && (IS_NPC(looker) || !IS_SET(looker->conf_flags, PLR_CONF_HOLYLIGHT))
	    && !IS_SET(flags, ACT_REALNAME))
		ch = ch->doppel;

	if (can_see(looker, ch)
	    || (!IS_IMMORTAL(ch) && IS_SET(flags, ACT_REALNAME))) {
		if (IS_NPC(ch)) {
			const char *descr;

			if (IS_SET(flags, ACT_FORMSH)) {
				return format_short(ch->short_descr, ch->name,
						    looker);
			}

			descr = mlstr_cval(ch->short_descr, looker);
			if (IS_SET(flags, ACT_FIXSH))
				return fix_short(descr);

			return descr;
		} else if (is_affected(ch, gsn_vampire) && !IS_IMMORTAL(looker)
			   && !IS_SET(flags, ACT_REALNAME)) {
			return word_form(GETMSG("an ugly creature",
						looker->lang),
					 ch->sex, looker->lang, RULES_GENDER);
		}
		return ch->name;
	}

	if (IS_IMMORTAL(ch)) {
		return word_form(GETMSG("an immortal", looker->lang), ch->sex,
				 looker->lang, RULES_GENDER);
	}

	return "someone";
}

void format_obj(BUFFER * output, OBJ_DATA * obj)
{
	buf_printf(output,
		   "Object '%s' is type %s, extra flags %s.\n"
		   "Wear location: %s\n"
		   "Weight is %d, value is %d, level is %d.\n",
		   obj->name,
		   flag_string(item_types, obj->pIndexData->item_type),
		   flag_string(extra_flags, obj->extra_flags & ~ITEM_ENCHANTED),
		   (obj->pIndexData->item_type == ITEM_LIGHT)
		   ? "light"
		   : flag_string(wear_flags, obj->wear_flags ^ ITEM_TAKE),
		   obj->weight / 10, obj->cost, obj->level);

	if (obj->pIndexData->limit != -1)
		buf_printf(output,
			   "This equipment has been LIMITED by number %d \n",
			   obj->pIndexData->limit);

	switch (obj->pIndexData->item_type) {
	case ITEM_LIGHT:
		buf_printf(output,
			   "Fuel left: %d%%\n",
			   obj->value[ITEM_LIGHT_DURATION] == -1 ? 100 :
			   obj->value[ITEM_LIGHT_DURATION] == 0 ? 0 :
			   (obj->value[ITEM_LIGHT_DURATION] * 100 / obj->pIndexData->value[2]));
		break;
	case ITEM_SCROLL:
	case ITEM_POTION:
	case ITEM_PILL:
		buf_printf(output, "Level %d spells of:", obj->value[ITEM_POTION_LEVEL]);

		if (obj->value[ITEM_POTION_SPELL1] >= 0)
			buf_printf(output, " '%s'", skill_name(obj->value[ITEM_POTION_SPELL1]));

		if (obj->value[ITEM_POTION_SPELL2] >= 0)
			buf_printf(output, " '%s'", skill_name(obj->value[ITEM_POTION_SPELL2]));

		if (obj->value[ITEM_POTION_SPELL3] >= 0)
			buf_printf(output, " '%s'", skill_name(obj->value[ITEM_POTION_SPELL3]));

		if (obj->value[ITEM_POTION_SPELL4] >= 0)
			buf_printf(output, " '%s'", skill_name(obj->value[ITEM_POTION_SPELL4]));

		buf_add(output, ".\n");
		break;

	case ITEM_WAND:
	case ITEM_STAFF:
		buf_printf(output, "Has %d charges of level %d",
			   obj->value[ITEM_WAND_CHARGES_REMAINING], 
			   obj->value[ITEM_WAND_LEVEL]);

		if (obj->value[ITEM_WAND_SPELL] >= 0)
			buf_printf(output, " '%s'", 
				skill_name(obj->value[ITEM_WAND_SPELL]));

		buf_add(output, ".\n");

		break;

	case ITEM_DRINK_CON:
		buf_printf(output, "It holds %s-colored %s.\n",
			   liq_table[obj->value[ITEM_DRINK_TYPE]].liq_color,
			   liq_table[obj->value[ITEM_DRINK_TYPE]].liq_name);
		break;

	case ITEM_CONTAINER:
		buf_printf(output,
			   "Capacity: %d#  Maximum weight: %d#  flags: %s\n",
			   obj->value[ITEM_CONTAINER_WEIGHT], 
			   obj->value[ITEM_CONTAINER_PER_ITEM_WEIGHT],
			   flag_string(cont_flags, obj->value[ITEM_CONTAINER_FLAGS]));
		if (obj->value[ITEM_CONTAINER_WEIGHT_MULTI] != 100)
			buf_printf(output, "Weight multiplier: %d%%\n",
				   obj->value[ITEM_CONTAINER_WEIGHT_MULTI]);
		break;

	case ITEM_WEAPON:
		buf_printf(output, "Weapon type is %s.\n",
			   flag_string(weapon_class, obj->value[ITEM_WEAPON_TYPE]));

		buf_printf(output, "Damage is %dd%d (average %d).\n",
			   obj->value[ITEM_WEAPON_DICE_NUM], 
			   obj->value[ITEM_WEAPON_DICE_SIZE],
			   (1 + obj->value[ITEM_WEAPON_DICE_SIZE]) 
			   * obj->value[ITEM_WEAPON_DICE_NUM] / 2);

		if (obj->value[ITEM_WEAPON_FLAGS])
			buf_printf(output, "Weapons flags: %s\n",
				   flag_string(weapon_type2, 
				   	obj->value[ITEM_WEAPON_FLAGS]));
		break;

	case ITEM_ARMOR:
		buf_printf(output, "Armor class is %d pierce, "
			   "%d bash, %d slash, and %d vs. magic.\n",
			   obj->value[ITEM_ARMOR_AC_PIERCE], 
			   obj->value[ITEM_ARMOR_AC_BASH],
			   obj->value[ITEM_ARMOR_AC_SLASH], 
			   obj->value[ITEM_ARMOR_AC_EXOTIC]);
		break;
	}
}

void format_obj_affects(BUFFER * output, AFFECT_DATA * paf, int flags)
{
	for (; paf; paf = paf->next) {
		where_t *w;

		if (paf->location != APPLY_NONE && paf->modifier) {
			buf_printf(output, "Affects %s by %d",
				   flag_string(apply_flags, paf->location),
				   paf->modifier);
			if (!IS_SET(flags, FOA_F_NODURATION)
			    && paf->duration > -1)
				buf_printf(output, " for %d hours",
					   paf->duration);
			buf_add(output, ".\n");
		}

		if (IS_SET(flags, FOA_F_NOAFFECTS))
			continue;

		if ((w = where_lookup(paf->where)) && paf->bitvector) {
			buf_add(output, "Adds ");
			buf_printf(output, w->format,
				   flag_string(w->table, paf->bitvector));
			buf_add(output, ".\n");
		}
	}
}


int get_wear_level(CHAR_DATA * ch, OBJ_DATA * obj)
{
	int wear_level = ch->level + 15;
/*	class_t *cl;

	if ((cl = class_lookup(ch->class)) == NULL)
		return wear_level;

	switch (obj->pIndexData->item_type) {
	case ITEM_POTION:
	case ITEM_PILL:
	case ITEM_WAND:
	case ITEM_STAFF:
	case ITEM_SCROLL:
		return wear_level;
	}

	if (!IS_SET(obj->pIndexData->extra_flags, ITEM_QUEST)
	&&  (obj->pIndexData->limit < 0 || obj->pIndexData->limit > 1))
		wear_level += pk_range(wear_level);

	if (IS_SET(cl->flags, CLASS_MAGIC)) {
		if (obj->pIndexData->item_type == ITEM_ARMOR)
			wear_level += 15;
	}
	else if (obj->pIndexData->item_type == ITEM_WEAPON)
		wear_level += 15;  */

	if (IS_SET(obj->pIndexData->extra_flags, ITEM_QUEST)) {
		wear_level = ch->level;
	}

	return wear_level;
}

/*
 * Compute a saving throw.
 * Negative apply's make saving throw better.
 */
bool saves_spell(int level, CHAR_DATA * victim, int dam_type)
{
	class_t *vcl;
	int save;

	if (IS_DELETED(victim))
		return TRUE;

	save = 40 + (victim->level + victim->drain_level - level) * 4 -
	    (victim->saving_throw * 30) / UMAX(45, victim->level);

	if (IS_AFFECTED(victim, AFF_BERSERK))
		save += victim->level / 5;

	switch (check_immune(victim, dam_type)) {
	case IS_IMMUNE:
		return TRUE;
	case IS_RESISTANT:
		save += victim->level / 5;
		break;
	case IS_VULNERABLE:
		save -= victim->level / 5;
		break;
	}

	if (!IS_NPC(victim))
		DEBUG(DEBUG_SAVES,
			"saving throw: %s[%d] against %s[%d] %d%%",
			victim->name, victim->level,
			flag_string(damage_class, dam_type), level, save);

	if (!IS_NPC(victim) && (vcl = class_lookup(victim->class))
	    && IS_SET(vcl->flags, CLASS_MAGIC))
		save = 9 * save / 10;
	save = URANGE(5, save, 98);
	return number_percent() < save;
}

/* RT configuration smashed */

bool saves_dispel(int dis_level, int spell_level, int duration)
{
	int save;

	/* impossible to dispel permanent effects */
	if (duration == -2)
		return 1;
	if (duration == -1)
		spell_level += 5;

	save = 50 + (spell_level - dis_level) * 5;
	save = URANGE(5, save, 95);
	return number_percent() < save;
}

/* co-routine for dispel magic and cancellation */

bool check_dispel(int dis_level, CHAR_DATA * victim, int sn)
{
	AFFECT_DATA *af;

	if (is_affected(victim, sn)) {
		for (af = victim->affected; af != NULL; af = af->next) {
			if (af->type == sn) {
				if (!saves_dispel
				    (dis_level, af->level, af->duration)) {
					skill_t *sk;

					affect_strip(victim, sn);
					if ((sk = skill_lookup(sn))
					    && !IS_NULLSTR(sk->msg_off))
						char_printf(victim, "%s\n",
							    sk->msg_off);
					return TRUE;
				} else
					af->level--;
			}
		}
	}
	return FALSE;
}

bool check_blind_raw(CHAR_DATA * ch)
{
	if (!IS_NPC(ch) && IS_SET(ch->conf_flags, PLR_CONF_HOLYLIGHT))
		return TRUE;

	if (IS_AFFECTED(ch, AFF_BLIND))
		return FALSE;

	return TRUE;
}

bool check_blind(CHAR_DATA * ch)
{
	bool can_see = check_blind_raw(ch);

	if (!can_see)
		char_puts("You can't see a thing!\n", ch);

	return can_see;
}

/*
 * check_trust - check if victim allow ch to cast SPELL_QUESTIONABLE spell
 */
bool check_trust(CHAR_DATA * ch, CHAR_DATA * victim)
{
	if (ch == victim)
		return TRUE;

	if (IS_NPC(victim))
		return is_same_group(ch, victim);

	if (IS_SET(victim->pcdata->trust, TRUST_ALL))
		return TRUE;

	return (IS_SET(victim->pcdata->trust, TRUST_GROUP) &&
		is_same_group(ch, victim))
	    || (ch->clan && IS_SET(victim->pcdata->trust, TRUST_CLAN) &&
		ch->clan == victim->clan)
	    || (IS_GOOD(ch) && IS_SET(victim->pcdata->trust, TRUST_GOOD))
	    || (IS_NEUTRAL(ch) && IS_SET(victim->pcdata->trust, TRUST_NEUTRAL))
	    || (IS_EVIL(ch) && IS_SET(victim->pcdata->trust, TRUST_EVIL));
}

/*----------------------------------------------------------------------------
 * show affects stuff
 */

void show_name(CHAR_DATA * ch, BUFFER * output,
	       AFFECT_DATA * paf, AFFECT_DATA * paf_last)
{
	if (paf_last && paf->type == paf_last->type)
		if (ch && ch->level < 20)
			return;
		else
			buf_add(output, "                      ");
	else
		buf_printf(output, "Spell: {c%-15s{x", skill_name(paf->type));
}

void show_duration(BUFFER * output, AFFECT_DATA * paf)
{
	if (paf->duration < 0)
		buf_add(output, "permanently.");
	else
		buf_printf(output, "for {c%d{x hours.", paf->duration);
	buf_add(output, "\n");
}

void show_loc_affect(CHAR_DATA * ch, BUFFER * output,
		     AFFECT_DATA * paf, AFFECT_DATA ** ppaf)
{
	if (ch->level < 20) {
		show_name(ch, output, paf, *ppaf);
		if (*ppaf && (*ppaf)->type == paf->type)
			return;
		buf_add(output, "\n");
		*ppaf = paf;
		return;
	}

	if (paf->location > 0) {
		show_name(ch, output, paf, *ppaf);
		buf_printf(output, ": modifies {c%s{x by {c%d{x ",
			   flag_string(apply_flags, paf->location),
			   paf->modifier);
		show_duration(output, paf);
		*ppaf = paf;
	}
}

void show_bit_affect(BUFFER * output, AFFECT_DATA * paf, AFFECT_DATA ** ppaf,
		     flag32_t where)
{
	char buf[MAX_STRING_LENGTH];
	where_t *w = NULL;

	if (IS_SET(paf->bitvector, AFF_NO_DISPLAY))
		return;

	if (paf->where != where
	    || (w = where_lookup(paf->where)) == NULL
	    || (paf->modifier != 0 &&
		paf->location != 0 && paf->bitvector == 0))
		return;

	show_name(NULL, output, paf, *ppaf);
	snprintf(buf, sizeof(buf), ": adds %s ", w->format);
	buf_printf(output, buf, (paf->bitvector)
		   ? flag_string(w->table, paf->bitvector)
		   : "special");
	show_duration(output, paf);
	*ppaf = paf;
}

void show_obj_affects(CHAR_DATA * ch, BUFFER * output, AFFECT_DATA * paf)
{
	AFFECT_DATA *paf_last = NULL;

	for (; paf; paf = paf->next)
		if (paf->location != APPLY_SPELL_AFFECT)
			if (paf->bitvector)
				show_bit_affect(output, paf, &paf_last,
						TO_AFFECTS);
}

void show_affects(CHAR_DATA * ch, BUFFER * output)
{
	OBJ_DATA *obj;
	AFFECT_DATA *paf, *paf_last = NULL;

	buf_add(output, "You are affected by the following spells:\n");
	for (paf = ch->affected; paf; paf = paf->next) {
		show_loc_affect(ch, output, paf, &paf_last);
		if (ch->level < 20)
			continue;
		show_bit_affect(output, paf, &paf_last, TO_AFFECTS);
		show_bit_affect(output, paf, &paf_last, TO_RESIST);
		show_bit_affect(output, paf, &paf_last, TO_IMMUNE);
	}

	if (ch->level < 20)
		return;

	for (obj = ch->carrying; obj; obj = obj->next_content)
		if (obj->wear_loc != WEAR_NONE) {
			if (!IS_SET(obj->extra_flags, ITEM_ENCHANTED))
				show_obj_affects(ch, output,
						 obj->pIndexData->affected);
			show_obj_affects(ch, output, obj->affected);
		}
}

/*
 * Parse a name for acceptability.
 */
bool pc_name_ok(const char *name)
{
	const char *pc;
	bool fIll, adjcaps = FALSE, cleancaps = FALSE;
	int total_caps = 0;
	int i;

	/*
	 * Reserved words.
	 */
	if (is_name(name, "chronos all auto immortals self someone something"
		    "the you demise balance circle loner honor " "none clan"))
		return FALSE;

	/*
	 * Length restrictions.
	 */

	if (strlen(name) < 2)
		return FALSE;

	if (strlen(name) > MAX_CHAR_NAME)
		return FALSE;

	/*
	 * Alphanumerics only.
	 * Lock out IllIll twits.
	 */
	fIll = TRUE;
	for (pc = name; *pc != '\0'; pc++) {
		if (IS_SET(mud_options, OPT_ASCII_ONLY_NAMES) && !isascii(*pc))
			return FALSE;

		if (!isalpha(*pc))
			return FALSE;

		if (isupper(*pc)) {	/* ugly anti-caps hack */
			if (adjcaps)
				cleancaps = TRUE;
			total_caps++;
			adjcaps = TRUE;
		} else
			adjcaps = FALSE;

		if (LOWER(*pc) != 'i' && LOWER(*pc) != 'l')
			fIll = FALSE;
	}

	if (fIll)
		return FALSE;

	if (total_caps > strlen(name) / 2)
		return FALSE;

	/*
	 * Prevent players from naming themselves after mobs.
	 */
	{
		MOB_INDEX_DATA *pMobIndex;
		int iHash;

		for (iHash = 0; iHash < MAX_KEY_HASH; iHash++) {
			for (pMobIndex = mob_index_hash[iHash];
			     pMobIndex != NULL; pMobIndex = pMobIndex->next)
				if (is_name(name, pMobIndex->name))
					return FALSE;
		}
	}

	for (i = 0; i < clans.nused; i++) {
		class_t *clan = VARR_GET(&clans, i);
		if (!str_cmp(name, clan->name))
			return FALSE;
	}

	return TRUE;
}

/*
 * check to see if a character has access to this
 * class skill
 *
 */
bool cskill_restricted_ok(CHAR_DATA * ch, cskill_t * skill)
{
	flag32_t ch_sex = (ch->sex == SEX_MALE) ? RS_MALE :
	    (ch->sex == SEX_FEMALE) ? RS_FEMALE : RS_NEUTER;
	flag32_t ch_align = RALIGN(ch);

	return (ch_sex & skill->restrict_sex
		&& ch_align & skill->restrict_align
		&& (!CHAR_CREATE_ETHOS || ch->ethos & skill->restrict_ethos));
}

#define MAX_STAT_ALIASES 6
const char *stat_aliases[MAX_STATS][MAX_STAT_ALIASES] = {
	{ /* STR */
	"Weak",
	"Poor", 
	"Average", 
	"Strong", 
	"Herculian", 
	"Titanic" 
	},
	{ /* INT */
	"Hopeless",
	"Poor", 
	"Average", 
	"Good", 
	"Clever", 
	"Genius" 
	},
	{ /* WIS */
	"Fool",
	"Dim", 
	"Average", 
	"Good", 
	"Wise", 
	"Enlightened" 
	},
	{ /* DEX */
	"Slow",
	"Clumsy", 
	"Average", 
	"Dextrous", 
	"Quick", 
	"Fast"
	},
	{ /* CON */
	"Fragile",
	"Poor", 
	"Average", 
	"Healthy", 
	"Hearty", 
	"Iron"
	},
	{ /* CHR */
	"Mongol",
	"Poor", 
	"Average", 
	"Good", 
	"Familier", 
	"Charismatic"
	},
	{ /* LCK */
	"Irish",
	"Poor",
	"Average",
	"Good",
	"Charmed",
	"Blessed"
	}
};

const char *get_stat_alias(CHAR_DATA * ch, int stat)
{
	int val;
	int delt;
	int interval = STAT_BASE / MAX_STAT_ALIASES;

	if (stat >= MAX_STATS)
		return "Unknown";

	val = get_curr_stat(ch, stat);

	if (IS_NPC(ch)) {
		if ((val / interval) >= MAX_STAT_ALIASES)
			return "Godly";
		else
			return stat_aliases[stat][val/interval];
	}

	interval = 2*STAT_RANDOM_VARIANCE / MAX_STAT_ALIASES;
	delt = (val - ((RACE(ch->race)->pcdata->stats[stat]) - STAT_RANDOM_VARIANCE)) - interval/ 2;

	if ((delt / interval) < 0) 
		return "Terrible";

	if ((delt / interval) >= MAX_STAT_ALIASES)
		return "Godly";

	return stat_aliases[stat][delt/interval];
}

/*****************************************************************************
 * some formatting stuff
 *
 */

/*
 * smash '~'
 */
const char *fix_short(const char *s)
{
	char *p;
	static char buf[MAX_STRING_LENGTH];

	if (!strchr(s, '~'))
		return s;

	for (p = buf; *s && p - buf < sizeof(buf) - 1; s++) {
		if (*s == '~')
			continue;
		*p++ = *s;
	}

	*p = '\0';
	return buf;
}

const char *format_short(mlstring * mlshort, const char *name,
			 CHAR_DATA * looker)
{
	static char buf[MAX_STRING_LENGTH];
	const char *sshort;

	sshort = fix_short(mlstr_cval(mlshort, looker));
	strnzcpy(buf, sizeof(buf), sshort);

	if (looker && !IS_SET(looker->comm, COMM_NOENG)
	    && sshort != mlstr_mval(mlshort)) {
		char buf2[MAX_STRING_LENGTH];
		char buf3[MAX_STRING_LENGTH];

		one_argument(name, buf3, sizeof(buf3));
		snprintf(buf2, sizeof(buf2), " (%s)", buf3);
		strnzcat(buf, sizeof(buf), buf2);
	}

	return buf;
}

/*
 * format description (long descr for mobs, description for objs)
 *
 * eng name expected to be in form " (foo)" and is stripped
 * if COMM_NOENG is set
 */
const char *format_descr(mlstring * ml, CHAR_DATA * looker)
{
	const char *s;
	const char *p, *q;
	static char buf[MAX_STRING_LENGTH];

	s = mlstr_cval(ml, looker);
	if (IS_NULLSTR(s)
	    || !IS_SET(looker->comm, COMM_NOENG)
	    || (p = strchr(s, '(')) == NULL || (q = strchr(p + 1, ')')) == NULL)
		return s;

	if (p != s && *(p - 1) == ' ')
		p--;

	strnzncpy(buf, sizeof(buf), s, p - s);
	strnzcat(buf, sizeof(buf), q + 1);
	return buf;
}

#if defined (WIN32)
void SET_ORG_RACE(CHAR_DATA * ch, int race)
{
	if (IS_NPC(ch))
		ch->pIndexData->race = race;
	else
		ch->pcdata->race = race;
}
#elif defined (LINUX)
void SET_ORG_RACE(CHAR_DATA * ch, int race)
{
	if (IS_NPC(ch))
		ch->pIndexData->race = race;
	else
		ch->pcdata->race = race;
}
#endif

/* 
 * extracts all objects of a certain type from a character,
 * whether in inventory or in a container.
 *
 * returns the number of objects extracted
 */
int strip_obj_from_char(CHAR_DATA * ch, CHAR_DATA * victim,
			OBJ_INDEX_DATA * pObj)
{
	int count = 0;

	count += strip_from_container(victim, victim->carrying, pObj);

	return count;
}

int strip_from_container(CHAR_DATA * victim, OBJ_DATA * first_item,
			 OBJ_INDEX_DATA * pObj)
{
	OBJ_DATA *obj, *obj_next;
	int count = 0;

	for (obj = first_item; obj; obj = obj_next) {
		obj_next = obj->next_content;
		if (obj->pIndexData->item_type == ITEM_CONTAINER)
			count +=
			    strip_from_container(victim, obj->contains, pObj);
		if (obj->pIndexData->vnum == pObj->vnum) {
			DEBUG(DEBUG_OBJ_STRIP,
				"stripped: %s lost [%d] %s",
				victim->name,
				obj->pIndexData->vnum,
				obj->pIndexData->name);
			if (obj->wear_loc != WEAR_NONE)
				remove_obj(victim, obj->wear_loc, TRUE);
			extract_obj(obj, 0);
			count++;
		}
	}
	return count;
}

int world_alignment()
{
	CHAR_DATA *wch;
	int align = 0;
	int count = 0;

	for (wch = char_list; wch && !IS_NPC(wch); wch = wch->next) {
		if (wch->desc 
		&& wch->desc->connected == CON_PLAYING
		&& !IS_IMMORTAL(wch)) {
			count++;
			align += wch->alignment;
		}
	}

	if (count == 0)
		return 0;

	return align / count;
}

/*
 * sets the average damage for the item
 * in the way of dice and size, as is appropriate
 * for the particular weapon.
 *
 * percent_damage - the percent of normal damage
 * 		that this object has.
 * 		100% is obviously default.
 *
 * returns the avg damage
 */
int autoweapon(OBJ_DATA * obj, int percent_damage)
{
	int dice = 0, size = 0, avg = 0;
	int saved_level = obj->pIndexData->level;

	if (percent_damage <= 0) {
		DEBUG(DEBUG_BUILD_AUTO,
			"autoweapon: requested %d%% damage. Autoing to 100%",
			percent_damage);
		percent_damage = 100;
	}

	obj->pIndexData->level = obj->level;
	avg = get_autoweapon(obj->pIndexData, &dice, &size, percent_damage);
	obj->pIndexData->level = saved_level;

	obj->value[ITEM_WEAPON_DICE_NUM] = dice;
	obj->value[ITEM_WEAPON_DICE_SIZE] = size;

	return avg;
}

bool is_aggressive_to(CHAR_DATA * ch, CHAR_DATA * victim)
{
	if (!IS_NPC(ch))
		return FALSE;

	if (!IS_SET(ch->pIndexData->act, ACT_AGGRESSIVE)
	    && !IS_SET(ch->pIndexData->act, ACT_AGGRO_RELATIVE))
		return FALSE;

	if (!victim)
		return TRUE;

	if (IS_NPC(victim))
		return FALSE;

	if (IS_IMMORTAL(victim))
		return FALSE;

	if (IS_SET(ch->pIndexData->act, ACT_AGGRO_RELATIVE)
	    && (ch->level >= (victim->level + pk_range(ch->level)) ||
		ch->level <= (victim->level - pk_range(ch->level)))
	    && (victim->level >= (ch->level + pk_range(victim->level)) ||
		victim->level <= (ch->level - pk_range(victim->level))))
		return TRUE;

	if (ch->level >= victim->level - 5)
		return FALSE;

	return TRUE;
}

void add_past_owner(OBJ_DATA * obj, CHAR_DATA * ch)
{
	int i;

	if (!ch->desc)
		return;

	if (!str_cmp(ch->name, obj->past_owner[0]))
		return;

	if (obj->past_owner[PAST_OWNER_MAX - 1]) {
		free_string(obj->past_owner[PAST_OWNER_MAX - 1]);
		obj->past_owner[PAST_OWNER_MAX - 1] = NULL;
	}
	if (obj->past_owner_ip[PAST_OWNER_MAX - 1]) {
		free_string(obj->past_owner_ip[PAST_OWNER_MAX - 1]);
		obj->past_owner_ip[PAST_OWNER_MAX - 1] = NULL;
	}

	for (i = PAST_OWNER_MAX - 1; i > 0; i--) {
		obj->past_owner[i] = obj->past_owner[i - 1];
		obj->past_owner_ip[i] = obj->past_owner_ip[i - 1];
	}

	obj->past_owner[0] = str_printf("%s", ch->name);
	obj->past_owner_ip[0] = str_printf("%s", 
			(ch->pcdata->fake_ip) 
				? ch->pcdata->fake_ip 
				: ch->desc->host);
}

/*
 * check to see if one of the previous owners was
 * an alt of the player.  Obviously, since IPs aren't necessarily
 * unique, this is just a suggestion of wrong doing.
 *
 * function reports which past owner.
 */
int past_owner_alt(OBJ_DATA * obj, CHAR_DATA * ch)
{
	int i = -1;

	for (i = 0; i < PAST_OWNER_MAX; i++) {
		if (!IS_NULLSTR(obj->past_owner[i])
		    && !str_cmp(
			(ch->pcdata->fake_ip) 
				? ch->pcdata->fake_ip 
				: ch->desc->host, obj->past_owner_ip[i])
		    && str_cmp(ch->name, obj->past_owner[i]))
			return i;
	}

	return -1;
}

int count_limiteds(CHAR_DATA * ch)
{
	OBJ_DATA *obj;
	int limiteds = 0;

	for (obj = ch->carrying; obj != NULL; obj = obj->next_content)
		if (IS_OBJ_LIMITED(obj->pIndexData))
			limiteds++;

	return limiteds;
}


bool align_standing_important(CHAR_DATA * ch)
{
	if (IS_NPC(ch))
		return FALSE;

	switch (ch->class) {
	case CLASS_PALADIN:
	case CLASS_CLERIC:
		return TRUE;
	}

	return FALSE;
}

/*
 * for neutrals to know if they're being too good, or evil
 */
int align_standing_leaning(CHAR_DATA * ch)
{
	int good;
	int evil;

	if (IS_NPC(ch))
		return 0;

	good = ch->pcdata->align_standing[ALIGN_INDEX_GOOD];
	evil = ch->pcdata->align_standing[ALIGN_INDEX_EVIL];

	if (good > (evil * 120 / 100))
		return 1;

	if (evil > (good * 120 / 100))
		return -1;

	return 0;
}

/**
 * return how true they are to their alignment
 */
int align_standing_raw(CHAR_DATA * ch)
{
	int sum = 0;
	int tmp = 0;
	int good = 0;
	int neut = 0;
	int evil = 0;

	if (IS_NPC(ch))
		return 0;

	good = ch->pcdata->align_standing[ALIGN_INDEX_GOOD];
	neut = ch->pcdata->align_standing[ALIGN_INDEX_NEUTRAL];
	evil = ch->pcdata->align_standing[ALIGN_INDEX_EVIL];

	if (IS_GOOD(ch)) {
		sum = good;
		sum -= neut / 3;
		sum -= evil * 2;
	} else if (IS_EVIL(ch)) {
		sum = evil;
		sum += neut / 4;
		sum -= good * 2;
	} else {
		sum = neut;
		sum += (good + evil);
		if (good > (evil * 120 / 100)
		    || evil > (good * 120 / 100))
			tmp = (good - evil) * 2;
		tmp = ABS(tmp);
		sum -= tmp;
	}

	return sum;
}

int align_standing(CHAR_DATA * ch)
{
	int index = 0;
	int standing = align_standing_raw(ch);

	if (standing > ALIGN_STANDING_UNIT * 5000)
		index = ALIGN_STANDING_G8;
	else if (standing > ALIGN_STANDING_UNIT * 2000)
		index = ALIGN_STANDING_G7;
	else if (standing > ALIGN_STANDING_UNIT * 1000)
		index = ALIGN_STANDING_G6;
	else if (standing > ALIGN_STANDING_UNIT * 500)
		index = ALIGN_STANDING_G5;
	else if (standing > ALIGN_STANDING_UNIT * 250)
		index = ALIGN_STANDING_G4;
	else if (standing > ALIGN_STANDING_UNIT * 100)
		index = ALIGN_STANDING_G3;
	else if (standing > ALIGN_STANDING_UNIT * 50)
		index = ALIGN_STANDING_G2;
	else if (standing > ALIGN_STANDING_UNIT * 10)
		index = ALIGN_STANDING_G1;

	else if (standing < ALIGN_STANDING_UNIT * -5)
		index = ALIGN_STANDING_B1;
	else if (standing < ALIGN_STANDING_UNIT * -25)
		index = ALIGN_STANDING_B2;
	else if (standing < ALIGN_STANDING_UNIT * -50)
		index = ALIGN_STANDING_B3;
	else if (standing < ALIGN_STANDING_UNIT * -100)
		index = ALIGN_STANDING_B4;
	else if (standing < ALIGN_STANDING_UNIT * -250)
		index = ALIGN_STANDING_B5;
	else if (standing < ALIGN_STANDING_UNIT * -500)
		index = ALIGN_STANDING_B6;
	else if (standing < ALIGN_STANDING_UNIT * -1000)
		index = ALIGN_STANDING_B7;
	else
		index = ALIGN_STANDING_0;

	DEBUG(DEBUG_ALIGN_STANDING,
		"align_standing: %s %d: %d/%d/%d = %d",
		ch->name, standing,
		ch->pcdata->align_standing[ALIGN_INDEX_GOOD],
		ch->pcdata->align_standing[ALIGN_INDEX_NEUTRAL],
		ch->pcdata->align_standing[ALIGN_INDEX_EVIL], index);

	return index;
}

/**
 * converts alignment standing to a bonus
 * or negative.
 */
int align_standing_bonus(CHAR_DATA * ch)
{
	int bonus = 0;
	int standing = align_standing(ch);

	switch (standing) {

	case ALIGN_STANDING_G8:
		bonus = 6;
		break;
	case ALIGN_STANDING_G7:
		bonus = 5;
		break;
	case ALIGN_STANDING_G6:
		bonus = 4;
		break;
	case ALIGN_STANDING_G5:
		bonus = 3;
		break;
	case ALIGN_STANDING_G4:
		bonus = 2;
		break;
	case ALIGN_STANDING_G3:
		bonus = 1;
		break;

	case ALIGN_STANDING_G2:
	case ALIGN_STANDING_G1:
		bonus = 0;
		break;

	case ALIGN_STANDING_0:
	case ALIGN_STANDING_B1:
		bonus = -1;
		break;
	case ALIGN_STANDING_B2:
		bonus = -2;
		break;
	case ALIGN_STANDING_B3:
		bonus = -3;
		break;
	case ALIGN_STANDING_B4:
		bonus = -4;
		break;
	case ALIGN_STANDING_B5:
		bonus = -5;
		break;
	case ALIGN_STANDING_B6:
		bonus = -6;
		break;
	case ALIGN_STANDING_B7:
		bonus = -7;
		break;

	default:
		bonus = 0;
		break;
	}

	return bonus;
}

int standing_update(CHAR_DATA * ch)
{
	int dec = ALIGN_STANDING_UNIT / 10;

	if (ch->level >= HERO)
		dec = dec * 50 / 100;

	if (IS_NPC(ch))
		return 0;

	if (IS_GOOD(ch)) {
		ch->pcdata->align_standing[ALIGN_INDEX_GOOD] -= dec;
		ch->pcdata->align_standing[ALIGN_INDEX_NEUTRAL] -= dec;
		ch->pcdata->align_standing[ALIGN_INDEX_EVIL] -= (dec / 5 + 1);
	} else if (IS_EVIL(ch)) {
		ch->pcdata->align_standing[ALIGN_INDEX_EVIL] -= dec;
		ch->pcdata->align_standing[ALIGN_INDEX_NEUTRAL] -= dec;
		ch->pcdata->align_standing[ALIGN_INDEX_GOOD] -= (dec / 5 + 1);
	} else {
		ch->pcdata->align_standing[ALIGN_INDEX_GOOD] -= dec;
		ch->pcdata->align_standing[ALIGN_INDEX_EVIL] -= dec;
	}

	/* no lower than zero */
	if (ch->pcdata->align_standing[ALIGN_INDEX_GOOD] < 0)
		ch->pcdata->align_standing[ALIGN_INDEX_GOOD] = 0;
	if (ch->pcdata->align_standing[ALIGN_INDEX_EVIL] < 0)
		ch->pcdata->align_standing[ALIGN_INDEX_EVIL] = 0;

	/* neuts can do anti-neut deeds */
	if (IS_NEUTRAL(ch)) {
		/* make neut standing tend toward zero */
		if (ch->pcdata->align_standing[ALIGN_INDEX_NEUTRAL] > 0)
			ch->pcdata->align_standing[ALIGN_INDEX_NEUTRAL] -= dec;
		else {
			ch->pcdata->align_standing[ALIGN_INDEX_NEUTRAL] +=
			    dec / 5;
			if (ch->pcdata->align_standing[ALIGN_INDEX_NEUTRAL] > 0)
				ch->pcdata->
				    align_standing[ALIGN_INDEX_NEUTRAL] = 0;
		}
	} else {
		if (ch->pcdata->align_standing[ALIGN_INDEX_NEUTRAL] < 0)
			ch->pcdata->align_standing[ALIGN_INDEX_NEUTRAL] = 0;
	}


	return 0;
}

int align_standing_death_gain(CHAR_DATA * ch, CHAR_DATA * victim)
{
	if (!IS_NPC(ch)) {
		if (IS_GOOD(victim) && victim->level > (ch->level - 5))
			ch->pcdata->align_standing[ALIGN_INDEX_EVIL] +=
			    (IS_NPC(victim)) ? ALIGN_STANDING_UNIT :
			    ALIGN_STANDING_UNIT * 10;

		if (IS_NEUTRAL(victim)) {
			if (IS_NEUTRAL(ch)) {
				ch->pcdata->
				    align_standing[ALIGN_INDEX_NEUTRAL] +=
				    (IS_NPC(victim)) ? ALIGN_STANDING_UNIT *
				    -1 : ALIGN_STANDING_UNIT * -1;
			} else if (IS_GOOD(ch)
				   && victim->level > (ch->level - 5)) {
				ch->pcdata->align_standing[ALIGN_INDEX_GOOD] +=
				    (IS_NPC(victim)) ? ALIGN_STANDING_UNIT /
				    5 : ALIGN_STANDING_UNIT * 3;
			} else if (victim->level > (ch->level - 5)) {
				ch->pcdata->align_standing[ALIGN_INDEX_EVIL] +=
				    (IS_NPC(victim)) ? ALIGN_STANDING_UNIT /
				    2 : ALIGN_STANDING_UNIT * 5;
			}
		}

		if (IS_EVIL(victim) && victim->level > (ch->level - 5)) {
			ch->pcdata->align_standing[ALIGN_INDEX_GOOD] +=
			    (IS_NPC(victim)) ? ALIGN_STANDING_UNIT :
			    ALIGN_STANDING_UNIT * 10;
		}
	}

	return 1;
}


/*
 * removes all types of hide affects
 */
bool remove_hide_affect(CHAR_DATA * ch, flag64_t aff_hide)
{
	AFFECT_DATA *paf = NULL;
	bool unhide = FALSE;

	if (IS_SET(aff_hide, AFF_HIDE)) {
		REMOVE_BIT(ch->affected_by, AFF_HIDE);
		if ((paf = affect_find(ch->affected, gsn_hide)) != NULL)
			affect_remove(ch, paf);
		unhide = TRUE;
	}

	if (IS_SET(aff_hide, AFF_FADE)) {
		REMOVE_BIT(ch->affected_by, AFF_FADE);
		if ((paf = affect_find(ch->affected, gsn_fade)) != NULL)
			affect_remove(ch, paf);
		unhide = TRUE;
	}

	if (IS_SET(aff_hide, AFF_CAMOUFLAGE)) {
		REMOVE_BIT(ch->affected_by, AFF_CAMOUFLAGE);
		if ((paf = affect_find(ch->affected, gsn_camouflage)) != NULL)
			affect_remove(ch, paf);
		unhide = TRUE;
	}

	return unhide;
}

bool add_pk_ok (CHAR_DATA *ch, CHAR_DATA *victim)
{
	if (IS_NPC(ch) || IS_NPC(victim)) return FALSE;
	return name_add(&ch->pcdata->pk_ok_list, victim->name, NULL, "PKOKlist");
}

bool delete_pk_ok (CHAR_DATA *ch, CHAR_DATA *victim)
{
	if (IS_NPC(ch) || IS_NPC(victim)) return FALSE;
	return name_delete(&ch->pcdata->pk_ok_list, victim->name, NULL, "PKOKlist");
}

/*
 * count the objects in a container.
 * if recursive, count the object in containers in the contain, etc
 */
int obj_in_context_count (OBJ_DATA *context, bool recursive)
{
	int count = 0;
	OBJ_DATA *obj;

	for (obj = context; obj; obj = obj->next_content) {
		count++;
		if (recursive && obj->pIndexData->item_type == ITEM_CONTAINER)
			count += obj_in_context_count(obj->contains, TRUE);
	}

	return count;
}

CHAR_DATA *summon_shop_guard (CHAR_DATA *ch, CHAR_DATA *victim)
{
	CHAR_DATA *guard;
	int i;
	guard = create_mob(get_mob_index(MOB_VNUM_SHOP_GUARD));

	for (i = 0; i < MAX_STATS; i++) {
		guard->perm_stat[i] = victim->perm_stat[i] - 1;
	}
	guard->perm_stat[STAT_STR] = 80;

	guard->max_hit = UMIN(30000, 2 * victim->max_hit);
	guard->hit = victim->max_hit;
	guard->max_mana = victim->max_mana;
	guard->hitroll = victim->hitroll;
	guard->damroll = victim->damroll;

	guard->level = victim->level;
	guard->damage[DICE_NUMBER] =
		UMAX(2, number_range(guard->level / 20, guard->level / 16));
	guard->damage[DICE_TYPE] =
		UMAX(2, number_range(guard->level / 4, guard->level / 3));
	guard->damage[DICE_BONUS] =
		UMAX(2, number_range(guard->level / 12, guard->level / 10));

	for (i = 0; i < 3; i++)
		guard->armor[i] = interpolate(guard->level, 100, -100);

	guard->armor[3] = interpolate(guard->level, 100, 0);
	guard->gold = number_range(0, 2);
	guard->target = victim;
	char_to_room(guard, victim->in_room);
	act("$n arrives, heeding the merchant's call.",
		guard, NULL, NULL, TO_ALL);

	return guard;
}

/*
 * Check if ch has a given item or item type
 * vnum: item vnum or -1
 * item_type: item type or -1
 * fWear: TRUE: item must be worn, FALSE: don't care
 */
OBJ_DATA * has_item(CHAR_DATA * ch, int vnum, int item_type, bool fWear)
{
	OBJ_DATA *obj;
	for (obj = ch->carrying; obj; obj = obj->next_content)
		if ((vnum < 0 || obj->pIndexData->vnum == vnum)
		    && (item_type < 0
			|| obj->pIndexData->item_type == item_type)
		    && (!fWear || obj->wear_loc != WEAR_NONE))
			return obj;
	return NULL;
}

bool extract_obj_type (CHAR_DATA *ch, int vnum, int item_type)
{
	OBJ_DATA *obj;
	for (obj = ch->carrying; obj; obj = obj->next_content)
		if ((vnum < 0 || obj->pIndexData->vnum == vnum)
		    && (item_type < 0
			|| obj->pIndexData->item_type == item_type)) {
		    	obj_from_char(obj);
		    	extract_obj(obj, 0);
			return TRUE;
		}
	return FALSE;
}

int get_ac (CHAR_DATA *ch, int type) 
{
	int ac = 0;
	ac = ch->armor[type];

	if (!IS_AWAKE(ch))
		ac = ac / 3;

	return ac;
}