empiremud/cnf/
empiremud/doc/
empiremud/lib/boards/
empiremud/lib/etc/
empiremud/lib/misc/
empiremud/lib/plralias/F-J/
empiremud/lib/plralias/K-O/
empiremud/lib/plralias/P-T/
empiremud/lib/plralias/U-Z/
empiremud/lib/plrobjs/
empiremud/lib/plrobjs/F-J/
empiremud/lib/plrobjs/K-O/
empiremud/lib/plrobjs/P-T/
empiremud/lib/plrobjs/U-Z/
empiremud/lib/world/
empiremud/lib/world/mob/
empiremud/lib/world/obj/
empiremud/log/
/* ************************************************************************
*   File: handler.c                                      EmpireMUD AD 1.0 *
*  Usage: internal funcs: moving and finding chars/objs                   *
*                                                                         *
*  All rights reserved.  See license.doc for complete information.        *
*                                                                         *
*  Code base by Paul Clarke.  EmpireMUD Project, a tbgMUD Production.     *
*  Based upon CircleMUD 3.0, beta patch level 17, by Jeremy Elson.        *
*                                                                         *
*  Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
*  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
************************************************************************ */

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


#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "db.h"
#include "handler.h"
#include "interpreter.h"
#include "empire.h"
#include "skills.h"

/* external vars */
extern Creature combat_list;
extern const char *MENU;


/* external functions */
void remove_follower(Creature ch);
void die_follower(Creature ch);
ACMD(do_return);


/* Returns the maximum an attribute may be for a given ch (or void, for normal max) */
int att_max(Creature ch) {
	if (ch && !IS_NPC(ch)) {
		/* morph accounts for werewolves */
		if (GET_MORPH(ch) != MORPH_NONE)
			return 10;
		if (IS_VAMPIRE(ch))
			return (GET_GENERATION(ch) < 8 ? (5 + 8 - GET_GENERATION(ch)) : 5);
		return 5;
		}

	return 5;
	}


/* Returns the maximum a discipline may be */
int disc_max(Creature ch) {
	if (IS_NPC(ch) || IS_HUMAN(ch))
		return 0;
	if (IS_VAMPIRE(ch))
		return att_max(ch);

	if (IS_GHOUL(ch))
		switch (GET_GENERATION(ch)) {
			case 7:		return 2;
			case 6:		return 3;
			case 5:		return 4;
			case 4:		return 5;
			case 3:		return 6;
			case 2:		return 7;
			case 1:		return 8;
			default:	return 1;
			}

	return 0;
	}


void gain_experience(Creature ch, int amt) {
	if (IS_NPC(ch))
		return;

	/* Experience may be 0 to 250 */
	GET_EXPERIENCE(ch) = MAX(0, MIN(EXP_CAP, GET_EXPERIENCE(ch) + amt));
	}


char *fname(const char *namelist) {
	static char holder[30];
	register char *point;

	for (point = holder; isalpha(*namelist); namelist++, point++)
		*point = *namelist;

	*point = '\0';

	return (holder);
	}


#define WHITESPACE " \t"

int isname(char *str, char *namelist) {
	char *newlist;
	char *curtok;

	newlist = strdup(namelist);

	for (curtok = strtok(newlist, WHITESPACE); curtok; curtok = strtok(NULL, WHITESPACE))
		if (curtok && is_abbrev(str, curtok)) {
			free(newlist); 
			return 1;
			}

	free(newlist);
	return 0;
	}


int iname(char *str, char *namelist) {
	char *newlist;
	char *curtok;

	newlist = strdup(namelist);

	for (curtok = strtok(newlist, WHITESPACE); curtok; curtok = strtok(NULL, WHITESPACE))
		if (curtok && !str_cmp(str, curtok)) {
			free(newlist); 
			return 1;
			}

	free(newlist);
	return 0;
	}




void affect_modify(Creature ch, byte loc, sh_int mod, bitvector_t bitv, bitvector_t disc_bit, bool add) {
	if (add) {
		SET_BIT(AFF_FLAGS(ch), bitv);
		SET_BIT(DSC_FLAGS(ch), disc_bit);
		}
	else {
		REMOVE_BIT(AFF_FLAGS(ch), bitv);
		REMOVE_BIT(DSC_FLAGS(ch), disc_bit);
		mod = -mod;
		}

	switch (loc) {
		case APPLY_NONE:
			break;

		case APPLY_STRENGTH:
			GET_STRENGTH(ch) += mod;
			break;
		case APPLY_DEXTERITY:
			GET_DEXTERITY(ch) += mod;
			break;
		case APPLY_STAMINA:
			GET_STAMINA(ch) += mod;
			break;

		case APPLY_CHARISMA:
			GET_CHARISMA(ch) += mod;
			break;
		case APPLY_MANIPULATION:
			GET_MANIPULATION(ch) += mod;
			break;
		case APPLY_APPEARANCE:
			/* Clan Weakness: Nosferatu */
			if (GET_CLAN(ch) != CLAN_NOSFERATU)
				GET_APPEARANCE(ch) += mod;
			break;

		case APPLY_PERCEPTION:
			GET_PERCEPTION(ch) += mod;
			break;
		case APPLY_INTELLIGENCE:
			GET_INTELLIGENCE(ch) += mod;
			break;
		case APPLY_WITS:
			GET_WITS(ch) += mod;
			break;

		case APPLY_AGE:
			ch->player.time.birth -= (mod * SECS_PER_MUD_YEAR);
			break;

		case APPLY_MOVE:
			GET_MAX_MOVE(ch) += mod;
			GET_MOVE(ch) += mod;
			break;

		case APPLY_SOAK:
			GET_SOAK(ch) += mod;
			break;

		case APPLY_BLOCK:
			GET_BLOCK(ch) += mod;
			break;

		case APPLY_ATTACKS:
			ATTACK_BONUS(ch) += mod;
			break;

		case APPLY_FORTITUDE:
			GET_REAL_DISC(ch, DISC_FORTITUDE) += mod;
			break;

		default:
			log("SYSERR: Unknown apply adjust %d attempt (%s, affect_modify).", loc, __FILE__);
			break;

		} /* switch */
	}


/* This updates a character by subtracting everything he is affected by */
/* restoring original abilities, and then affecting all again           */
void affect_total(Creature ch) {
	void update_morph_stats(Creature ch, bool add);

	struct affected_type *af;
	int i, j;

	for (i = 0; i < NUM_WEARS; i++) {
		if (GET_EQ(ch, i) && i != WEAR_IN_SHEATH_1 && i != WEAR_IN_SHEATH_2 && i != WEAR_IN_SHEATH_3)
			for (j = 0; j < MAX_OBJ_AFFECT; j++)
				affect_modify(ch, GET_EQ(ch, i)->affected[j].location, GET_EQ(ch, i)->affected[j].modifier, GET_EQ(ch, i)->obj_flags.bitvector, 0, FALSE);
		}

	update_morph_stats(ch, FALSE);

	for (af = ch->affected; af; af = af->next)
		affect_modify(ch, af->location, af->modifier, af->bitvector, af->disc_bit, FALSE);

	ch->aff_abils = ch->real_abils;
	GET_MAX_MOVE(ch) = 100;

	for (i = 0; i < NUM_WEARS; i++) {
		if (GET_EQ(ch, i) && i != WEAR_IN_SHEATH_1 && i != WEAR_IN_SHEATH_2 && i != WEAR_IN_SHEATH_3)
			for (j = 0; j < MAX_OBJ_AFFECT; j++)
				affect_modify(ch, GET_EQ(ch, i)->affected[j].location, GET_EQ(ch, i)->affected[j].modifier, GET_EQ(ch, i)->obj_flags.bitvector, 0, TRUE);
		}

	update_morph_stats(ch, TRUE);

	for (af = ch->affected; af; af = af->next)
		affect_modify(ch, af->location, af->modifier, af->bitvector, af->disc_bit, TRUE);

	/* Make sure maximums are considered */
	GET_STRENGTH(ch) = MAX(0, MIN(GET_STRENGTH(ch), att_max(ch)));
	GET_DEXTERITY(ch) = MAX(0, MIN(GET_DEXTERITY(ch), att_max(ch)));
	GET_STAMINA(ch) = MAX(0, MIN(GET_STAMINA(ch), att_max(ch)));

	GET_CHARISMA(ch) = MAX(0, MIN(GET_CHARISMA(ch), att_max(ch)));
	GET_MANIPULATION(ch) = MAX(0, MIN(GET_MANIPULATION(ch), att_max(ch)));

	/* Clan Weakness: Nosferatu */
	if (GET_CLAN(ch) == CLAN_NOSFERATU)
		GET_APPEARANCE(ch) = 0;
	else
		GET_APPEARANCE(ch) = MAX(0, MIN(GET_APPEARANCE(ch), att_max(ch)));

	GET_PERCEPTION(ch) = MAX(0, MIN(GET_PERCEPTION(ch), att_max(ch)));
	GET_INTELLIGENCE(ch) = MAX(0, MIN(GET_INTELLIGENCE(ch), att_max(ch)));
	GET_WITS(ch) = MAX(0, MIN(GET_WITS(ch), att_max(ch)));
	}


/*
 * Insert an affect_type in a char_data structure
 *  Automatically sets apropriate bits and apply's
 */
void affect_to_char(Creature ch, struct affected_type *af) {
	struct affected_type *affected_alloc;

	CREATE(affected_alloc, struct affected_type, 1);

	*affected_alloc = *af;
	affected_alloc->next = ch->affected;
	ch->affected = affected_alloc;

	affect_modify(ch, af->location, af->modifier, af->bitvector, af->disc_bit, TRUE);
	affect_total(ch);
	}


/* Add an affect to a room */
void affect_to_room(room_rnum room, struct affected_type *af) {
	struct affected_type *affected_alloc;

	CREATE(affected_alloc, struct affected_type, 1);

	*affected_alloc = *af;
	affected_alloc->next = world[room].af;
	world[room].af = affected_alloc;

	SET_BIT(world[room].affects, af->bitvector);
	}


/*
 * Remove an affected_type structure from a char (called when duration
 * reaches zero). Pointer *af must never be NIL!  Frees mem and calls
 * affect_location_apply
 */
void affect_remove(Creature ch, struct affected_type *af) {
	struct affected_type *temp;

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

	affect_modify(ch, af->location, af->modifier, af->bitvector, af->disc_bit, FALSE);
	REMOVE_FROM_LIST(af, ch->affected, next);
	free(af);
	affect_total(ch);
	}


/*
 * Remove an affected_type structure from a room (called when duration
 * reaches zero). Pointer *af must never be NIL!  Frees mem and calls
 * affect_location_apply
 */
void affect_remove_room(room_rnum room, struct affected_type *af) {
	struct affected_type *temp;

	if (world[room].af == NULL)
		return;

	REMOVE_BIT(world[room].affects, af->bitvector);

	REMOVE_FROM_LIST(af, world[room].af, next);
	free(af);
	}


/* Call affect_remove with every skill named "type" */
void affect_from_char(Creature ch, int type) {
	struct affected_type *hjp, *next;

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


/* Call affect_remove_room with every skill named "type" */
void affect_from_room(room_rnum room, int type) {
	struct affected_type *hjp, *next;

	for (hjp = world[room].af; hjp; hjp = next) {
		next = hjp->next;
		if (hjp->type == type)
			affect_remove_room(room, hjp);
		}
	}


/*
 * Return TRUE if a char is affected by an ability,
 * FALSE indicates not affected.
 */
bool affected_by_spell(Creature ch, int type) {
	struct affected_type *hjp;

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

	return (FALSE);
	}


bool room_affected_by_spell(room_rnum room, int type) {
	struct affected_type *hjp;

	for (hjp = world[room].af; hjp; hjp = hjp->next)
		if (hjp->type == type)
			return (TRUE);

	return (FALSE);
	}


void affect_join(Creature ch, struct affected_type * af, bool add_dur, bool avg_dur, bool add_mod, bool avg_mod) {
	struct affected_type *hjp;
	bool found = FALSE;

	for (hjp = ch->affected; !found && hjp; hjp = hjp->next) {
		if ((hjp->type == af->type) && (hjp->location == af->location)) {
			if (add_dur)
				af->duration += hjp->duration;
			if (avg_dur)
				af->duration = (af->duration + hjp->duration) / 2;

			if (add_mod)
				af->modifier += hjp->modifier;
			if (avg_mod)
				af->modifier = (af->modifier + hjp->modifier) / 2;

			affect_remove(ch, hjp);
			affect_to_char(ch, af);
			found = TRUE;
			}
		}

	if (!found)
		affect_to_char(ch, af);
	}


/* move a player out of a room */
void char_from_room(Creature ch) {
	extern int char_from_chair(Creature ch);
	Creature temp;
	Object obj;
	int pos;

	if (ch == NULL || ch->in_room == NOWHERE) {
		log("SYSERR: NULL character or NOWHERE in %s, char_from_room", __FILE__);
		exit(1);
		}

	if (ON_CHAIR(ch))
		char_from_chair(ch);

	if (FIGHTING(ch) != NULL)
		stop_fighting(ch);

	for (pos = 0; pos < NUM_WEARS; pos++)
		if (GET_EQ(ch, pos) && OBJ_FLAGGED(GET_EQ(ch, pos), ITEM_LIGHT))
			world[ch->in_room].light--;
	for (obj = ch->carrying; obj; obj = obj->next_content)
		if (OBJ_FLAGGED(obj, ITEM_LIGHT))
			world[ch->in_room].light--;

	REMOVE_FROM_LIST(ch, world[ch->in_room].people, next_in_room);
	ch->in_room = NOWHERE;
	ch->next_in_room = NULL;
	}


/* place a character in a room */
void char_to_room(Creature ch, room_rnum room) {
	int pos;
	Object obj;

	if (ch == NULL || room < 0 || room > top_of_world)
		log("SYSERR: Illegal value(s) passed to char_to_room. (Room: %d/%d Ch: %p", room, top_of_world, ch);
	else {
		if (ch->in_room != NOWHERE)
			char_from_room(ch);

		ch->next_in_room = world[room].people;
		world[room].people = ch;
		ch->in_room = room;

		for (pos = 0; pos < NUM_WEARS; pos++)
			if (GET_EQ(ch, pos) && OBJ_FLAGGED(GET_EQ(ch, pos), ITEM_LIGHT))
				world[room].light++;
		for (obj = ch->carrying; obj; obj = obj->next_content)
			if (OBJ_FLAGGED(obj, ITEM_LIGHT))
				world[room].light++;

		/* Stop fighting now, if we left. */
		if (FIGHTING(ch) && IN_ROOM(ch) != IN_ROOM(FIGHTING(ch))) {
			stop_fighting(FIGHTING(ch));
			stop_fighting(ch);
			}
		}

	if (GET_RIDING(ch))
		char_to_room(GET_RIDING(ch), ch->in_room);
	}


/* Make sure an object is NOWHERE! */
void check_obj_in_void(Object obj) {
	if (!obj)						return;
	if (obj->in_room != NOWHERE)	obj_from_room(obj);
	if (obj->in_obj)				obj_from_obj(obj);
	if (obj->carried_by)			obj_from_char(obj);
	if (obj->worn_by)
		if (unequip_char(obj->worn_by, obj->worn_on) != obj)
			log("SYSERR: Inconsistent worn_by and worn_on pointers!!");
	}


/* give an object to a char */
void obj_to_char(Object object, Creature ch) {
	check_obj_in_void(object);

	if (object && ch) {
		object->next_content = ch->carrying;
		ch->carrying = object;
		object->carried_by = ch;
		object->in_room = NOWHERE;
		IS_CARRYING_W(ch) += GET_OBJ_WEIGHT(object);
		IS_CARRYING_N(ch)++;

		if (ch->in_room != NOWHERE && OBJ_FLAGGED(object, ITEM_LIGHT))
			world[ch->in_room].light++;
		}
	else
		log("SYSERR: NULL obj (%p) or char (%p) passed to obj_to_char.", object, ch);
	}


/* take an object from a char */
void obj_from_char(Object object) {
	Object temp;

	if (object == NULL) {
		log("SYSERR: NULL object passed to obj_from_char.");
		return;
		}
	REMOVE_FROM_LIST(object, object->carried_by->carrying, next_content);

	IS_CARRYING_W(object->carried_by) -= GET_OBJ_WEIGHT(object);
	IS_CARRYING_N(object->carried_by)--;

	if (object->carried_by->in_room != NOWHERE && OBJ_FLAGGED(object, ITEM_LIGHT))
		world[object->carried_by->in_room].light++;

	object->carried_by = NULL;
	object->next_content = NULL;
	}


void equip_char(Creature ch, Object obj, int pos) {
	int j;

	if (pos < 0 || pos >= NUM_WEARS)
		return;

	if (GET_EQ(ch, pos)) {
		log("SYSERR: Char is already equipped: %s, %s", GET_NAME(ch), obj->short_description);
		return;
		}

	check_obj_in_void(obj);

	if (obj->carried_by) {
		log("SYSERR: EQUIP: Obj is carried_by when equip.");
		return;
		}
	if (obj->in_room != NOWHERE) {
		log("SYSERR: EQUIP: Obj is in_room when equip.");
		return;
		}

	GET_EQ(ch, pos) = obj;
	obj->worn_by = ch;
	obj->worn_on = pos;

	if (ch->in_room != NOWHERE && OBJ_FLAGGED(obj, ITEM_LIGHT))
		world[ch->in_room].light++;

	if (pos != WEAR_IN_SHEATH_1 && pos != WEAR_IN_SHEATH_2 && pos != WEAR_IN_SHEATH_3)
		for (j = 0; j < MAX_OBJ_AFFECT; j++)
			affect_modify(ch, obj->affected[j].location, obj->affected[j].modifier, obj->obj_flags.bitvector, 0, TRUE);

	affect_total(ch);
	}


Object unequip_char(Creature ch, int pos) {
	int j;
	Object obj;

	if ((pos < 0 || pos >= NUM_WEARS) || GET_EQ(ch, pos) == NULL)
		return (NULL);

	obj = GET_EQ(ch, pos);
	obj->worn_by = NULL;
	obj->worn_on = -1;

	if (ch->in_room != NOWHERE && OBJ_FLAGGED(obj, ITEM_LIGHT))
		world[ch->in_room].light--;

	GET_EQ(ch, pos) = NULL;

	if (pos != WEAR_IN_SHEATH_1 && pos != WEAR_IN_SHEATH_2 && pos != WEAR_IN_SHEATH_3)
		for (j = 0; j < MAX_OBJ_AFFECT; j++)
			affect_modify(ch, obj->affected[j].location, obj->affected[j].modifier, obj->obj_flags.bitvector, 0, FALSE);

	affect_total(ch);

	return (obj);
	}


int get_number(char **name) {
	int i;
	char *ppos;
	char number[MAX_INPUT_LENGTH];

	*number = '\0';

	if ((ppos = strchr(*name, '.')) != NULL) {
		*ppos++ = '\0';
		strcpy(number, *name);
		strcpy(*name, ppos);

		for (i = 0; *(number + i); i++)
			if (!isdigit(*(number + i)))
		return (0);

		return (atoi(number));
		}
	return (1);
	}


/* Search a given list for an object number, and return a ptr to that obj */
Object get_obj_in_list_num(int num, Object list) {
	Object i;

	for (i = list; i; i = i->next_content)
		if (GET_OBJ_RNUM(i) == num)
			return (i);

	return (NULL);
	}


/* search the entire world for an object number, and return a pointer  */
Object get_obj_num(obj_rnum nr) {
	Object i;

	for (i = object_list; i; i = i->next)
		if (GET_OBJ_RNUM(i) == nr)
			return (i);

	return (NULL);
	}


/* search a room for a char, and return a pointer if found..  */
Creature get_char_room(char *name, room_rnum room) {
	Creature i;
	int j = 0, number;
	char tmpname[MAX_INPUT_LENGTH];
	char *tmp = tmpname;

	strcpy(tmp, name);
	if (!(number = get_number(&tmp)))
		return (NULL);

	for (i = world[room].people; i && (j <= number); i = i->next_in_room)
		if (isname(tmp, i->player.name) || isname(tmp, PERS(i, i, 0)) || isname(tmp, PERS(i, i, 1)) || (GET_LASTNAME(i) && isname(name, GET_LASTNAME(i))))
			if (++j == number)
				return (i);

	return (NULL);
	}


/* search all over the world for a char num, and return a pointer if found */
Creature get_char_num(mob_rnum nr) {
	Creature i;

	for (i = character_list; i; i = i->next)
		if (GET_MOB_RNUM(i) == nr)
			return (i);

	return (NULL);
	}


/* put an object in a room */
void obj_to_room(Object object, room_rnum room) {
	check_obj_in_void(object);

	if (!object || room < 0 || room > top_of_world)
		log("SYSERR: Illegal value(s) passed to obj_to_room. (Room #%d/%d, obj %p)", room, top_of_world, object);
	else {
		object->next_content = world[room].contents;
		world[room].contents = object;
		object->in_room = room;
		object->carried_by = NULL;
		if (OBJ_FLAGGED(object, ITEM_LIGHT))
			world[object->in_room].light++;
		ROOM_WEIGHT(room) += GET_OBJ_WEIGHT(object);
		}
	}


/* Take an object from a room */
void obj_from_room(Object object) {
	extern int char_from_chair(Creature ch);
	Object temp;

	if (!object || object->in_room == NOWHERE) {
		log("SYSERR: NULL object (%p) or obj not in a room (%d) passed to obj_from_room", object, object->in_room);
		return;
		}

	if (IN_CHAIR(object))
		char_from_chair(IN_CHAIR(object));

	if (OBJ_FLAGGED(object, ITEM_LIGHT))
		world[object->in_room].light--;

	ROOM_WEIGHT(object->in_room) -= GET_OBJ_WEIGHT(object);

	REMOVE_FROM_LIST(object, world[object->in_room].contents, next_content);
	object->in_room = NOWHERE;
	object->next_content = NULL;
	}


/* put an object in an object (quaint)  */
void obj_to_obj(Object obj, Object obj_to) {
	Object tmp_obj;

	if (!obj || !obj_to || obj == obj_to) {
		log("SYSERR: NULL object (%p) or same source (%p) and target (%p) obj passed to obj_to_obj.", obj, obj, obj_to);
		return;
		}

	check_obj_in_void(obj);

	obj->next_content = obj_to->contains;
	obj_to->contains = obj;
	obj->in_obj = obj_to;

	for (tmp_obj = obj->in_obj; tmp_obj->in_obj; tmp_obj = tmp_obj->in_obj)
		GET_OBJ_WEIGHT(tmp_obj) += GET_OBJ_WEIGHT(obj);

	/* top level object.  Subtract weight from inventory if necessary. */
	GET_OBJ_WEIGHT(tmp_obj) += GET_OBJ_WEIGHT(obj);
	if (tmp_obj->carried_by)
		IS_CARRYING_W(tmp_obj->carried_by) += GET_OBJ_WEIGHT(obj);
	}


/* remove an object from an object */
void obj_from_obj(Object obj) {
	Object temp, obj_from;

	if (obj->in_obj == NULL) {
		log("SYSERR: (%s): trying to illegally extract obj from obj.", __FILE__);
		return;
		}
	obj_from = obj->in_obj;
	REMOVE_FROM_LIST(obj, obj_from->contains, next_content);

	/* Subtract weight from containers container */
	for (temp = obj->in_obj; temp->in_obj; temp = temp->in_obj)
		GET_OBJ_WEIGHT(temp) -= GET_OBJ_WEIGHT(obj);

	/* Subtract weight from char that carries the object */
	GET_OBJ_WEIGHT(temp) -= GET_OBJ_WEIGHT(obj);
	if (temp->carried_by)
		IS_CARRYING_W(temp->carried_by) -= GET_OBJ_WEIGHT(obj);

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


/* Set all carried_by to point to new owner */
void object_list_new_owner(Object list, Creature ch) {
	if (list) {
		object_list_new_owner(list->contains, ch);
		object_list_new_owner(list->next_content, ch);
		list->carried_by = ch;
		}
	}


/* Extract an object from the world */
void extract_obj(Object obj) {
	Object temp;

	check_obj_in_void(obj);

	/* Get rid of the contents of the object, as well. */
	while (obj->contains)
		extract_obj(obj->contains);

	REMOVE_FROM_LIST(obj, object_list, next);

	if (GET_OBJ_RNUM(obj) >= 0)
		(obj_index[GET_OBJ_RNUM(obj)].number)--;
	free_obj(obj);
	}


/* Extract a ch completely from the world, and leave his stuff behind */
void extract_char(Creature ch) {
	void display_statistics_to_char(Creature ch);

	Creature k, temp;
	Descr t_desc;
	Object obj;
	int i, freed = 0;

	perform_dismount(ch);

	if (GET_FED_ON_BY(ch)) {
		GET_FEEDING_FROM(GET_FED_ON_BY(ch)) = NULL;
		GET_FED_ON_BY(ch) = NULL;
		}
	if (GET_FEEDING_FROM(ch)) {
		GET_FED_ON_BY(GET_FEEDING_FROM(ch)) = NULL;
		GET_FEEDING_FROM(ch) = NULL;
		}

	if (GET_LEADING(ch)) {
		GET_LED_BY(GET_LEADING(ch)) = NULL;
		GET_LEADING(ch) = NULL;
		}
	if (GET_LED_BY(ch)) {
		GET_LEADING(GET_LED_BY(ch)) = NULL;
		GET_LED_BY(ch) = NULL;
		}

	if (GET_PULLING(ch)) {
		if (GET_PULLED_BY(GET_PULLING(ch), 0) == ch)
			GET_PULLED_BY(GET_PULLING(ch), 0) = NULL;
		if (GET_PULLED_BY(GET_PULLING(ch), 1) == ch)
			GET_PULLED_BY(GET_PULLING(ch), 1) = NULL;
		GET_PULLING(ch) = NULL;
		}

	if (!IS_NPC(ch) && !ch->desc) {
		for (t_desc = descriptor_list; t_desc; t_desc = t_desc->next)
			if (t_desc->original == ch)
				do_return(t_desc->character, NULL, 0, 0);
		}
	if (ch->in_room == NOWHERE) {
		log("SYSERR: NOWHERE extracting char %s. (%s, extract_char)", GET_NAME(ch), __FILE__);
		exit(1);
		}
	if (ch->followers || ch->master)
		die_follower(ch);

	/* Forget snooping, if applicable */
	if (ch->desc) {
		if (ch->desc->snooping) {
			ch->desc->snooping->snoop_by = NULL;
			ch->desc->snooping = NULL;
			}
		if (ch->desc->snoop_by) {
			SEND_TO_Q("Your victim is no longer among us.\r\n", ch->desc->snoop_by);
			ch->desc->snoop_by->snooping = NULL;
			ch->desc->snoop_by = NULL;
			}
		}
	/* transfer objects to room, if any */
	while (ch->carrying) {
		obj = ch->carrying;
		obj_from_char(obj);
		obj_to_room(obj, ch->in_room);
		}

	/* transfer equipment to room, if any */
	for (i = 0; i < NUM_WEARS; i++)
		if (GET_EQ(ch, i))
			obj_to_room(unequip_char(ch, i), ch->in_room);

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

	for (k = combat_list; k; k = temp) {
		temp = k->next_fighting;
		if (FIGHTING(k) == ch)
			stop_fighting(k);
		}

	char_from_room(ch);

	/* pull the char from the list */
	REMOVE_FROM_LIST(ch, character_list, next);

	if (ch->desc && ch->desc->original)
		do_return(ch, NULL, 0, 0);

	if (!IS_NPC(ch))
		Crash_delete_crashfile(ch);
	else {
		if (GET_MOB_RNUM(ch) > -1)		/* if mobile */
			mob_index[GET_MOB_RNUM(ch)].number--;
		free_char(ch);
		freed = 1;
		}

	if (!freed && ch->desc != NULL) {
		STATE(ch->desc) = CON_MENU;
		display_statistics_to_char(ch);
		SEND_TO_Q(MENU, ch->desc);
		}
	else {  /* if a player gets purged from within the game */
		if (!freed)
			free_char(ch);
		}
	}



/* ***********************************************************************
* Here follows high-level versions of some earlier routines, ie functions*
* which incorporate the actual player-data                               *.
*********************************************************************** */


Creature get_player_vis(Creature ch, char *name, int inroom) {
	Creature i;

	for (i = character_list; i; i = i->next) {
		if (IS_NPC(i))
			continue;
		if (IS_SET(inroom, FIND_CHAR_ROOM) && i->in_room != ch->in_room)
			continue;
		if (IS_SET(inroom, FIND_CHAR_ROOM) && AFF_FLAGGED(i, AFF_NO_TARGET_IN_ROOM))
			continue;
		if (!isname(name, i->player.name) && (!GET_LASTNAME(i) || !isname(name, GET_LASTNAME(i))) && !isname(name, PERS(i, ch, 0)) && !isname(name, PERS(i, ch, 1)))
			continue;
		if (!CAN_SEE(ch, i) && (!IS_SET(inroom, FIND_NO_DARK) || !CAN_SEE_NO_DARK(ch, i)))
			continue;

		return (i);
		}
	return (NULL);
	}


Creature get_char_room_vis(Creature ch, char *name) {
	Creature i;
	int j = 0, number;
	char tmpname[MAX_INPUT_LENGTH];
	char *tmp = tmpname;

	/* JE 7/18/94 :-) :-) */
	if (!str_cmp(name, "self") || !str_cmp(name, "me"))
		return (ch);

	/* 0.<name> means PC with name */
	strcpy(tmp, name);
	if (!(number = get_number(&tmp)))
		return (get_player_vis(ch, tmp, FIND_CHAR_ROOM));

	for (i = world[ch->in_room].people; i && j <= number; i = i->next_in_room)
		if (isname(tmp, i->player.name) || isname(tmp, PERS(i, ch, 0)) || isname(tmp, PERS(i, ch, 1)) || (GET_LASTNAME(i) && isname(name, GET_LASTNAME(i))))
			if (CAN_SEE(ch, i) && !AFF_FLAGGED(i, AFF_NO_TARGET_IN_ROOM))
				if (++j == number)
					return (i);

	return (NULL);
	}


Creature get_char_vis(Creature ch, char *name, int where) {
	Creature i;
	int j = 0, number;
	char tmpname[MAX_INPUT_LENGTH];
	char *tmp = tmpname;

	/* check the room first */
	if (IS_SET(where, FIND_CHAR_ROOM))
		return get_char_room_vis(ch, name);
	else if (IS_SET(where, FIND_CHAR_WORLD)) {
		if ((i = get_char_room_vis(ch, name)) != NULL)
			return (i);

		strcpy(tmp, name);
		if (!(number = get_number(&tmp)))
			return get_player_vis(ch, tmp, where);

		for (i = character_list; i && (j <= number); i = i->next)
			if ((isname(tmp, i->player.name) || isname(tmp, PERS(i, ch, 0)) || isname(tmp, PERS(i, ch, 1)) || (GET_LASTNAME(i) && isname(name, GET_LASTNAME(i)))) && (CAN_SEE(ch, i) || (IS_SET(where, FIND_NO_DARK) && CAN_SEE_NO_DARK(ch, i))))
				if (++j == number)
					return (i);
		}

	return (NULL);
	}


Object get_obj_in_list_vis(Creature ch, char *name, Object list) {
	Object i;
	int j = 0, number;
	char tmpname[MAX_INPUT_LENGTH];
	char *tmp = tmpname;

	strcpy(tmp, name);
	if (!(number = get_number(&tmp)))
		return (NULL);

	for (i = list; i && (j <= number); i = i->next_content)
		if (isname(tmp, i->name))
			if (CAN_SEE_OBJ(ch, i))
				if (++j == number)
					return (i);

	return (NULL);
	}


/* search the entire world for an object, and return a pointer  */
Object get_obj_vis(Creature ch, char *name) {
	Object i;
	int j = 0, number;
	char tmpname[MAX_INPUT_LENGTH];
	char *tmp = tmpname;

	/* scan items carried */
	if ((i = get_obj_in_list_vis(ch, name, ch->carrying)) != NULL)
		return (i);

	/* scan room */
	if ((i = get_obj_in_list_vis(ch, name, world[ch->in_room].contents)) != NULL)
		return (i);

	strcpy(tmp, name);
	if ((number = get_number(&tmp)) == 0)
		return (NULL);

	/* ok.. no luck yet. scan the entire obj list   */
	for (i = object_list; i && (j <= number); i = i->next)
		if (isname(tmp, i->name))
			if (CAN_SEE_OBJ(ch, i))
				if (++j == number)
					return (i);

	return (NULL);
	}


Object get_object_in_equip_vis(Creature ch, char *arg, Object equipment[], int *j) {
	for ((*j) = 0; (*j) < NUM_WEARS; (*j)++)
		if (equipment[(*j)])
			if (CAN_SEE_OBJ(ch, equipment[(*j)]))
				if (isname(arg, equipment[(*j)]->name))
					return (equipment[(*j)]);

	return (NULL);
	}


/* Generic Find, designed to find any object/character
 *
 * Calling:
 *  *arg     is the pointer containing the string to be searched for.
 *           This string doesn't have to be a single word, the routine
 *           extracts the next word itself.
 *  bitv..   All those bits that you want to "search through".
 *           Bit found will be result of the function
 *  *ch      This is the person that is trying to "find"
 *  **tar_ch Will be NULL if no character was found, otherwise points
 * **tar_obj Will be NULL if no object was found, otherwise points
 *
 * The routine used to return a pointer to the next word in *arg (just
 * like the one_argument routine), but now it returns an integer that
 * describes what it filled in.
 */
int generic_find(char *arg, bitvector_t bitvector, Creature ch, Creature *tar_ch, Object *tar_obj) {
	int i, found;
	char name[256];

	*tar_ch = NULL;
	*tar_obj = NULL;

	one_argument(arg, name);

	if (!*name)
		return (0);

	if (IS_SET(bitvector, FIND_CHAR_ROOM)) {	/* Find person in room */
		if ((*tar_ch = get_char_vis(ch, name, FIND_CHAR_ROOM)) != NULL)
			return (FIND_CHAR_ROOM);
		}
	if (IS_SET(bitvector, FIND_CHAR_WORLD)) {
		if ((*tar_ch = get_char_vis(ch, name, FIND_CHAR_WORLD)) != NULL)
			return (FIND_CHAR_WORLD);
		}
	if (IS_SET(bitvector, FIND_OBJ_EQUIP)) {
		for (found = FALSE, i = 0; i < NUM_WEARS && !found; i++)
			if (GET_EQ(ch, i) && isname(name, GET_EQ(ch, i)->name)) {
				*tar_obj = GET_EQ(ch, i);
				found = TRUE;
				}
		if (found)
			return (FIND_OBJ_EQUIP);
		}
	if (IS_SET(bitvector, FIND_OBJ_INV)) {
		if ((*tar_obj = get_obj_in_list_vis(ch, name, ch->carrying)) != NULL)
			return (FIND_OBJ_INV);
		}
	if (IS_SET(bitvector, FIND_OBJ_ROOM)) {
		if ((*tar_obj = get_obj_in_list_vis(ch, name, world[ch->in_room].contents)) != NULL)
			return (FIND_OBJ_ROOM);
		}
	if (IS_SET(bitvector, FIND_OBJ_WORLD)) {
		if ((*tar_obj = get_obj_vis(ch, name)))
			return (FIND_OBJ_WORLD);
 		}
	return (0);
	}


/* a function to scan for "all" or "all.x" */
int find_all_dots(char *arg) {
	if (!strcmp(arg, "all"))
		return (FIND_ALL);
	else if (!strncmp(arg, "all.", 4)) {
		strcpy(arg, arg + 4);
		return (FIND_ALLDOT);
		}
	else
		return (FIND_INDIV);
	}


int char_from_chair(Creature ch) {
	Object chair;

	if (!ON_CHAIR(ch))
		return 0;

	if (!(chair = ON_CHAIR(ch))) {
		log("SYSERR: No chair for char in char_from_chair");
		ON_CHAIR(ch) = NULL;
		return 0;
		}

	ON_CHAIR(ch) = NULL;
	IN_CHAIR(chair) = NULL;

	return 1;
	}


int char_to_chair(Creature ch, Object chair) {
	if (!ch || !chair)
		return 0;

	if (IN_CHAIR(chair) || ON_CHAIR(ch))
		return 0;

	IN_CHAIR(chair) = ch;
	ON_CHAIR(ch) = chair;

	return 1;
	}


/* affect_update: called from comm.c (causes spells to wear off) */
void affect_update(void) {
	extern const char *affect_wear_off_msgs[];
	struct affected_type *af, *next;
	Creature i;

	for (i = character_list; i; i = i->next)
		for (af = i->affected; af; af = next) {
			next = af->next;
			if (af->duration >= 1)
				af->duration--;
			else if (af->duration == -1)	/* No action */
				af->duration = -1;	/* GODs only! unlimited */
			else {
				if ((af->type > 0))
					if (!af->next || (af->next->type != af->type) || (af->next->duration > 0)) {
						if (*affect_wear_off_msgs[af->type])
							msg_to_char(i, "%s\r\n", affect_wear_off_msgs[af->type]);
						}
				affect_remove(i, af);
				}
			}
	}


/* affect_update_room: called from comm.c (causes spells to wear off) */
void affect_update_room(void) {
	extern const char *affect_wear_off_msgs[];
	struct affected_type *af, *next;
	room_rnum room;

	for (room = 0; room <= top_of_world; room++) {
		for (af = world[room].af; af; af = next) {
			next = af->next;
			if (af->duration >= 1)
				af->duration--;
			else if (af->duration == -1)	/* No action */
				af->duration = -1;	/* GODs only! unlimited */
			else {
				if ((af->type > 0))
					if (!af->next || (af->next->type != af->type) || (af->next->duration > 0)) {
						if (*affect_wear_off_msgs[af->type] && world[room].people)
							act(affect_wear_off_msgs[af->type], FALSE, world[room].people, 0, 0, TO_CHAR | TO_ROOM);
						}
				affect_remove_room(room, af);
				}
			}
		SET_BIT(ROOM_AFF_FLAGS(room), world[room].base_affects);
		}
	}


void perform_dismount(Creature ch) {
	if (GET_RIDING(ch)) {
		GET_RIDDEN_BY(GET_RIDING(ch)) = NULL;
		GET_RIDING(ch) = NULL;
		}
	else if (GET_RIDDEN_BY(ch)) {
		GET_RIDING(GET_RIDDEN_BY(ch)) = NULL;
		GET_RIDDEN_BY(ch) = NULL;
		}
	}


void perform_mount(Creature ch, Creature mount) {
	perform_dismount(ch);

	/* Just in case */
	if (GET_RIDDEN_BY(mount))
		return;

	GET_RIDING(ch) = mount;
	GET_RIDDEN_BY(mount) = ch;
	}


/* Find an optimal place to start */
room_rnum find_load_room(Creature ch) {
	extern int num_of_start_locs;
	extern int *start_locs;

	if (num_of_start_locs < 0)
		return 0;

	return (real_room(start_locs[number(0, num_of_start_locs)]));
	}


/* Remove specific lore */
void remove_lore_record(Creature ch, struct lore_data *lore) {
	struct lore_data *temp;

	if (!ch || IS_NPC(ch) || !lore)
		return;

	REMOVE_FROM_LIST(lore, GET_LORE(ch), next);
	free(lore);
	}


/* Remove all lore of a given type */
void remove_lore(Creature ch, int type, long value) {
	struct lore_data *lore, *next_lore;


	if (IS_NPC(ch))
		return;

	for (lore = GET_LORE(ch); lore; lore = next_lore) {
		next_lore = lore->next;

		if (lore->type == type)
			if (value == -1 || value == lore->value)
				remove_lore_record(ch, lore);
		}
	}


/* Clean up old kill/death lore */
void clean_lore(Creature ch) {
	extern struct time_info_data *mud_time_passed(time_t t2, time_t t1);
	extern long load_time();
	extern struct time_info_data time_info;

	struct lore_data *lore, *next_lore;
	struct time_info_data t;
	long beginning_of_time = load_time();

	if (IS_NPC(ch))
		return;

	for (lore = GET_LORE(ch); lore; lore = next_lore) {
		next_lore = lore->next;

		if (lore->type == LORE_PLAYER_KILL || lore->type == LORE_PLAYER_DEATH || lore->type == LORE_TOWER_DEATH) {
			t = *mud_time_passed((time_t) beginning_of_time, (time_t) lore->date);
			if (time_info.year - (-t.year + YEAR_ADD * 2) >= 1)
				remove_lore_record(ch, lore);
			}
		}
	}

/* Add lore to a character's list */
void add_lore(Creature ch, int type, long value) {
	void write_lore(Creature ch);

	struct lore_data *new, *lore;


	if (IS_NPC(ch))
		return;

	/* Clean old records */
	switch (type) {
		case LORE_START_VAMPIRE:
		case LORE_EMBRACE_VAMPIRE:
		case LORE_MAKE_GHOUL:
			remove_lore(ch, LORE_START_VAMPIRE, -1);
			remove_lore(ch, LORE_EMBRACE_VAMPIRE, -1);
			remove_lore(ch, LORE_MAKE_GHOUL, -1);
			break;
		case LORE_JOIN_EMPIRE:
			remove_lore(ch, LORE_DEFECT_EMPIRE, -1);
			remove_lore(ch, LORE_KICKED_EMPIRE, -1);
			remove_lore(ch, LORE_FOUND_EMPIRE, -1);
			break;
		case LORE_DEFECT_EMPIRE:
		case LORE_KICKED_EMPIRE:
			remove_lore(ch, LORE_JOIN_EMPIRE, -1);
			remove_lore(ch, LORE_FOUND_EMPIRE, -1);
			break;
		case LORE_FOUND_EMPIRE:
			remove_lore(ch, LORE_FOUND_EMPIRE, -1);
			remove_lore(ch, LORE_JOIN_EMPIRE, -1);
			remove_lore(ch, LORE_KICKED_EMPIRE, -1);
			remove_lore(ch, LORE_DEFECT_EMPIRE, -1);
			break;
		}

	/* Find the last entry in ch's lore */
	for (lore = GET_LORE(ch); lore && lore->next; lore = lore->next);

	CREATE(new, struct lore_data, 1);
	new->type = type;
	new->value = value;
	new->date = (long) time(0);
	new->next = NULL;

	/* If they have lore, append this to the end.  Elsewise it becomes their lore */
	if (lore)
		lore->next = new;
	else
		GET_LORE(ch) = new;

	/* And last but not least, save it */
	write_lore(ch);
	}