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: skills.c                                         EmpireMUD 0.0b *
*  Usage: code related to non-offensive skills and abilities              *
*                                                                         *
*  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 "interpreter.h"
#include "handler.h"
#include "db.h"
#include "skills.h"
#include "vnums.h"
#include "empire.h"


/* Ability names */
const char *talents[] = {
	"Acting",
	"Alertness",
	"Athletics",
	"Brawl",
	"Dodge",
	"Empathy",
	"Intimidation",
	"Larceny",
	"Leadership",
	"Primal-Urge",
	"Subterfuge",
	"\n"
	};

const char *skills[] = {
	"Animal Ken",
	"Archery",
	"Crafts",
	"Etiquette",
	"Herbalism",
	"Melee",
	"Music",
	"Performance",
	"Ride",
	"Stealth",
	"Survival",
	"\n"
	};

const char *knowledges[] = {
	"Academics",
	"Enigmas",
	"Hearth Wisdom",
	"Investigation",
	"Law",
	"Linguistics",
	"Medicine",
	"Occult",
	"Politics",
	"Science",
	"Seneschal",
	"\n"
	};


/* external variables */
extern int last_action_rotation;
extern const int dam_type[];
extern int rev_dir[];


Creature find_closest_char(Creature ch, char *arg, bool pc) {
	Creature best = NULL, i;
	int best_dist = MAP_SIZE * 2;
	int d[2], dist, j = 0, number;
	char tmpname[MAX_INPUT_LENGTH];
	char *tmp = tmpname;

	if ((i = get_char_room_vis(ch, arg)) != NULL)
		return (i);

	strcpy(tmp, arg);
	if (!(number = get_number(&tmp)))
		return find_closest_char(ch, tmp, TRUE);

	for (i = character_list; i && (j <= number); i = i->next)
		if ((isname(tmp, i->player.name) || (i->player.lastname && isname(tmp, i->player.lastname))) && CAN_SEE(ch, i) && i->in_room != NOWHERE && (!pc || !IS_NPC(i))) {
			if (number == 1) {
				d[0] = X_COORD(HOME_ROOM(ch->in_room)) - X_COORD(HOME_ROOM(i->in_room));
				d[1] = Y_COORD(HOME_ROOM(ch->in_room)) - Y_COORD(HOME_ROOM(i->in_room));
				if (d[0] < (-1 * MAP_WIDTH / 2))	d[0] += MAP_WIDTH;
				if (d[0] > (MAP_WIDTH / 2))			d[0] -= MAP_WIDTH;
				if (d[1] < (-1 * MAP_HEIGHT / 2))	d[1] += MAP_HEIGHT;
				if (d[1] > (MAP_HEIGHT / 2))		d[1] -= MAP_HEIGHT;
				dist = (d[0]) * (d[0]) + (d[1]) * (d[1]);
				if (dist < best_dist) {
					best_dist = dist;
					best = i;
					}
				}
			else if (++j == number)
				return (i);
			}

	return (best);
	}


ACMD(do_track) {
	Creature vict;
	int a[2], b[2], d[2], s[2], in_room, to_room, i;

	char *x_shifts[] = { "west", "", "east" };
	char *y_shifts[] = { "south", "", "north" };

	if (SECT(ch->in_room) == SECT_OCEAN || SECT(ch->in_room) == SECT_RIVER) {
		msg_to_char(ch, "You can't track over the ocean.\r\n");
		return;
		}
	if (SECT(ch->in_room) == SECT_BUILDING || SECT(ch->in_room) == SECT_MONUMENT_CLOSED || SECT(ch->in_room) == SECT_INSIDE) {
		msg_to_char(ch, "Why don't you try going outside before you start tracking.\r\n");
		return;
		}

	#ifdef X
		#undef X
	#endif
	#ifdef Y
		#undef Y
	#endif

	#define X		0
	#define Y		1

	if (IS_NPC(ch)) {
		send_to_char("You have no idea how.\r\n", ch);
		return;
		}
	one_argument(argument, arg);
	if (!*arg) {
		send_to_char("Who are you trying to track?\r\n", ch);
		return;
		}
	/* The person can't see the victim. */
	if (!(vict = find_closest_char(ch, arg, FALSE))) {
		send_to_char(NOPERSON, ch);
		return;
		}

	if ((i = ww_dice(GET_PERCEPTION(ch) + GET_INVESTIGATION(ch), 6 - SENSES_BONUS(ch))) BOTCHED) {
		s[X] = number(-1, 1);
		s[Y] = number(-1, 1);
		*buf = '\0';
		sprintf(buf + strlen(buf), "%s%s", y_shifts[s[Y]+1], x_shifts[s[X]+1]);
		if (!*buf) {
			msg_to_char(ch, "You sense no trail.\r\n");
			return;
			}
		msg_to_char(ch, "You think you sense a trail %s from here!\r\n", buf);
		return;
		}
	else if (i FAILED) {
		msg_to_char(ch, "You sense no trail.\r\n");
		return;
		}

	in_room = ch->in_room;
	to_room = HOME_ROOM(vict->in_room);

	a[X] = X_COORD(in_room);
	a[Y] = Y_COORD(in_room);
	b[X] = X_COORD(to_room);
	b[Y] = Y_COORD(to_room);
	d[X] = a[X] - b[X];
	d[Y] = a[Y] - b[Y];

	if (d[X] < (-1 * MAP_WIDTH / 2))		d[X] += MAP_WIDTH;
	if (d[X] > (MAP_WIDTH / 2))				d[X] -= MAP_WIDTH;
	if (d[Y] < (-1 * MAP_HEIGHT / 2))		d[Y] += MAP_HEIGHT;
	if (d[Y] > (MAP_HEIGHT / 2))			d[Y] -= MAP_HEIGHT;

	if (d[X] > 0)		s[X] = -1;
	else if (d[X])		s[X] = 1;
	else				s[X] = 0;
	if (d[Y] > 0)		s[Y] = -1;
	else if (d[Y])		s[Y] = 1;
	else				s[Y] = 0;

	if (SECT(real_shift(in_room, s[X], s[Y])) == SECT_OCEAN || SECT(real_shift(ch->in_room, s[X], s[Y])) == SECT_RIVER) {
		if (s[X] && SECT(real_shift(in_room, s[X], 0)) != SECT_OCEAN && SECT(real_shift(in_room, s[X], 0)) != SECT_RIVER)		s[Y] = 0;
		else if (s[Y] && SECT(real_shift(in_room, 0, s[Y])) != SECT_OCEAN && SECT(real_shift(in_room, 0, s[Y])) != SECT_RIVER)		s[X] = 0;
		else {
			msg_to_char(ch, "Unfortunately, the trail ends at the water's edge.\r\n");
			return;
			}
		}
	if (SECT(real_shift(in_room, s[X], s[Y])) == SECT_BUILDING || SECT(real_shift(in_room, s[X], s[Y])) == SECT_MONUMENT_CLOSED || SECT(real_shift(in_room, s[X], s[Y])) == SECT_MULTI) {
		if (SECT(real_shift(in_room, s[X], 0)) != SECT_BUILDING && SECT(real_shift(in_room, s[X], 0)) != SECT_MONUMENT_CLOSED && SECT(real_shift(in_room, s[X], 0)) != SECT_MULTI && s[X])		s[Y] = 0;
		else if (SECT(real_shift(in_room, 0, s[Y])) != SECT_BUILDING && SECT(real_shift(in_room, 0, s[Y])) != SECT_MONUMENT_CLOSED && SECT(real_shift(in_room, 0, s[Y])) != SECT_MULTI && s[Y])	s[X] = 0;
		else {
			msg_to_char(ch, "The trail ends at the base of the building.\r\n");
			return;
			}
		}

	sprintf(buf, "%s%s", y_shifts[s[Y]+1], x_shifts[s[X]+1]);
	if (!*buf)
		msg_to_char(ch, "You're already there!\r\n");
	else
		msg_to_char(ch, "You sense a trail %s from here!\r\n", buf);
	}


ACMD(do_steal) {
	Creature vict;
	Object obj;
	char vict_name[MAX_INPUT_LENGTH], obj_name[MAX_INPUT_LENGTH];
	int eq_pos, ohoh = 0;

	if (IS_NPC(ch)) {
		send_to_char("You have no idea how to do that.\r\n", ch);
		return;
		}

	two_arguments(argument, obj_name, vict_name);

	if (!(vict = get_char_vis(ch, vict_name, FIND_CHAR_ROOM))) {
		send_to_char("Steal what from whom?\r\n", ch);
		return;
		}
	else if (vict == ch) {
		send_to_char("Come on now, that's rather stupid!\r\n", ch);
		return;
		}

	if (IS_GOD(vict) || IS_IMMORTAL(vict)) {
		msg_to_char(ch, "You can't steal from a God!\r\n");
		return;
		}
	if (IS_GOD(ch) && !IS_GOD(vict)) {
		msg_to_char(ch, "That would just be cruel.\r\n");
		return;
		}

	if (!IS_NPC(vict) && GET_MORPH(vict) != MORPH_NONE) {
		msg_to_char(ch, "You can't steal from someone with no inventory!\r\n");
		return;
		}

	if (DSC_FLAGGED(vict, DSC_BITUMENOUS_FLESH)) {
		act("You can't steal from $M, $E's protected by a solid, bitumenous layer!", FALSE, ch, 0, vict, TO_CHAR);
		return;
		}

	if (!(obj = get_obj_in_list_vis(ch, obj_name, vict->carrying))) {
		for (eq_pos = 0; eq_pos < NUM_WEARS; eq_pos++)
			if (GET_EQ(vict, eq_pos) && (isname(obj_name, GET_EQ(vict, eq_pos)->name)) && CAN_SEE_OBJ(ch, GET_EQ(vict, eq_pos))) {
				obj = GET_EQ(vict, eq_pos);
				break;
				}
		if (!obj) {
			act("$E hasn't got that item.", FALSE, ch, 0, vict, TO_CHAR);
			return;
			}
		else {			/* It is equipment */
			if ((GET_POS(vict) > POS_SLEEPING)) {
				send_to_char("Steal the equipment now?  Impossible!\r\n", ch);
				return;
				}
			else if (eq_pos == WEAR_SHEATH_1 && GET_EQ(vict, WEAR_IN_SHEATH_1))	{
				msg_to_char(ch, "You can't remove that while something's in it!\r\n");
				return;
				}
			else if (eq_pos == WEAR_SHEATH_2 && GET_EQ(vict, WEAR_IN_SHEATH_2))	{
				msg_to_char(ch, "You can't remove that while something's in it!\r\n");
				return;
				}
			else if (eq_pos == WEAR_SHEATH_3 && GET_EQ(vict, WEAR_IN_SHEATH_3))	{
				msg_to_char(ch, "You can't remove that while something's in it!\r\n");
				return;
				}
			else if (eq_pos == WEAR_WAIST && (GET_EQ(vict, WEAR_SHEATH_1) || GET_EQ(vict, WEAR_SHEATH_2) || GET_EQ(vict, WEAR_SHEATH_3)))	{
				msg_to_char(ch, "You can't remove that while something's attached to it!\r\n");
				return;
				}
			else {
				act("You unequip $p and steal it.", FALSE, ch, obj, 0, TO_CHAR);
				act("$n steals $p from $N.", FALSE, ch, obj, vict, TO_NOTVICT);
				obj_to_char(unequip_char(vict, eq_pos), ch);
				if (GET_LEVEL(vict) >= LVL_GOD)
					msg_to_char(vict, "You sense that someone has %s fingers in your pocket.\r\n", HSHR(ch));
				}
			}
		}
	else {			/* obj found in inventory */
		if ((ww_dice(GET_DEXTERITY(ch) + GET_LARCENY(ch), 7) <= 0 && AWAKE(vict) && !AFF_FLAGGED(ch, AFF_INVISIBLE)) || IS_IMMORTAL(vict) || IS_GOD(vict)) {
			ohoh = TRUE;
			send_to_char("Oops..\r\n", ch);
			act("$n tried to steal something from you!", FALSE, ch, 0, vict, TO_VICT);
			act("$n tries to steal something from $N.", TRUE, ch, 0, vict, TO_NOTVICT);
			}
		else {			/* Steal the item */
			if (IS_CARRYING_N(ch) + 1 < CAN_CARRY_N(ch)) {
				if (IS_CARRYING_W(ch) + GET_OBJ_WEIGHT(obj) < CAN_CARRY_W(ch)) {
					obj_to_char(obj, ch);
					send_to_char("Got it!\r\n", ch);
				if (GET_LEVEL(vict) >= LVL_GOD)
					msg_to_char(vict, "You sense that %s has %s fingers in your pocket.\r\n", PERS(ch, vict, 0), HSHR(ch));
					}
				else
					send_to_char("You cannot carry that much weight.\r\n", ch);
				}
			else
				send_to_char("You cannot carry that much.\r\n", ch);
			}
		}

	if (ohoh && IS_NPC(vict) && AWAKE(vict))
		hit(vict, ch);
	}



ACMD(do_hide) {
	Creature c;
	byte stealth;

	switch (SECT(ch->in_room)) {
		case SECT_FIELD:
		case SECT_WASTELAND:
		case SECT_DESERT:
		case SECT_ROAD:
		case SECT_FOREST_1:
		case SECT_OCEAN:
		case SECT_RIVER:
		case SECT_SEEDED:
		case SECT_FOUNTAIN:
		case SECT_WELL:
		case SECT_TOWER_OF_SOULS:
		case SECT_OASIS:
			msg_to_char(ch, "You can't hide here!\r\n");
			return;
		}

	if (GET_RIDING(ch)) {
		msg_to_char(ch, "You can't hide while mounted!\r\n");
		return;
		}

	msg_to_char(ch, "You attempt to hide yourself.\r\n");

	if (AFF_FLAGGED(ch, AFF_HIDE))
		REMOVE_BIT(AFF_FLAGS(ch), AFF_HIDE);

	WAIT_STATE(ch, PULSE_VIOLENCE * 2);

	for (c = world[ch->in_room].people; c; c = c->next_in_room)
		if (c != ch && CAN_SEE(c, ch) && ww_dice(GET_PERCEPTION(c) + GET_ALERTNESS(c), GET_WITS(ch) + GET_STEALTH(ch) - SENSES_BONUS(c)) > 0) {
			msg_to_char(ch, "You can't hide with somebody watching!\r\n");
			return;
			}

	/* Outdoors, stealth may not exceed survival */
	if (SECT(ch->in_room) == SECT_BUILDING || SECT(ch->in_room) == SECT_MONUMENT_CLOSED || SECT(ch->in_room) == SECT_MONUMENT_OPEN || SECT(ch->in_room) == SECT_INSIDE || SECT(ch->in_room) == SECT_MULTI)
		stealth = GET_STEALTH(ch);
	else
		stealth = MIN(GET_STEALTH(ch), GET_SURVIVAL(ch));

	if (ww_dice(GET_DEXTERITY(ch) + stealth, 6) < 1)
		return;

	SET_BIT(AFF_FLAGS(ch), AFF_HIDE);
	}


ACMD(do_mill) {
	Object obj;

	/*
	 * This is designed for o_WHEAT only.  It would have to be rewritten to allow
	 * the milling of other objects.
	 */

	one_argument(argument, arg);

	if (!*arg)
		msg_to_char(ch, "Mill what?\r\n");
	else if (!(obj = get_obj_in_list_vis(ch, arg, ch->carrying)))
		msg_to_char(ch, "You don't seem to have that.\r\n");
	else if (BUILDING_TYPE(ch->in_room) != BUILDING_MILL)
		msg_to_char(ch, "You need to be in a mill to do this.\r\n");
	else if (GET_OBJ_VNUM(obj) != o_WHEAT)
		msg_to_char(ch, "You can't mill that!\r\n");
	else if (!CAN_USE_ROOM(ch, ch->in_room, 0))
		msg_to_char(ch, "You don't have permission to use the mill.\r\n");
	else if (GET_ACTION(ch) != ACT_NONE)
		msg_to_char(ch, "You're already quite busy.\r\n");
	else {
		GET_ACTION(ch) = ACT_MILLING;
		GET_ACTION_TIMER(ch) = 60;
		GET_ACTION_ROOM(ch) = ch->in_room;
		GET_ACTION_ROTATION(ch) = last_action_rotation;
		act("You start milling $p.", FALSE, ch, obj, 0, TO_CHAR);
		act("$n starts milling $p.", FALSE, ch, obj, 0, TO_ROOM);
		extract_obj(obj);
		}
	}


ACMD(do_bake) {
	Object obj;

	/*
	 * This is designed to bake o_FLOUR only.  To bake other things,
	 * it would have to be rewritten.
	 */

	one_argument(argument, arg);

	if (!*arg)
		msg_to_char(ch, "What would you like to bake?\r\n");
	else if (!(obj = get_obj_in_list_vis(ch, arg, ch->carrying)))
		msg_to_char(ch, "You don't seem to have that.\r\n");
	else if (BUILDING_TYPE(ch->in_room) != BUILDING_BAKER && ROOM_TYPE(ch->in_room) != RTYPE_KITCHEN)
		msg_to_char(ch, "You can't bake here.\r\n");
	else if (GET_OBJ_VNUM(obj) != o_FLOUR)
		msg_to_char(ch, "You can't bake that.\r\n");
	else if (!CAN_USE_ROOM(ch, ch->in_room, 0))
		msg_to_char(ch, "You don't have permission to bake anything here.\r\n");
	else if (GET_ACTION(ch) != ACT_NONE)
		msg_to_char(ch, "You're already busy doing something else.\r\n");
	else {
		GET_ACTION(ch) = ACT_BAKING;
		GET_ACTION_TIMER(ch) = 60;
		GET_ACTION_ROOM(ch) = ch->in_room;
		GET_ACTION_ROTATION(ch) = last_action_rotation;
		act("You begin to bake with $p.", FALSE, ch, obj, 0, TO_CHAR);
		act("$n begins to bake with $p.", FALSE, ch, obj, 0, TO_ROOM);
		extract_obj(obj);
		}
	}


ACMD(do_skin) {
	Object obj, obj2;

	one_argument(argument, arg);

	if (!*arg)
		msg_to_char(ch, "What would you like to skin.\r\n");
	else if (!(obj = get_obj_in_list_vis(ch, arg, ch->carrying)))
		msg_to_char(ch, "You don't seem to have anything like that.\r\n");
	else if (!IS_CORPSE(obj))
		msg_to_char(ch, "You can only skin corpses.\r\n");
	else if (IS_SET(GET_OBJ_VAL(obj, 2), CORPSE_EATEN))
		msg_to_char(ch, "It's too badly mangled to get any amount of usable skin.\r\n");
	else if (IS_SET(GET_OBJ_VAL(obj, 2), CORPSE_SKINNED))
		msg_to_char(ch, "It's already been skinned.\r\n");
	else if (!GET_EQ(ch, WEAR_WIELD) || GET_OBJ_TYPE(GET_EQ(ch, WEAR_WIELD)) != ITEM_WEAPON || dam_type[GET_OBJ_VAL(GET_EQ(ch, WEAR_WIELD), 2)] < DAM_LETHAL)
		msg_to_char(ch, "You need to be wielding a sharp tool to skin a corpse.\r\n");
	else {
		act("You carefully skin $p.", FALSE, ch, obj, 0, TO_CHAR);
		act("$n carefully skins $p.", TRUE, ch, obj, 0, TO_ROOM);
		obj2 = read_object(o_SKIN, VIRTUAL);
		GET_OBJ_VAL(obj2, 0) = GET_OBJ_VAL(obj, 1);		/* Amount of skin */
		obj_to_char(obj2, ch);
		SET_BIT(GET_OBJ_VAL(obj, 2), CORPSE_SKINNED);
		WAIT_STATE(ch, 2 RL_SEC);
		}
	}


ACMD(do_sew) {
	int i = 0;
	Object obj;

	struct sew_data {
		char *name;
		int vnum;
		Resource resources[5];
		int skill;	/* Crafts rating required */
		} sews[] = {
			{ "shawl",		o_SHAWL,	{ {o_SKIN, 200}, END_RESOURCE_LIST },	1	},
			{ "hood",		o_HOOD,		{ {o_SKIN, 25}, END_RESOURCE_LIST },	1	},
			{ "cloak",		o_CLOAK,	{ {o_SKIN, 220}, END_RESOURCE_LIST },	1	},
			{ "gloves",		o_GLOVES,	{ {o_SKIN, 25}, END_RESOURCE_LIST },	1	},
			{ "loop",		o_BELTLOOP,	{ {o_SKIN, 10}, END_RESOURCE_LIST },	1	},
			{ "shirt",		o_SHIRT,	{ {o_SKIN, 130}, END_RESOURCE_LIST },	2	},
			{ "blouse",		o_BLOUSE,	{ {o_SKIN, 130}, END_RESOURCE_LIST },	2	},
			{ "toga",		o_TOGA,		{ {o_SKIN, 150}, END_RESOURCE_LIST },	2	},
			{ "sash",		o_SASH,		{ {o_SKIN, 25}, END_RESOURCE_LIST },	2	},
			{ "belt",		o_BELT,		{ {o_SKIN, 25}, END_RESOURCE_LIST },	2	},
			{ "shield",		o_SHIELD,	{ {o_SKIN, 90}, END_RESOURCE_LIST },	2	},
			{ "shoes",		o_SHOES,	{ {o_SKIN, 50}, END_RESOURCE_LIST },	2	},
			{ "quiver",		o_QUIVER,	{ {o_SKIN, 100}, END_RESOURCE_LIST },	2	},
			{ "canteen",	o_CANTEEN,	{ {o_SKIN, 100}, END_RESOURCE_LIST },	2	},
			{ "scabbard",	o_SCABBARD,	{ {o_SKIN, 40}, END_RESOURCE_LIST },	2	},
			{ "cape",		o_CAPE,		{ {o_SKIN, 170}, END_RESOURCE_LIST },	3	},
			{ "sheath",		o_SHEATH,	{ {o_SKIN, 40}, END_RESOURCE_LIST },	3	},
			{ "rope",		o_ROPE,		{ {o_SKIN, 1}, END_RESOURCE_LIST },		3	},
			{ "pants",		o_PANTS,	{ {o_SKIN, 180}, END_RESOURCE_LIST },	3	},
			{ "tunic",		o_TUNIC,	{ {o_SKIN, 150}, END_RESOURCE_LIST },	3	},
			{ "sack",		o_SACK,		{ {o_SKIN, 80}, END_RESOURCE_LIST },	3	},
			{ "gauntlets",	o_GAUNTLETS,{ {o_SKIN, 75}, END_RESOURCE_LIST },	4	},
			{ "wineskin",	o_WINESKIN,	{ {o_SKIN, 120}, END_RESOURCE_LIST },	4	},
			{ "whip",		o_WHIP,		{ {o_SKIN, 120}, END_RESOURCE_LIST },	4	},
			{ "boots",		o_BOOTS,	{ {o_SKIN, 80}, END_RESOURCE_LIST },	4	},
			{ "hat",		o_HAT,		{ {o_SKIN, 30}, END_RESOURCE_LIST },	4	},
			{ "jacket",		o_JACKET,	{ {o_SKIN, 150}, END_RESOURCE_LIST },	5	},
			{ "ridingboots",o_RIDINGBOOTS,{ {o_SKIN, 80}, END_RESOURCE_LIST },	5	},
			{ "dress",		o_DRESS,	{ {o_SKIN, 220}, END_RESOURCE_LIST },	5	},
			{ "bracers",	o_BRACERS,	{ {o_SKIN, 60}, END_RESOURCE_LIST },	5	},
			{ "pack",		o_PACK,		{ {o_SKIN, 120}, END_RESOURCE_LIST },	5	},

			{ "\n", 0, { END_RESOURCE_LIST }, 0 }
		};

	skip_spaces(&argument);

	if (*argument)
		while ((!is_abbrev(argument, sews[i].name) || GET_CRAFTS(ch) < sews[i].skill) && str_cmp(sews[i].name, "\n"))
			i++;

	if (!*argument || !str_cmp(sews[i].name, "\n")) {
		msg_to_char(ch, "What would you like to sew?\r\nYour choices are:");
		for (i = 0; str_cmp(sews[i].name, "\n"); i++)
			if (sews[i].skill <= GET_CRAFTS(ch))
				msg_to_char(ch, " %s", sews[i].name);
		msg_to_char(ch, "\r\n");
		}
	else if (!has_resources(ch, sews[i].resources, FALSE))
		{ /* This line intentionally left blank */ }
	else {
		extract_resources(ch, sews[i].resources, FALSE);
		obj_to_char((obj = read_object(sews[i].vnum, VIRTUAL)), ch);
		act("You sew some skin together into $p!", FALSE, ch, obj, 0, TO_CHAR);
		act("$n sews some skin together into $p!", TRUE, ch, obj, 0, TO_ROOM);
		WAIT_STATE(ch, 2 RL_SEC);
		}
	}


struct forge_data {
	char *name;
	int vnum;
	int crafts;		/* Min requirement */
	int dexterity;	/* Min requirement */
	Resource resources[10];
	} forge[] = {

		/* Weapons */
		{ "dirk",		f_DIRK,				1,	1,		{ {o_IRON, 2}, END_RESOURCE_LIST }	},
		{ "axe",		f_AXE,				1,	1,		{ {o_IRON, 2}, {o_HANDLE, 1}, END_RESOURCE_LIST }	},
		{ "hammer",		f_HAMMER,			1,	1,		{ {o_IRON, 2}, {o_HANDLE, 1}, END_RESOURCE_LIST }	},
		{ "pick",		f_PICK,				1,	1,		{ {o_IRON, 2}, {o_HANDLE, 1}, END_RESOURCE_LIST }	},
		{ "shortsword",	f_SHORTSWORD,		1,	1,		{ {o_IRON, 2}, END_RESOURCE_LIST }	},
		{ "dagger",		f_DAGGER,			2,	1,		{ {o_IRON, 2}, END_RESOURCE_LIST }	},
		{ "shovel",		f_SHOVEL,			2,	2,		{ {o_IRON, 2}, {o_HANDLE, 1}, END_RESOURCE_LIST }	},
		{ "rapier",		f_RAPIER,			2,	2,		{ {o_IRON, 2}, END_RESOURCE_LIST }	},
		{ "mace",		f_MACE,				3,	2,		{ {o_IRON, 3}, {o_HANDLE, 1}, END_RESOURCE_LIST }	},
		{ "sword",		f_SWORD,			3,	2,		{ {o_IRON, 3}, END_RESOURCE_LIST }	},
		{ "scimitar",	f_SCIMITAR,			3,	2,		{ {o_IRON, 3}, END_RESOURCE_LIST }	},
		{ "pike",		f_PIKE,				3,	3,		{ {o_IRON, 2}, {o_HANDLE, 1}, END_RESOURCE_LIST }	},
		{ "scythe",		f_SCYTHE,			3,	3,		{ {o_IRON, 3}, {o_HANDLE, 1}, END_RESOURCE_LIST }	},
		{ "halbert",	f_HALBERT,			4,	3,		{ {o_IRON, 2}, {o_HANDLE, 1}, END_RESOURCE_LIST }	},
		{ "maul",		f_MAUL,				4,	4,		{ {o_IRON, 2}, {o_HANDLE, 1}, END_RESOURCE_LIST }	},
		{ "poleaxe",	f_POLEAXE,			4,	4,		{ {o_IRON, 2}, {o_HANDLE, 1}, END_RESOURCE_LIST }	},
		{ "broadsword",	f_BROADSWORD,		5,	4,		{ {o_IRON, 4}, END_RESOURCE_LIST }	},
		{ "claymore",	f_CLAYMORE,			5,	5,		{ {o_IRON, 3}, {o_HANDLE, 1}, END_RESOURCE_LIST }	},
		{ "flamberge",	f_FLAMBERGE,		5,	5,		{ {o_IRON, 4}, {o_HANDLE, 1}, END_RESOURCE_LIST }	},

		/* Shields */
		{ "shield",		f_SHIELD,			2,	2,		{ {o_IRON, 4}, END_RESOURCE_LIST }	},
		{ "kiteshield",	f_KITE,				3,	3,		{ {o_IRON, 6}, END_RESOURCE_LIST }	},
		{ "towershield",f_TOWER,			4,	4,		{ {o_IRON, 8}, END_RESOURCE_LIST }	},

		/* Belts */
		{ "clip",		f_BELTCLIP,			1,	1,		{ {o_IRON, 1}, END_RESOURCE_LIST }	},
		{ "platedbelt",	f_BELT,				2,	2,		{ {o_IRON, 2}, END_RESOURCE_LIST }	},

		/* Armor */
		{ "breastplate",f_BREASTPLATE,		2,	1,		{ {o_IRON, 6}, END_RESOURCE_LIST }	},
		{ "chainmail",	f_CHAIN,			2,	2,		{ {o_IRON, 7}, END_RESOURCE_LIST }	},
		{ "ringmail",	f_RING,				3,	2,		{ {o_IRON, 8}, END_RESOURCE_LIST }	},
		{ "platemail",	f_PLATE,			4,	3,		{ {o_IRON, 12}, END_RESOURCE_LIST }	},
		{ "fullplate",	f_FULL_PLATE,		5,	4,		{ {o_IRON, 16}, END_RESOURCE_LIST }	},

		/* Headgear */
		{ "helm",		f_HELM,				2,	2,		{ {o_IRON, 3}, END_RESOURCE_LIST }	},
		{ "greathelm",	f_GREAT_HELM,		5,	2,		{ {o_IRON, 3}, END_RESOURCE_LIST }	},
		{ "crown",		f_CROWN,			3,	3,		{ {o_GOLD, 4}, END_RESOURCE_LIST }	},

		/* Jewelry */
		{ "silver ring",		f_RING_SILVER,		3,	2,	{ {o_SILVER, 1}, END_RESOURCE_LIST }	},
		{ "silver earrings", 	f_EARRING_SILVER,	3,	2,	{ {o_SILVER, 2}, END_RESOURCE_LIST }	},
		{ "gold ring",			f_RING_GOLD,		4,	3,	{ {o_GOLD, 1}, END_RESOURCE_LIST }	},
		{ "gold earrings",		f_EARRING_GOLD,		4,	3,	{ {o_GOLD, 2}, END_RESOURCE_LIST }	},
		{ "engagement ring",	f_RING_ENGAGEMENT,	4,	3,	{ {o_GOLD, 1}, END_RESOURCE_LIST }	},
		{ "wedding band",		f_RING_WEDDING,		4,	3,	{ {o_GOLD, 1}, END_RESOURCE_LIST }	},

		/* Misc */
		{ "mirror",		o_MIRROR,			3,	2,		{ {o_IRON, 1}, {o_PANE_SMALL, 1}, END_RESOURCE_LIST } },
		{ "silvermirror",o_MIRROR_SILVER,	3,	3,		{ {o_PANE_SMALL, 1}, {o_SILVER, 3}, END_RESOURCE_LIST } },
		{ "pan",		f_PAN,				3,	4,		{ {o_IRON, 2}, END_RESOURCE_LIST }	},
		{ "goldmirror",	o_MIRROR_GOLD,		4,	4,		{ {o_PANE_SMALL, 1}, {o_SILVER, 1}, {o_GOLD, 2}, END_RESOURCE_LIST } },

		{ "\n", 0, 0, 0, { END_RESOURCE_LIST, END_RESOURCE_LIST }	}
	};

void cancel_forging(Creature ch) {
	give_resources(ch, forge[GET_ACTION_VNUM(ch, 0)].resources, TRUE);

	GET_ACTION(ch) = 0;
	}

void finish_forging(Creature ch) {
	int f = GET_ACTION_VNUM(ch, 0);
	Object obj;

	GET_ACTION(ch) = ACT_NONE;

	obj_to_char((obj = read_object(forge[f].vnum, VIRTUAL)), ch);

	act("You finish forging $p!", FALSE, ch, obj, 0, TO_CHAR);
	act("$n finishes forging $p!", FALSE, ch, obj, 0, TO_ROOM);

	/* done AFTER the act() */
	if (GET_CRAFTS(ch) >= 5 && GET_INTELLIGENCE(ch) >= 4 && GET_OBJ_MATERIAL(obj) == ITEM_MAT_IRON)
		SET_BIT(GET_OBJ_EXTRA(obj), ITEM_SUPERIOR);
	}


ACMD(do_forge) {
	int i = -1;

	if (IS_NPC(ch))
		return;

	skip_spaces(&argument);

	if (*argument)
		while (1 == 1) {
			i++;
			if (!str_cmp(forge[i].name, "\n"))
				break;
			if (GET_DEXTERITY(ch) < forge[i].dexterity || GET_CRAFTS(ch) < forge[i].crafts)
				continue;
			if (is_abbrev(argument, forge[i].name))
				break;
			}

	if (!*argument || !str_cmp(forge[i].name, "\n")) {
		msg_to_char(ch, "What do you want to forge?\r\n");
		msg_to_char(ch, "You know how to make:");
		for (i = 0; str_cmp(forge[i].name, "\n"); i++)
			if (GET_DEXTERITY(ch) >= forge[i].dexterity && GET_CRAFTS(ch) >= forge[i].crafts)
				msg_to_char(ch, " %s", forge[i].name);
		msg_to_char(ch, "\r\n");
		}
	else if (BUILDING_TYPE(ch->in_room) != BUILDING_FORGE && ROOM_TYPE(ch->in_room) != RTYPE_FORGE)
		msg_to_char(ch, "You need to be in a forge to do this.\r\n");
	else if (!CAN_USE_ROOM(ch, ch->in_room, 0))
		msg_to_char(ch, "You don't have permission to use the forge.\r\n");
	else if (GET_ACTION(ch) == ACT_FORGING) {
		cancel_forging(ch);
		msg_to_char(ch, "You stop forging.\r\n");
		}
	else if (!GET_EQ(ch, WEAR_WIELD) || GET_OBJ_TYPE(GET_EQ(ch, WEAR_WIELD)) != ITEM_WEAPON || GET_OBJ_VAL(GET_EQ(ch, WEAR_WIELD), 2) != TYPE_HAMMER)
		msg_to_char(ch, "You don't have the proper tool to forge.\r\n");
	else if (GET_ACTION(ch) != ACT_NONE)
		msg_to_char(ch, "You're already quite busy.\r\n");
	else if (!has_resources(ch, forge[i].resources, FALSE))
		msg_to_char(ch, "You don't have the resources to forge that.\r\n");
	else {
		GET_ACTION(ch) = ACT_FORGING;
		GET_ACTION_TIMER(ch) = get_total_resources(forge[i].resources) * 10;
		GET_ACTION_ROOM(ch) = ch->in_room;
		GET_ACTION_ROTATION(ch) = last_action_rotation;
		GET_ACTION_VNUM(ch, 0) = i;
		extract_resources(ch, forge[i].resources, FALSE);
		act("You start forging.", FALSE, ch, 0, 0, TO_CHAR);
		act("$n starts forging.", FALSE, ch, 0, 0, TO_ROOM);
		}
	}


ACMD(do_fire) {
	Object obj;
	int i;

	int fire_changes[][2] = {
		{ uc_BOWL,			c_BOWL			},
		{ uc_POT,			c_POT			},
		{ uc_SHIELD,		c_SHIELD		},
		{ uc_LARGESHIELD,	c_LARGESHIELD	},
		{ uc_VESSEL,		c_VESSEL		},
		{ uc_GREATSHIELD,	c_GREATSHIELD	},

		{ -1, -1 }};

	one_argument(argument, arg);

	if (!*arg)
		msg_to_char(ch, "What do you wish to fire?\r\n");
	else if (!(obj = get_obj_in_list_vis(ch, arg, ch->carrying)))
		msg_to_char(ch, "You don't have anything like that.\r\n");
	else if (BUILDING_TYPE(ch->in_room) != BUILDING_POTTER)
		msg_to_char(ch, "You don't have the resources to fire clay here.\r\n");
	else {
		for (i = 0; fire_changes[i][0] != -1 && fire_changes[i][0] != GET_OBJ_VNUM(obj); i++);

		if (fire_changes[i][0] == -1) {
			msg_to_char(ch, "You can't fire that!\r\n");
			return;
			}
		extract_obj(obj);
		obj = read_object(fire_changes[i][1], VIRTUAL);
		obj_to_char(obj, ch);
		act("You put $p in the fire until it hardens.", FALSE, ch, obj, 0, TO_CHAR);
		act("$n puts $p in the fire until it hardens.", FALSE, ch, obj, 0, TO_ROOM);
		WAIT_STATE(ch, 2 RL_SEC);
		}
	}


ACMD(do_mine) {
	if (IS_NPC(ch))
		return;

	if (GET_ACTION(ch) == ACT_MINING) {
		msg_to_char(ch, "You stop mining.\r\n");
		act("$n stops mining.", FALSE, ch, 0, 0, TO_ROOM);
		GET_ACTION(ch) = ACT_NONE;
		}
	else if (GET_ACTION(ch) != ACT_NONE)
		msg_to_char(ch, "You're busy doing something else right now.\r\n");
	else if (BUILDING_TYPE(ch->in_room) != BUILDING_MINE)
		msg_to_char(ch, "This isn't a mine..\r\n");
	else if (!CAN_USE_ROOM(ch, ch->in_room, 0))
		msg_to_char(ch, "You don't have permission to mine here.\r\n");
	else if (!IS_COMPLETE(ch->in_room))
		msg_to_char(ch, "The mine shafts aren't finished yet.\r\n");
	else if (world[ch->in_room].spare <= 0)
		msg_to_char(ch, "The mine is depleted, you find nothing of use.\r\n");
	else if (BUILDING_TYPE(ch->in_room) != BUILDING_MINE)
		msg_to_char(ch, "You can't mine here.\r\n");
	else if (!GET_EQ(ch, WEAR_WIELD) || ((GET_OBJ_TYPE(GET_EQ(ch, WEAR_WIELD)) != ITEM_WEAPON || GET_OBJ_VAL(GET_EQ(ch, WEAR_WIELD), 2) != TYPE_PICK) && GET_OBJ_VNUM(GET_EQ(ch, WEAR_WIELD)) != o_HANDAXE))
		msg_to_char(ch, "You don't have a tool suitable for mining.\r\n");
	else {
		msg_to_char(ch, "You look for a suitable place and begin to mine.\r\n");
		act("$n finds a suitable place and begins to mine.", FALSE, ch, 0, 0, TO_ROOM);
		GET_ACTION(ch) = ACT_MINING;
		GET_ACTION_ROOM(ch) = ch->in_room;
		GET_ACTION_ROTATION(ch) = last_action_rotation;
		GET_ACTION_TIMER(ch) = 45;
		}
	}


ACMD(do_mold) {
	Object obj, obj2;
	int i;

	struct molding_data {
		char *name;
		int skill;	/* Crafts required */
		int vnum;
		} molds[] = {
			{ "bowl",		1,		uc_BOWL			},
			{ "pot",		2,		uc_POT			},
			{ "shield",		3,		uc_SHIELD		},
			{ "largeshield",4,		uc_LARGESHIELD	},
			{ "vessel",		5,		uc_VESSEL		},
			{ "greatshield",5,		uc_GREATSHIELD	},

			{ "\n", 0, 0 }};

	two_arguments(argument, arg, buf);

	if (!*arg || !*buf) {
		msg_to_char(ch, "What do you want to mold into what?\r\n");
		msg_to_char(ch, "You can mold:");
		for (i = 0; str_cmp(molds[i].name, "\n"); i++)
			if (GET_CRAFTS(ch) >= molds[i].skill)
				msg_to_char(ch, " %s", molds[i].name);
		msg_to_char(ch, ".\r\n");
		return;
		}
	if (!(obj = get_obj_in_list_vis(ch, arg, ch->carrying))) {
		msg_to_char(ch, "Nothing by that name here.\r\n");
		return;
		}

	i = 0;
	while (str_cmp(molds[i].name, "\n") && !is_abbrev(buf, molds[i].name))
		i++;

	if (!str_cmp(molds[i].name, "\n") || GET_CRAFTS(ch) < molds[i].skill) {
		msg_to_char(ch, "You don't know how to mold that.\r\n");
		msg_to_char(ch, "You can mold:");
		for (i = 0; str_cmp(molds[i].name, "\n"); i++)
			if (GET_CRAFTS(ch) >= molds[i].skill)
				msg_to_char(ch, " %s", molds[i].name);
		msg_to_char(ch, ".\r\n");
		return;
		}

	if (GET_OBJ_TYPE(obj) != ITEM_CLAY) {
		msg_to_char(ch, "You can only mold clay.\r\n");
		return;
		}

	if (!(obj2 = read_object(molds[i].vnum, VIRTUAL))) {
		msg_to_char(ch, "Molding is currently off-line.\r\n");
		return;
		}

	act("You mold $p into $P!", FALSE, ch, obj, obj2, TO_CHAR);
	act("$n molds $p into $P!", TRUE, ch, obj, obj2, TO_ROOM);

	extract_obj(obj);
	obj_to_char(obj2, ch);

	WAIT_STATE(ch, 2 RL_SEC);
	}


ACMD(do_harvest) {
	extern const char *crops[];

	if (IS_NPC(ch))
		return;

	if (GET_ACTION(ch) == ACT_HARVESTING) {
		msg_to_char(ch, "You stop harvesting the %s.\r\n", crops[(int) world[ch->in_room].type]);
		act("$n stops harvesting.\r\n", FALSE, ch, 0, 0, TO_ROOM);
		GET_ACTION(ch) = ACT_NONE;
		}
	else if (GET_ACTION(ch) != ACT_NONE)
		msg_to_char(ch, "You already busy doing something else.\r\n");
	else if (SECT(ch->in_room) != SECT_CROP)
		msg_to_char(ch, "You can't harvest anything here!\r\n");
	else if (!CAN_USE_ROOM(ch, ch->in_room, 0))
		msg_to_char(ch, "You don't have permission to harvest this crop.\r\n");
	else if (real_empire(world[ch->in_room].owner) != -1 && GET_RANK(ch) < empire[real_empire(world[ch->in_room].owner)].priv[PRIV_HARVEST])
		msg_to_char(ch, "You don't have permission to harvest crops.\r\n");
	else if (!GET_EQ(ch, WEAR_WIELD) || GET_OBJ_TYPE(GET_EQ(ch, WEAR_WIELD)) != ITEM_WEAPON || (GET_OBJ_VAL(GET_EQ(ch, WEAR_WIELD), 2) != TYPE_SLICE && GET_OBJ_VAL(GET_EQ(ch, WEAR_WIELD), 2) != TYPE_SLASH))
		msg_to_char(ch, "You aren't using the proper tool for that.\r\n");
	else {
		GET_ACTION(ch) = ACT_HARVESTING;
		GET_ACTION_ROOM(ch) = ch->in_room;
		GET_ACTION_ROTATION(ch) = last_action_rotation;
		if (GET_BUILD_VALUE(ch->in_room) == 0)
			GET_BUILD_VALUE(ch->in_room) = 40;
		msg_to_char(ch, "You begin harvesting the %s.\r\n", crops[(int) world[ch->in_room].type]);
		act("$n begins to harvest the crop.", FALSE, ch, 0, 0, TO_ROOM);
		}
	}


ACMD(do_dig) {
	if (GET_ACTION(ch) == ACT_DIGGING) {
		send_to_char("You stop digging.\r\n", ch);
		act("$n stops digging.", FALSE, ch, 0, 0, TO_ROOM);
		GET_ACTION(ch) = ACT_NONE;
		}
	else if (GET_ACTION(ch) != ACT_NONE)
		send_to_char("You're already busy.\r\n", ch);
	else if (
			/* Places you CAN'T dig */
			(SECT(ch->in_room) == SECT_RIVER || SECT(ch->in_room) == SECT_OCEAN ||
			SECT(ch->in_room) == SECT_ROAD || SECT(ch->in_room) == SECT_MONUMENT_CLOSED ||
			SECT(ch->in_room) == SECT_BUILDING || SECT(ch->in_room) == SECT_INSIDE ||
			SECT(ch->in_room) == SECT_MULTI ||
			SECT(ch->in_room) == SECT_TOWER_OF_SOULS || SECT(ch->in_room) == SECT_WASTELAND ||
			SECT(ch->in_room) == SECT_MONUMENT_OPEN)
			
			/* Places you CAN */
			&& (BUILDING_TYPE(ch->in_room) != BUILDING_MINE)
			)
		send_to_char("You can't dig here.\r\n", ch);
	else if (!CAN_USE_ROOM(ch, ch->in_room, 0))
		msg_to_char(ch, "You don't have permission to dig here.\r\n");
	else {
		GET_ACTION(ch) = ACT_DIGGING;
		GET_ACTION_TIMER(ch) = 8;
		GET_ACTION_ROOM(ch) = ch->in_room;
		GET_ACTION_ROTATION(ch) = last_action_rotation;
		send_to_char("You begin to dig into the ground.\r\n", ch);
		act("$n kneels down and begins to dig.", TRUE, ch, 0, 0, TO_ROOM);
		}
	}


ACMD(do_gather) {
	if (GET_ACTION(ch) == ACT_GATHERING) {
		send_to_char("You stop searching for sticks.\r\n", ch);
		act("$n stops looking around.", TRUE, ch, 0, 0, TO_ROOM);
		GET_ACTION(ch) = ACT_NONE;
		}
	else if (GET_ACTION(ch) != ACT_NONE)
		send_to_char("You're already busy.\r\n", ch);
	else if (SECT(ch->in_room) != SECT_FOREST_4 && SECT(ch->in_room) != SECT_FOREST_3 && SECT(ch->in_room) != SECT_FOREST_2 && SECT(ch->in_room) != SECT_FOREST_1&& (SECT(ch->in_room) != SECT_CROP || world[ch->in_room].type != CROP_FRUIT))
		send_to_char("You can't really gather outside of the forest or orchard!\r\n", ch);
	else if (!CAN_USE_ROOM(ch, ch->in_room, 0))
		msg_to_char(ch, "You don't have permission to gather here.\r\n");
	else {
		GET_ACTION(ch) = ACT_GATHERING;
		GET_ACTION_TIMER(ch) = number(5, 9);
		GET_ACTION_ROOM(ch) = ch->in_room;
		GET_ACTION_ROTATION(ch) = last_action_rotation;
		send_to_char("You begin looking around for sticks.\r\n", ch);
		}
	}


ACMD(do_pick) {
	if (GET_ACTION(ch) == ACT_PICKING) {
		send_to_char("You stop searching.\r\n", ch);
		act("$n stops looking around.", TRUE, ch, 0, 0, TO_ROOM);
		GET_ACTION(ch) = ACT_NONE;
		}
	else if (GET_ACTION(ch) != ACT_NONE)
		send_to_char("You're already busy.\r\n", ch);
	else if (SECT(ch->in_room) != SECT_FIELD && (SECT(ch->in_room) != SECT_CROP || world[ch->in_room].type != CROP_FRUIT))
		send_to_char("You can only pick flowers in a field, or apples in an orchard!\r\n", ch);
	else if (!CAN_USE_ROOM(ch, ch->in_room, 0))
		msg_to_char(ch, "You don't have permission to pick anything here.\r\n");
	else {
		GET_ACTION(ch) = ACT_PICKING;
		GET_ACTION_TIMER(ch) = number(5, 9);
		GET_ACTION_ROOM(ch) = ch->in_room;
		GET_ACTION_ROTATION(ch) = last_action_rotation;
		send_to_char("You begin looking around.\r\n", ch);
		}
	}


ACMD(do_chop) {
	if (GET_ACTION(ch) == ACT_CHOPPING) {
		send_to_char("You stop chopping the tree.\r\n", ch);
		act("$n stops chopping at the tree.", FALSE, ch, 0, 0, TO_ROOM);
		GET_ACTION(ch) = ACT_NONE;
		}
	else if(GET_ACTION(ch) != ACT_NONE)
		send_to_char("You're already busy.\r\n", ch);
	else if (SECT(ch->in_room) != SECT_FOREST_4 && SECT(ch->in_room) != SECT_FOREST_3 && SECT(ch->in_room) != SECT_FOREST_2 && SECT(ch->in_room) != SECT_FOREST_1)
		send_to_char("You can't really chop down trees unless you're in the forest.\r\n", ch);
	else if (!CAN_USE_ROOM(ch, ch->in_room, 0))
		msg_to_char(ch, "You don't have permission to chop down trees here.\r\n");
	else if (real_empire(world[ch->in_room].owner) != -1 && GET_RANK(ch) < empire[real_empire(world[ch->in_room].owner)].priv[PRIV_CHOP])
		msg_to_char(ch, "You don't have permission to chop down trees.\r\n");
	else if (!GET_EQ(ch, WEAR_WIELD))
		send_to_char("You need to be wielding some kind of axe to chop.\r\n", ch);
	else if (GET_OBJ_VAL(GET_EQ(ch, WEAR_WIELD), 2) != TYPE_SLICE)
		send_to_char("You need to be wielding some kind of axe to chop.\r\n", ch);
	else {
		GET_ACTION(ch) = ACT_CHOPPING;
		GET_ACTION_ROOM(ch) = ch->in_room;
		GET_ACTION_ROTATION(ch) = last_action_rotation;
		if (GET_BUILD_VALUE(ch->in_room) == 0)
			GET_BUILD_VALUE(ch->in_room) = 30;
		send_to_char("You swing back your axe and prepare to chop...\r\n", ch);
		act("$n swings $s axe over $s shoulder.", TRUE, ch, 0, 0, TO_ROOM);
		}
	}


ACMD(do_herd) {
	extern int perform_move(Creature ch, int dir, int need_specials_check, byte mode);

	Creature victim;
	int dir, to_room;

	two_arguments(argument, arg, buf);

	if (IS_NPC(ch))
		return;
	else if (!*arg || !*buf)
		msg_to_char(ch, "Who do you want to herd, and which direction?\r\n");
	else if (!(victim = get_char_vis(ch, arg, FIND_CHAR_ROOM)))
		msg_to_char(ch, NOPERSON);
	else if (victim == ch)
		msg_to_char(ch, "Type HELP -- SERIOUS HELP.\r\n");
	else if (!IS_NPC(victim) || victim->desc)
		msg_to_char(ch, "You can only herd animals.\r\n");
	else if ((dir = parse_direction(buf)) < 0 || (to_room = real_shift(ch->in_room, shift_dir[dir][0], shift_dir[dir][1])) == NOWHERE)
		msg_to_char(ch, "That's not a direction!\r\n");
	else if (!CAN_USE_ROOM(ch, ch->in_room, 0))
		msg_to_char(ch, "You don't have permission to herd here.\r\n");
	else if ((SECT(ch->in_room) == SECT_MULTI || SECT(ch->in_room) == SECT_INSIDE || SECT(ch->in_room) == SECT_BUILDING || SECT(ch->in_room) == SECT_MONUMENT_CLOSED) && !CAN_GO(ch, dir))
		msg_to_char(ch, "You can't herd anyone in a direction that's blocked!\r\n");
	else if (SECT(ch->in_room) == SECT_MOUNTAIN || SECT(ch->in_room) == SECT_OCEAN || SECT(ch->in_room) == SECT_RIVER)
		msg_to_char(ch, "You find it difficult to do that here.\r\n");
	else if (SECT(to_room) == SECT_MOUNTAIN || SECT(to_room) == SECT_OCEAN || SECT(to_room) == SECT_RIVER)
		msg_to_char(ch, "You find that difficult to do.\r\n");
	else if (GET_RIDDEN_BY(victim))
		msg_to_char(ch, "You can't herd someone who is being ridden.\r\n");
	else if (GET_LED_BY(victim) && GET_LED_BY(victim)->in_room == victim->in_room)
		msg_to_char(ch, "You can't herd someone who is being led by someone else.\r\n");
	else if (ww_dice(GET_MANIPULATION(ch) + GET_ANIMAL_KEN(ch), 6) <= 0) {
		act("You try to herd $N, but $E refuses to move!", FALSE, ch, 0, victim, TO_CHAR);
		act("$n tries to herd $N away, but $E refuses to move!", FALSE, ch, 0, victim, TO_ROOM);
		}
	else if (SECT(to_room) == SECT_BUILDING && world[real_shift(ch->in_room, shift_dir[dir][0], shift_dir[dir][1])].type != BUILDING_STABLE)
		msg_to_char(ch, "You can't herd an animal into a building.\r\n");
	else if (SECT(to_room) == SECT_MULTI)
		msg_to_char(ch, "You can't herd an animal in there.\r\n");
	else if (SECT(real_shift(ch->in_room, shift_dir[dir][0], shift_dir[dir][1])) == SECT_INSIDE)
		msg_to_char(ch, "You can't herd an animal in here.\r\n");
	else {
		if (perform_move(victim, dir, TRUE, 0)) {
			act("You skillfully herd $N.", FALSE, ch, 0, victim, TO_CHAR);
			act("$n skillfully herds $N.", FALSE, ch, 0, victim, TO_ROOM);
			if (!perform_move(ch, dir, FALSE, 0))
				char_to_room(victim, ch->in_room);
			}
		else {
			act("You try to herd $N, but $E refuses to move!", FALSE, ch, 0, victim, TO_CHAR);
			act("$n tries to herd $N away, but $E refuses to move!", FALSE, ch, 0, victim, TO_ROOM);
			}
		}
	}


ACMD(do_fish) {
	int dir;
	byte type = 0;

	for (dir = 0; dir < NUM_2D_DIRS; dir++)
		if (SECT(real_shift(ch->in_room, shift_dir[dir][0], shift_dir[dir][1])) == SECT_OCEAN)
			type = 1;
		else if (SECT(real_shift(ch->in_room, shift_dir[dir][0], shift_dir[dir][1])) == SECT_RIVER)
			type = 2;

	if (!type && SECT(ch->in_room) == SECT_OCEAN)
		type = 1;
	if (!type && SECT(ch->in_room) == SECT_RIVER)
		type = 2;

	if (IS_NPC(ch))
		return;
	else if (GET_ACTION(ch) == ACT_FISHING) {
		msg_to_char(ch, "You stop fishing.\r\n");
		act("$n stops fishing.", TRUE, ch, 0, 0, TO_ROOM);
		GET_ACTION(ch) = ACT_NONE;
		}
	else if (GET_ACTION(ch))
		msg_to_char(ch, "You're really too busy to do that.\r\n");
	else if (!IS_OUTDOORS(ch))
		msg_to_char(ch, "You can't fish in here!\r\n");
	else if (SECT(ch->in_room) == SECT_MOUNTAIN)
		msg_to_char(ch, "You're a little high up to be fishing.\r\n");
	else if (!type)
		msg_to_char(ch, "Unless you're fishing for worms in puddles, there's really nothing to catch here.\r\n");
	else if (!GET_EQ(ch, WEAR_WIELD) || GET_OBJ_TYPE(GET_EQ(ch, WEAR_WIELD)) != ITEM_WEAPON || GET_OBJ_VAL(GET_EQ(ch, WEAR_WIELD), 2) != TYPE_STING)
		msg_to_char(ch, "You'll need a spear to fish.\r\n");
	else {
		GET_ACTION(ch) = ACT_FISHING;
		GET_ACTION_ROTATION(ch) = last_action_rotation;
		GET_ACTION_TIMER(ch) = 40;
		GET_ACTION_ROOM(ch) = ch->in_room;
		GET_ACTION_VNUM(ch, 0) = type;
		msg_to_char(ch, "You begin looking for fish..\r\n");
		act("$n begins looking for fish.", TRUE, ch, 0, 0, TO_ROOM);
		}
	}


/* Melt Data */
int melt_data[][4] = {
	/* from, to, needs (max 8), gives */
	{ o_GOLD_SMALL,		o_GOLD_DISC,	8,	1 },
	{ o_SILVER,			o_SILVER_DISC,	2,	1 },
	{ o_GOLD,			o_GOLD_DISC,	2,	1 },
	{ o_SILVER_DISC,	o_SILVER_BAR,	2,	1 },
	{ o_GOLD_DISC,		o_GOLD_BAR,		2,	1 },

	/* Melted down */
	{ o_SILVER_BAR,		o_SILVER,		1,	4 },
	{ o_GOLD_BAR,		o_GOLD,			1,	4 },

	{ NOTHING, NOTHING }
	};


void finish_melting(Creature ch) {
	Object obj = NULL;
	int i, j;

	/* Find the entry in the table */
	for (i = 0; melt_data[i][0] != NOTHING && melt_data[i][0] != GET_ACTION_VNUM(ch, 0); i++);

	if (melt_data[i][0] == NOTHING) {
		GET_ACTION(ch) = ACT_NONE;
		return;
		}

	for (j = 0; j < melt_data[i][3]; j++)
		obj_to_char((obj = read_object(melt_data[i][1], VIRTUAL)), ch);

	sprintf(buf2, " (%dx)", j);
	sprintf(buf, "You have successfully created $p%s!", j > 1 ? buf2 : "");
	sprintf(buf1, "$n has successfully created $p%s!", j > 1 ? buf2 : "");

	act(buf, FALSE, ch, obj, 0, TO_CHAR);
	act(buf1, TRUE, ch, obj, 0, TO_ROOM);

	GET_ACTION(ch) = ACT_NONE;
	}


void cancel_melting(Creature ch) {
	Object obj = NULL;
	int i, j;

	/* Find the entry in the table */
	for (i = 0; melt_data[i][0] != NOTHING && melt_data[i][0] != GET_ACTION_VNUM(ch, 0); i++);

	for (j = 0; j < melt_data[i][2]; j++)
		obj_to_char((obj = read_object(melt_data[i][0], VIRTUAL)), ch);

	GET_ACTION(ch) = ACT_NONE;
	}


ACMD(do_melt) {
	Object obj[8];
	int i, k;

	if (IS_NPC(ch))
		return;

	one_argument(argument, arg);

	if (!*arg)
		msg_to_char(ch, "What would you like to melt?\r\n");
	else if (GET_ACTION(ch) != ACT_NONE)
		msg_to_char(ch, "You're busy right now.\r\n");
	else if (!(obj[0] = get_obj_in_list_vis(ch, arg, ch->carrying)))
		msg_to_char(ch, "You don't seem to have anything like that.\r\n");
	else if (BUILDING_TYPE(ch->in_room) != BUILDING_FORGE && ROOM_TYPE(ch->in_room) != RTYPE_FORGE)
		msg_to_char(ch, "You need to be in a forge to do this.\r\n");
	else if (!CAN_USE_ROOM(ch, ch->in_room, 0))
		msg_to_char(ch, "You don't have permission to use the forge.\r\n");
	else {
		/* Make sure it's in the list */
		for (i = 0; melt_data[i][0] != NOTHING && melt_data[i][0] != GET_OBJ_VNUM(obj[0]); i++);
		if (melt_data[i][0] == NOTHING) {
			msg_to_char(ch, "You can't melt that here.\r\n");
			return;
			}

		/* Find another few of identical vnum */
		for (k = 1; k < melt_data[i][2] && obj[k-1]; k++)
			for (obj[k] = obj[k-1]->next_content; obj[k]; obj[k] = obj[k]->next_content)
				if (GET_OBJ_VNUM(obj[k]) == GET_OBJ_VNUM(obj[0]))
					break;

		/* oops, couldn't find one */
		if (!obj[k-1]) {
			msg_to_char(ch, "You'll need %d of them to melt.\r\n", melt_data[i][2]);
			return;
			}

		GET_ACTION(ch) = ACT_MELTING;
		GET_ACTION_TIMER(ch) = 10;
		GET_ACTION_ROOM(ch) = ch->in_room;
		GET_ACTION_ROTATION(ch) = last_action_rotation;
		GET_ACTION_VNUM(ch, 0) = melt_data[i][0];
		act("You begin to melt $p.", FALSE, ch, obj[0], 0, TO_CHAR);
		act("$n begins to melt $p.", FALSE, ch, obj[0], 0, TO_ROOM);

		for (k = 0; k < melt_data[i][2]; k++)
			if (obj[k])
				extract_obj(obj[k]);
		}
	}


ACMD(do_blow) {
	int i = 0;

	struct blow_data_structure {
		char *name;
		obj_vnum vnum;
		} blow_data[] = {
			{ "flask",	o_FLASK	},
			{ "jar",	o_JAR	},
			{ "smallpane",	o_PANE_SMALL	},

			{ "\n", NOTHING }
			};

	one_argument(argument, arg);

	if (*arg)
		for (i = 0; str_cmp(blow_data[i].name, "\n") && !is_abbrev(arg, blow_data[i].name); i++);

	if (BUILDING_TYPE(ch->in_room) != BUILDING_GLASS)
		msg_to_char(ch, "You can't blow glass here.\r\n");
	else if (!IS_COMPLETE(ch->in_room))
		msg_to_char(ch, "You'll need to finish the building first.\r\n");
	else if (!CAN_USE_ROOM(ch, ch->in_room, 0))
		msg_to_char(ch, "You don't have permission to use this facility.\r\n");
	else if (GET_ACTION(ch) == ACT_GLASS) {
		msg_to_char(ch, "You scrap the material you were using and stop working on the glass.\r\n");
		act("$n stops blowing glass.", TRUE, ch, 0, 0, TO_ROOM);
		GET_ACTION(ch) = ACT_NONE;
		}
	else if (GET_ACTION(ch) != ACT_NONE)
		msg_to_char(ch, "You're already a little busy right now.\r\n");
	else if (!*arg || !str_cmp(blow_data[i].name, "\n")) {
		msg_to_char(ch, "Blow what: ");
		for (i = 0; str_cmp(blow_data[i].name, "\n"); i++)
			msg_to_char(ch, " %s", blow_data[i].name);
		msg_to_char(ch, "\r\n");
		}
	else {
		strcpy(buf1, GET_OBJ_NAME_BY_PROTO(real_object(blow_data[i].vnum)));
		msg_to_char(ch, "You prepare to blow %s.\r\n", buf1);
		sprintf(buf, "$n prepares to blow %s.", buf1);
		act(buf, TRUE, ch, 0, 0, TO_ROOM);
		GET_ACTION(ch) = ACT_GLASS;
		GET_ACTION_TIMER(ch) = 9;
		GET_ACTION_VNUM(ch, 0) = blow_data[i].vnum;
		GET_ACTION_ROOM(ch) = ch->in_room;
		GET_ACTION_ROTATION(ch) = last_action_rotation;
		}
	}


ACMD(do_milk) {
	void weight_change_object(Object obj, int weight);

	Creature mob;
	Object cont;
	int amount;

	two_arguments(argument, arg, buf);

	if (BUILDING_TYPE(ch->in_room) != BUILDING_STABLE)
		msg_to_char(ch, "You can't milk animals here!\r\n");
	else if (!CAN_USE_ROOM(ch, ch->in_room, 0))
		msg_to_char(ch, "You don't have permission to milk these animals.\r\n");
	else if (!*arg || !*buf)
		msg_to_char(ch, "What would you like to milk, and into what?\r\n");
	else if (!(mob = get_char_vis(ch, arg, FIND_CHAR_ROOM)))
		msg_to_char(ch, NOPERSON);
	else if (!IS_NPC(mob) || !MOB_FLAGGED(mob, MOB_MILKABLE))
		act("You can't milk $N!", FALSE, ch, 0, mob, TO_CHAR);
	else if (MOB_MILK_TIMER(mob))
		act("$E can't be milked again for a while.", FALSE, ch, 0, mob, TO_CHAR);
	else if (!(cont = get_obj_in_list_vis(ch, buf, ch->carrying)))
		msg_to_char(ch, "You don't seem to have a %s.\r\n", buf);
	else if (GET_OBJ_TYPE(cont) != ITEM_DRINKCON)
		act("You can't milk $N into $p!", FALSE, ch, cont, mob, TO_CHAR);
	else if (GET_OBJ_VAL(cont, 1) && GET_OBJ_VAL(cont, 2) != LIQ_MILK)
		msg_to_char(ch, "It's already full of something else.\r\n");
	else if (GET_OBJ_VAL(cont, 1) >= GET_OBJ_VAL(cont, 0))
		msg_to_char(ch, "It's already full.\r\n");
	else {
		act("You milk $N into $p.", FALSE, ch, cont, mob, TO_CHAR);
		act("$n milks $N into $p.", FALSE, ch, cont, mob, TO_ROOM);
		MOB_MILK_TIMER(mob) = number(10, 14);
		amount = GET_OBJ_VAL(cont, 0) - GET_OBJ_VAL(cont, 1);
		GET_OBJ_VAL(cont, 1) += amount;
		GET_OBJ_VAL(cont, 2) = LIQ_MILK;
		weight_change_object(cont, amount);	/* Add weight */
		}
	}


ACMD(do_sap) {
	SPECIAL(spec_bee_hive);
	void weight_change_object(Object obj, int weight);

	Object hive, cont;
	int amount;

	two_arguments(argument, arg, buf);

	if (BUILDING_TYPE(ch->in_room) != BUILDING_APIARY)
		msg_to_char(ch, "You can't sap honey here!\r\n");
	else if (!CAN_USE_ROOM(ch, ch->in_room, 0))
		msg_to_char(ch, "You don't have permission to sap honey here.\r\n");
	else if (!*arg || !*buf)
		msg_to_char(ch, "What would you like to sap honey from, and into what?\r\n");
	else if (!(hive = get_obj_in_list_vis(ch, arg, world[ch->in_room].contents)))
		msg_to_char(ch, "There's no %s here!", arg);
	else if (GET_OBJ_RNUM(hive) < 0 || obj_index[GET_OBJ_RNUM(hive)].spec_proc != spec_bee_hive)
		act("You can't get honey from $p!", FALSE, ch, hive, 0, TO_CHAR);
	else if (GET_OBJ_VAL(hive, 1) <= 0)
		act("$p has no honey in it!", FALSE, ch, hive, 0, TO_CHAR);
	else if (!(cont = get_obj_in_list_vis(ch, buf, ch->carrying)))
		msg_to_char(ch, "You don't seem to have a %s.\r\n", buf);
	else if (GET_OBJ_TYPE(cont) != ITEM_DRINKCON)
		act("You can't sap honey into $p!", FALSE, ch, cont, 0, TO_CHAR);
	else if (GET_OBJ_VAL(cont, 1) && GET_OBJ_VAL(cont, 2) != LIQ_HONEY)
		msg_to_char(ch, "It's already full of something else.\r\n");
	else if (GET_OBJ_VAL(cont, 1) >= GET_OBJ_VAL(cont, 0))
		msg_to_char(ch, "It's already full.\r\n");
	else {
		act("You sap some honey from $p into $P.", FALSE, ch, hive, cont, TO_CHAR);
		act("$n saps some honey from $p into $P.", FALSE, ch, hive, cont, TO_ROOM);

		amount = MIN(GET_OBJ_VAL(cont, 0) - GET_OBJ_VAL(cont, 1), GET_OBJ_VAL(hive, 1));
		GET_OBJ_VAL(hive, 1) -= amount;

		GET_OBJ_VAL(cont, 1) += amount;
		GET_OBJ_VAL(cont, 2) = LIQ_HONEY;
		weight_change_object(cont, amount);	/* Add weight */
		}
	}


ACMD(do_extract) {
	SPECIAL(spec_bee_hive);

	Object hive, wax;

	one_argument(argument, arg);

	if (BUILDING_TYPE(ch->in_room) != BUILDING_APIARY)
		msg_to_char(ch, "You can't extract wax here!\r\n");
	else if (!CAN_USE_ROOM(ch, ch->in_room, 0))
		msg_to_char(ch, "You don't have permission to extract wax here.\r\n");
	else if (!*arg)
		msg_to_char(ch, "What would you like to extract wax from?\r\n");
	else if (!(hive = get_obj_in_list_vis(ch, arg, world[ch->in_room].contents)))
		msg_to_char(ch, "There's no %s here!", arg);
	else if (GET_OBJ_RNUM(hive) < 0 || obj_index[GET_OBJ_RNUM(hive)].spec_proc != spec_bee_hive)
		act("You can't get wax from $p!", FALSE, ch, hive, 0, TO_CHAR);
	else if (GET_OBJ_VAL(hive, 2) < 100)
		act("There isn't enough wax in $p to extract.", FALSE, ch, hive, 0, TO_CHAR);
	else {
		wax = read_object(o_WAX, VIRTUAL);

		if (IS_CARRYING_N(ch) >= CAN_CARRY_N(ch) || GET_OBJ_WEIGHT(wax) + IS_CARRYING_W(ch) > CAN_CARRY_W(ch))
			obj_to_room(wax, ch->in_room);
		else
			obj_to_char(wax, ch);

		act("You extract $p from $P.", FALSE, ch, wax, hive, TO_CHAR);
		act("$n extracts $p from $P.", FALSE, ch, wax, hive, TO_ROOM);

		GET_OBJ_VAL(hive, 2) -= 100;
		}
	}


ACMD(do_infiltrate) {
	int to_room, dir;

	skip_spaces(&argument);

	if (IS_NPC(ch))
		return;
	else if (!*argument)
		msg_to_char(ch, "You must choose a direction to infiltrate.\r\n");
	else if ((dir = parse_direction(argument)) < 0)
		msg_to_char(ch, "Which direction would you like to infiltrate?\r\n");
	else if (dir == UP || dir == DOWN)
		msg_to_char(ch, "You can't infiltrate that direction!\r\n");
	else if (SECT((to_room = real_shift(ch->in_room, shift_dir[dir][0], shift_dir[dir][1]))) != SECT_BUILDING && SECT(to_room) != SECT_MULTI)
		msg_to_char(ch, "You can only infiltrate buildings.\r\n");
	else if (SECT(ch->in_room) == SECT_INSIDE)
		msg_to_char(ch, "You're already inside.\r\n");
	else if (GET_RIDING(ch))
		msg_to_char(ch, "You can't infiltrate while riding.\r\n");
	else if ((!IS_COMPLETE(to_room) && !ALWAYS_CLOSED(to_room)) || CAN_USE_ROOM(ch, to_room, 0))
		msg_to_char(ch, "You can just walk in.\r\n");
	else if (world[to_room].building_entrance != dir && ((BUILDING_TYPE(to_room) != BUILDING_DOCKS && BUILDING_TYPE(to_room) != BUILDING_SHIPYARD && BUILDING_TYPE(to_room) != BUILDING_TUNNEL && BUILDING_TYPE(to_room) != BUILDING_TUNNEL2 && BUILDING_TYPE(to_room) != BUILDING_GATEHOUSE && BUILDING_TYPE(to_room) != BUILDING_SHIPYARD2) || world[to_room].building_entrance != rev_dir[dir]))
		msg_to_char(ch, "You can only infiltrate at the entrance.\r\n");
	else if (BUILDING_TYPE(to_room) == BUILDING_HAVEN_BURROW || BUILDING_TYPE(to_room) == BUILDING_HAVEN_PUEBLO || BUILDING_TYPE(to_room) == BUILDING_HAVEN_CAVE || BUILDING_TYPE(to_room) == BUILDING_HAVEN_HOUSE)
		msg_to_char(ch, "You can't infiltrate a haven!\r\n");
	else if (ww_dice(GET_DEXTERITY(ch) + GET_LARCENY(ch), 7) <= 0 && !AFF_FLAGGED(ch, AFF_INVISIBLE)) {
		msg_to_char(ch, "You fail.\r\n");
		if (real_empire(world[to_room].owner) != -1)
			log_to_empire(real_empire(world[to_room].owner), "Someone has attempted to infiltrate at (%d, %d)!", X_COORD(to_room), Y_COORD(to_room));
		}
	else {
		char_from_room(ch);
		char_to_room(ch, to_room);
		look_at_room(ch);
		msg_to_char(ch, "\r\nInfiltration successful.\r\n");
		}
	}


/* tie/untie an npc */
void perform_npc_tie(Creature ch, Creature victim, int subcmd) {
	extern bool is_entrance(int rnum);
	Object rope;

	if (!subcmd && MOB_FLAGGED(victim, MOB_TIED))
		act("$E's already tied up!", FALSE, ch, 0, victim, TO_CHAR);
	else if (subcmd && !MOB_FLAGGED(victim, MOB_TIED))
		act("$E isn't even tied up!", FALSE, ch, 0, victim, TO_CHAR);
	else if (MOB_FLAGGED(victim, MOB_TIED)) {
		act("You untie $N.", FALSE, ch, 0, victim, TO_CHAR);
		act("$n unties you!", FALSE, ch, 0, victim, TO_VICT | TO_SLEEP);
		act("$n unties $N.", FALSE, ch, 0, victim, TO_NOTVICT);
		REMOVE_BIT(MOB_FLAGS(victim), MOB_TIED);
		obj_to_char(read_object(o_ROPE, VIRTUAL), ch);
		}
	else if (!(rope = get_obj_in_list_num(real_object(o_ROPE), ch->carrying)))
		msg_to_char(ch, "You don't have any rope.\r\n");
	else if (!is_entrance(ch->in_room))
		msg_to_char(ch, "You can't tie it here.\r\n");
	else {
		act("You tie $N up.", FALSE, ch, 0, victim, TO_CHAR);
		act("$n ties you up.", FALSE, ch, 0, victim, TO_VICT | TO_SLEEP);
		act("$n ties $N up.", FALSE, ch, 0, victim, TO_NOTVICT);
		SET_BIT(MOB_FLAGS(victim), MOB_TIED);
		extract_obj(rope);
		}
	}


ACMD(do_tie) {
	Creature victim;
	Object rope;

	/* subcmd 0 = tie, 1 = untie */

	one_argument(argument, arg);

	if (!*arg)
		msg_to_char(ch, "%sie whom?\r\n", subcmd ? "Unt" : "T");
	else if (!(victim = get_char_vis(ch, arg, FIND_CHAR_ROOM)))
		msg_to_char(ch, NOPERSON);
	else if (IS_NPC(victim))
		perform_npc_tie(ch, victim, subcmd);
	else if (IS_GOD(victim) || IS_IMMORTAL(victim))
		msg_to_char(ch, "You can't tie up a god!\r\n");
	else if (!subcmd && IS_INJURED(victim, INJ_TIED))
		act("$E is already tied!", FALSE, ch, 0, victim, TO_CHAR);
	else if (subcmd && !IS_INJURED(victim, INJ_TIED))
		act("$E isn't even tied up!", FALSE, ch, 0, victim, TO_CHAR);
	else if (IS_INJURED(victim, INJ_TIED)) {
		act("You unbind $N.", FALSE, ch, 0, victim, TO_CHAR);
		act("$n unbinds you!", FALSE, ch, 0, victim, TO_VICT | TO_SLEEP);
		act("$n unbinds $N.", FALSE, ch, 0, victim, TO_NOTVICT);
		GET_DAMAGE(victim) = MIN(6, GET_DAMAGE(victim));
		GET_AGG_DAMAGE(victim) = MIN(GET_DAMAGE(victim), GET_AGG_DAMAGE(victim));
		GET_POS(victim) = POS_RESTING;
		REMOVE_BIT(INJURY_FLAGS(victim), INJ_TIED);
		obj_to_char(read_object(o_ROPE, VIRTUAL), ch);
		}
	else if (GET_POS(victim) >= POS_SLEEPING)
		act("You need to knock $M out first.", FALSE, ch, 0, victim, TO_CHAR);
	else if (!(rope = get_obj_in_list_num(real_object(o_ROPE), ch->carrying)))
		msg_to_char(ch, "You don't have any rope.\r\n");
	else {
		act("You bind and gag $N!", FALSE, ch, 0, victim, TO_CHAR);
		act("$n binds and gags you!", FALSE, ch, 0, victim, TO_VICT | TO_SLEEP);
		act("$n binds and gags $N!", FALSE, ch, 0, victim, TO_NOTVICT);
		SET_BIT(INJURY_FLAGS(victim), INJ_TIED);
		if (GET_DAMAGE(victim) >= 7) {
			GET_DAMAGE(victim) = 6;
			update_pos(victim);
			}
		extract_obj(rope);
		}
	}


ACMD(do_harness) {
	Creature victim;
	Object rope = NULL, cart = NULL;

	/* subcmd 0 = harness, 1 = unharness */

	two_arguments(argument, arg, buf);

	if (!*arg || (!subcmd && !*buf)) {
		if (subcmd)
			msg_to_char(ch, "Remove whose harness?\r\n");
		else
			msg_to_char(ch, "Harness whom to what?\r\n");
		}
	else if (!(victim = get_char_vis(ch, arg, FIND_CHAR_ROOM)))
		msg_to_char(ch, NOPERSON);
	else if (subcmd && !GET_PULLING(victim))
		act("$E isn't harnessed.", FALSE, ch, 0, victim, TO_CHAR);
	else if (subcmd) {
		obj_to_char(read_object(o_ROPE, VIRTUAL), ch);
		cart = GET_PULLING(victim);
		if (GET_PULLED_BY(cart, 0) == victim)
			GET_PULLED_BY(cart, 0) = NULL;
		if (GET_PULLED_BY(cart, 1) == victim)
			GET_PULLED_BY(cart, 1) = NULL;
		GET_PULLING(victim) = NULL;
		act("You unlatch $N from $p.", FALSE, ch, cart, victim, TO_CHAR);
		act("$n unlatches you from $p.", FALSE, ch, cart, victim, TO_VICT);
		act("$n unlatches $N from $p.", FALSE, ch, cart, victim, TO_NOTVICT);
		}
	else if (GET_PULLING(victim))
		act("$E is already harnessed!", FALSE, ch, 0, victim, TO_CHAR);
	else if (GET_RIDDEN_BY(victim))
		msg_to_char(ch, "You can't harness someone who is being ridden.\r\n");
	else if (!IS_NPC(victim))
		msg_to_char(ch, "You can only harness animals.\r\n");
	else if (!(cart = get_obj_in_list_vis(ch, buf, world[ch->in_room].contents)))
		msg_to_char(ch, "You don't see a %s here.\r\n", buf);
	else if (GET_OBJ_TYPE(cart) != ITEM_CART)
		msg_to_char(ch, "You can't harness anyone to that!\r\n");
	else if (GET_PULLED_BY(cart, 0) && (GET_OBJ_VAL(cart, 1) <= 1 || GET_PULLED_BY(cart, 1)))
		msg_to_char(ch, "You can't harness any more animals to it.\r\n");
	else if (!(rope = get_obj_in_list_num(real_object(o_ROPE), ch->carrying)))
		msg_to_char(ch, "You need some rope to do that.\r\n");
	else if (!MOB_FLAGGED(victim, MOB_MOUNTABLE))
		act("You can't harness $N to that!", FALSE, ch, 0, victim, TO_CHAR);
	else {
		extract_obj(rope);
		if (GET_PULLED_BY(cart, 0))
			GET_PULLED_BY(cart, 1) = victim;
		else
			GET_PULLED_BY(cart, 0) = victim;
		GET_PULLING(victim) = cart;
		act("You harness $N to $p.", FALSE, ch, cart, victim, TO_CHAR);
		act("$n harnesses you to $p.", FALSE, ch, cart, victim, TO_VICT);
		act("$n harnesses $N to $p.", FALSE, ch, cart, victim, TO_NOTVICT);
		}
	}


ACMD(do_assemble) {
	int i = 0;
	Object obj;

	struct assemble_data {
		char *name;
		obj_vnum vnum;
		int science;
		int intelligence;
		Resource resources[10];
		} assembles[] = {
			{ "stool",		o_STOOL,		1,	1,		{ {o_STICK, 12}, {o_IRON, 1}, END_RESOURCE_LIST }	},
			{ "chair",		o_CHAIR,		1,	1,		{ {o_STICK, 12}, {o_TREE, 1}, {o_IRON, 1}, END_RESOURCE_LIST }	},
			{ "longbow",	o_LONGBOW,		2,	2,		{ {o_STICK, 3}, {o_SKIN, 20}, END_RESOURCE_LIST }	},
			{ "table",		o_TABLE,		2,	2,		{ {o_STICK, 12}, {o_TREE, 3}, {o_IRON, 3}, END_RESOURCE_LIST }	},
			{ "bench",		o_BENCH,		2,	2,		{ {o_STICK, 12}, {o_TREE, 3}, {o_IRON, 3}, END_RESOURCE_LIST }	},
			{ "shortbow",	o_SHORTBOW,		3,	2,		{ {o_STICK, 2}, {o_SKIN, 20}, END_RESOURCE_LIST }	},
			{ "lantern",	o_LANTERN,		3,	3,		{ {o_MIRROR, 2}, {o_IRON, 3}, {o_CANDLE, 2}, END_RESOURCE_LIST }	},
			{ "cart",		o_CART,			3,	3,		{ {o_STICK, 48}, {o_TREE, 20}, {o_IRON, 15}, END_RESOURCE_LIST }	},
			{ "carriage",	o_CARRIAGE,		3,	3,		{ {o_STICK, 48}, {o_TREE, 40}, {o_IRON, 25}, END_RESOURCE_LIST }	},
			{ "wagon",		o_WAGON,		4,	4,		{ {o_STICK, 48}, {o_TREE, 50}, {o_IRON, 35}, {o_SKIN, 300},	END_RESOURCE_LIST }	},
			{ "beehive",	o_BEEHIVE,		4,	4,		{ {o_STICK, 35}, {o_TREE, 8}, END_RESOURCE_LIST }	},
			{ "catapult",	o_CATAPULT,		5,	4,		{ {o_STICK, 48}, {o_TREE, 60}, {o_IRON, 45}, END_RESOURCE_LIST }	},

			{ "\n", 0, 0, 0, { END_RESOURCE_LIST} }
		};

	skip_spaces(&argument);

	if (*argument)
		while ((!is_abbrev(argument, assembles[i].name) || GET_INTELLIGENCE(ch) < assembles[i].intelligence || GET_SCIENCE(ch) < assembles[i].science) && str_cmp(assembles[i].name, "\n"))
			i++;

	if (!*argument || !str_cmp(assembles[i].name, "\n")) {
		msg_to_char(ch, "What would you like to assemble?\r\nYour choices are:");
		for (i = 0; str_cmp(assembles[i].name, "\n"); i++)
			if (assembles[i].intelligence <= GET_INTELLIGENCE(ch) && assembles[i].science <= GET_SCIENCE(ch))
				msg_to_char(ch, " %s", assembles[i].name);
		msg_to_char(ch, "\r\n");
		}
	else if (BUILDING_TYPE(ch->in_room) != BUILDING_CARPENTER)
		msg_to_char(ch, "You can't assemble here!\r\n");
	else if (!CAN_USE_ROOM(ch, ch->in_room, 0))
		msg_to_char(ch, "You don't have permission to do that here.\r\n");
	else if (!has_resources(ch, assembles[i].resources, TRUE))
		{ /* This line intentionally left blank */ }
	else {
		extract_resources(ch, assembles[i].resources, TRUE);

		obj = read_object(assembles[i].vnum, VIRTUAL);
		if (CAN_WEAR(obj, ITEM_WEAR_TAKE))
			obj_to_char(obj, ch);
		else
			obj_to_room(obj, ch->in_room);
		act("You assemble $p!", FALSE, ch, obj, 0, TO_CHAR);
		act("$n assembles $p!", TRUE, ch, obj, 0, TO_ROOM);
		WAIT_STATE(ch, 2 RL_SEC);
		}
	}


ACMD(do_struggle) {
	if (!IS_INJURED(ch, INJ_TIED))
		msg_to_char(ch, "You aren't even bound!\r\n");
	else if (ww_dice(GET_STRENGTH(ch), 8) + GET_POTENCE(ch) < 2) {
		msg_to_char(ch, "You struggle a bit, but fail to break free.\r\n");
		act("$n struggles a little with $s bindings!", TRUE, ch, 0, 0, TO_ROOM);
		WAIT_STATE(ch, 30 RL_SEC);
		}
	else {
		msg_to_char(ch, "You break free!\r\n");
		act("$n struggles with $s bindings and breaks free!", TRUE, ch, 0, 0, TO_ROOM);
		REMOVE_BIT(INJURY_FLAGS(ch), INJ_TIED);
		}
	}


ACMD(do_craft) {
	Object obj, weapon;
	int i = 0;

	struct craft_data_structure {
		char *name;
		obj_vnum from, to;
		int timer;
		} craft_data[] = {
			{ "arrow",			o_STICK,		o_ARROW,			16	},
			{ "canoe",			o_TREE,			o_CANOE,			48	},
			{ "flute",			o_STICK,		o_FLUTE,			48	},
			{ "handle",			o_STICK,		o_HANDLE,			16	},
			{ "lyre",			o_TREE,			o_LYRE,				48	},
			{ "walkingstick",	o_STICK,		o_WALKING_STICK,	48	},
			{ "stake",			o_STICK,		o_STAKE,			48	},
			{ "candle",			o_WAX,			o_CANDLE,			16	},

			{ "\n", NOTHING, NOTHING }
			};


	one_argument(argument, arg);

	/* Find what they picked */
	if (*arg)
		for (i = 0; str_cmp(craft_data[i].name, "\n") && !is_abbrev(arg, craft_data[i].name); i++);

	if (GET_ACTION(ch) == ACT_CRAFTING) {
		obj_to_char((obj = read_object(GET_ACTION_VNUM(ch, 0), VIRTUAL)), ch);
		act("You stop scraping at $p.", FALSE, ch, obj, 0, TO_CHAR);
		GET_ACTION(ch) = ACT_NONE;
		}
	else if (GET_ACTION(ch) != ACT_NONE)
		msg_to_char(ch, "You're already busy doing something else.\r\n");
	else if (!*arg || !str_cmp(craft_data[i].name, "\n")) {
		msg_to_char(ch, "You can craft:\r\n");
		for (i = 0; str_cmp(craft_data[i].name, "\n") && !is_abbrev(arg, craft_data[i].name); i++)
			msg_to_char(ch, "%s\r\n", craft_data[i].name);
		}
	else if ((!(weapon = GET_EQ(ch, WEAR_WIELD)) || dam_type[GET_OBJ_VAL(weapon, 2)] < DAM_LETHAL) && (weapon || !(weapon = GET_EQ(ch, WEAR_HOLD)) || dam_type[GET_OBJ_VAL(weapon, 2)] < DAM_LETHAL))
		msg_to_char(ch, "You need to be using a sharp tool to craft anything.\r\n");
	else if (!(obj = get_obj_in_list_num(real_object(craft_data[i].from), ch->carrying)) && !(obj = get_obj_in_list_num(real_object(craft_data[i].from), world[ch->in_room].contents)))
		msg_to_char(ch, "You need %s to craft that.\r\n", GET_OBJ_NAME_BY_PROTO(real_object(craft_data[i].from)));
	else {
		GET_ACTION(ch) = ACT_CRAFTING;
		GET_ACTION_ROOM(ch) = NOWHERE;
		GET_ACTION_TIMER(ch) = craft_data[i].timer;
		GET_ACTION_ROTATION(ch) = last_action_rotation;
		GET_ACTION_VNUM(ch, 0) = craft_data[i].from;
		GET_ACTION_VNUM(ch, 1) = craft_data[i].to;

		sprintf(buf, "You begin crafting %s.", GET_OBJ_NAME_BY_PROTO(real_object(craft_data[i].to)));
		sprintf(buf1, "$n begins crafting %s.", GET_OBJ_NAME_BY_PROTO(real_object(craft_data[i].to)));
		act(buf, FALSE, ch, obj, 0, TO_CHAR);
		act(buf1, TRUE, ch, obj, 0, TO_ROOM);
		extract_obj(obj);
		}
	}


ACMD(do_chip) {
	Object target, weapon;

	one_argument(argument, arg);

	if (GET_ACTION(ch) == ACT_CHIPPING) {
		obj_to_char((target = read_object(GET_ACTION_VNUM(ch, 0), VIRTUAL)), ch);
		msg_to_char(ch, "You stop chipping it.\r\n");
		act("$n stops chipping $p.", TRUE, ch, target, 0, TO_ROOM);
		GET_ACTION(ch) = ACT_NONE;
		}
	else if (GET_ACTION(ch) != ACT_NONE)
		msg_to_char(ch, "You're already busy doing something else.\r\n");
	else if (!*arg)
		msg_to_char(ch, "Chip what?\r\n");
	else if (!(target = get_obj_in_list_vis(ch, arg, ch->carrying)))
		msg_to_char(ch, "You don't seem to have a %s.\r\n", arg);
	else if (GET_OBJ_VNUM(target) != o_ROCK && GET_OBJ_VNUM(target) != o_CHIPPED && GET_OBJ_VNUM(target) != o_HANDAXE)
		msg_to_char(ch, "You can't chip that!\r\n");
	else if ((!(weapon = GET_EQ(ch, WEAR_WIELD)) || GET_OBJ_VAL(weapon, 2) != TYPE_HAMMER) && (weapon || !(weapon = GET_EQ(ch, WEAR_HOLD)) || GET_OBJ_VAL(weapon, 2) != TYPE_HAMMER))
		msg_to_char(ch, "You need to be using some kind of hammer to chip it.\r\n");
	else {
		GET_ACTION(ch) = ACT_CHIPPING;
		GET_ACTION_ROOM(ch) = NOWHERE;
		GET_ACTION_TIMER(ch) = 16;
		GET_ACTION_ROTATION(ch) = last_action_rotation;
		GET_ACTION_VNUM(ch, 0) = GET_OBJ_VNUM(target);
		act("You begin to chip at $p.", FALSE, ch, target, 0, TO_CHAR);
		act("$n begins to chip at $p.", TRUE, ch, target, 0, TO_ROOM);
		extract_obj(target);
		}
	}


ACMD(do_pan) {
	int i;

	for (i = 0; i < NUM_2D_DIRS; i++)
		if (SECT(real_shift(ch->in_room, shift_dir[i][0], shift_dir[i][1])) == SECT_RIVER)
			break;

	if (GET_ACTION(ch) == ACT_PANNING) {
		msg_to_char(ch, "You stop panning for gold.\r\n");
		GET_ACTION(ch) = ACT_NONE;
		}
	else if (GET_ACTION(ch) != ACT_NONE)
		msg_to_char(ch, "You're a bit busy right now.\r\n");
	else if (!IS_OUTDOORS(ch) || i == NUM_2D_DIRS)
		msg_to_char(ch, "You need to be next to a river to pan for gold.\r\n");
	else if (!GET_EQ(ch, WEAR_HOLD) || GET_OBJ_VNUM(GET_EQ(ch, WEAR_HOLD)) != f_PAN)
		msg_to_char(ch, "You need to be holding a pan to do that.\r\n");
	else {
		msg_to_char(ch, "You kneel down and begin panning at the river bed.\r\n");
		act("$n kneels down and begins panning at the river bed.", TRUE, ch, 0, 0, TO_ROOM);
		GET_ACTION(ch) = ACT_PANNING;
		GET_ACTION_TIMER(ch) = 10;
		GET_ACTION_ROOM(ch) = ch->in_room;
		GET_ACTION_ROTATION(ch) = last_action_rotation;
		}
	}


ACMD(do_play) {
	Object obj;

	if (GET_ACTION(ch) == ACT_MUSIC) {
		msg_to_char(ch, "You stop playing music.\r\n");
		act("$n stops playing music.", FALSE, ch, 0, 0, TO_ROOM);
		GET_ACTION(ch) = ACT_NONE;
		}
	else if (GET_ACTION(ch) != ACT_NONE)
		msg_to_char(ch, "You're busy right now.\r\n");
	else if (!(obj = GET_EQ(ch, WEAR_HOLD)) || GET_OBJ_TYPE(obj) != ITEM_INSTRUMENT)
		msg_to_char(ch, "You need to hold an instrument to play music!\r\n");
	else if (GET_MUSIC(ch) == 0)
		msg_to_char(ch, "You have no musical skill whatsoever.\r\n");
	else {
		act("You begin to play $p.", FALSE, ch, obj, 0, TO_CHAR);
		act("$n begins to play $p.", FALSE, ch, obj, 0, TO_ROOM);
		GET_ACTION(ch) = ACT_MUSIC;
		GET_ACTION_TIMER(ch) = -1;
		GET_ACTION_ROOM(ch) = NOWHERE;
		GET_ACTION_ROTATION(ch) = last_action_rotation;
		}
	}


ACMD(do_excavate) {
	extern bool is_entrance(int rnum);

	if (GET_ACTION(ch) == ACT_EXCAVATING) {
		msg_to_char(ch, "You stop the excavation.\r\n");
		act("$n stops excavating the trench.", FALSE, ch, 0, 0, TO_ROOM);
		GET_ACTION(ch) = ACT_NONE;
		}
	else if (GET_ACTION(ch) != ACT_NONE)
		msg_to_char(ch, "You're already quite busy.\r\n");
	else if (!CAN_USE_ROOM(ch, ch->in_room, 1))
		msg_to_char(ch, "You don't have permission to excavate here!\r\n");
	else if ((!GET_EQ(ch, WEAR_HOLD) || GET_OBJ_VNUM(GET_EQ(ch, WEAR_HOLD)) != f_SHOVEL) && (!GET_EQ(ch, WEAR_WIELD) || GET_OBJ_VNUM(GET_EQ(ch, WEAR_WIELD)) != f_SHOVEL))
		msg_to_char(ch, "You need a shovel to excavate.\r\n");
	else if (SECT(ch->in_room) == SECT_TRENCH && GET_BUILD_VALUE(ch->in_room) < 0) {
		msg_to_char(ch, "You begin to dig.\r\n");
		GET_ACTION(ch) = ACT_EXCAVATING;
		GET_ACTION_TIMER(ch) = -1;
		GET_ACTION_ROOM(ch) = ch->in_room;
		GET_ACTION_ROTATION(ch) = last_action_rotation;
		}
	else if (SECT(ch->in_room) == SECT_TRENCH)
		msg_to_char(ch, "The trench is already complete!\r\n");
	else if (SECT(ch->in_room) != SECT_FIELD && SECT(ch->in_room) != SECT_DESERT)
		msg_to_char(ch, "You can only dig a trench in an open field.\r\n");
	else if (is_entrance(ch->in_room))
		msg_to_char(ch, "You can't dig a trench in front of an entrance.\r\n");
	else {
		msg_to_char(ch, "You begin to excavate a trench.\r\n");
		act("$n begins excavating a trench.", FALSE, ch, 0, 0, TO_ROOM);
		GET_ACTION(ch) = ACT_EXCAVATING;
		GET_ACTION_TIMER(ch) = -1;
		GET_ACTION_ROOM(ch) = ch->in_room;
		GET_ACTION_ROTATION(ch) = last_action_rotation;

		/* Set up the trench */
		GET_BUILD_VALUE(ch->in_room) = -300;
		SECT(ch->in_room) = SECT_TRENCH;
		}
	}


ACMD(do_search) {
	Creature targ;
	bool found = FALSE;

	if (IS_NPC(ch))
		return;
	else if (AFF_FLAGGED(ch, AFF_BLIND))
		msg_to_char(ch, "How can you do that, you're blind!\r\n");
	else if (!CAN_SEE_IN_DARK_ROOM(ch, ch->in_room))
		msg_to_char(ch, "You can't see well enough here to search for anyone!\r\n");
	else if (AFF_FLAGGED(ch, AFF_SENSE_HIDE))
		msg_to_char(ch, "You search, but find nobody.\r\n");
	else {
		act("$n begins searching around!", TRUE, ch, 0, 0, TO_ROOM);

		for (targ = world[ch->in_room].people; targ; targ = targ->next_in_room) {
			if (ch == targ)
				continue;
			if (!AFF_FLAGGED(targ, AFF_HIDE) || CAN_SEE(ch, targ))
				continue;

			SET_BIT(AFF_FLAGS(ch), AFF_SENSE_HIDE);

			if (CAN_SEE(ch, targ) && ww_dice(GET_PERCEPTION(ch) + GET_INVESTIGATION(ch), 7 - SENSES_BONUS(ch)) > ww_dice(GET_DEXTERITY(targ) + GET_STEALTH(targ), 6)) {
				act("You find $N!", FALSE, ch, 0, targ, TO_CHAR);
				msg_to_char(targ, "You are discovered!\r\n");
				REMOVE_BIT(AFF_FLAGS(targ), AFF_HIDE);
				found = TRUE;
				}

			REMOVE_BIT(AFF_FLAGS(ch), AFF_SENSE_HIDE);
			}

		if (!found)
			msg_to_char(ch, "You search, but find nobody.\r\n");
		WAIT_STATE(ch, PULSE_VIOLENCE);
		}
	}


ACMD(do_scrape) {
	Object obj, weapon;

	one_argument(argument, arg);

	if (GET_ACTION(ch) == ACT_SCRAPING) {
		obj_to_char((obj = read_object(o_TREE, VIRTUAL)), ch);
		act("You stop scraping $p.", FALSE, ch, obj, 0, TO_CHAR);
		GET_ACTION(ch) = ACT_NONE;
		}
	else if (GET_ACTION(ch) != ACT_NONE)
		msg_to_char(ch, "You're already busy doing something else.\r\n");
	else if (!*arg)
		msg_to_char(ch, "Scrape what?\r\n");
	else if (!(obj = get_obj_in_list_vis(ch, arg, ch->carrying)))
		msg_to_char(ch, "You don't seem to have anything like that.\r\n");
	else if (GET_OBJ_VNUM(obj) != o_TREE)
		msg_to_char(ch, "You can't scrape that!\r\n");
	else if ((!(weapon = GET_EQ(ch, WEAR_WIELD)) || dam_type[GET_OBJ_VAL(weapon, 2)] < DAM_LETHAL) && (weapon || !(weapon = GET_EQ(ch, WEAR_HOLD)) || dam_type[GET_OBJ_VAL(weapon, 2)] < DAM_LETHAL))
		msg_to_char(ch, "You need to be using a sharp tool to scrape anything.\r\n");
	else {
		GET_ACTION(ch) = ACT_SCRAPING;
		GET_ACTION_ROOM(ch) = NOWHERE;
		GET_ACTION_TIMER(ch) = number(3, 8);
		GET_ACTION_ROTATION(ch) = last_action_rotation;

		act("You begin scraping $p.", FALSE, ch, obj, 0, TO_CHAR);
		act("$n begins scraping $p.", TRUE, ch, obj, 0, TO_ROOM);
		extract_obj(obj);
		}
	}


/* Temporary Werewolf Stuff */
struct tribe_data tribe[] = {
	{ "Black Furies" },
	{ "Bone Gnawers" },
	{ "Children of Gaia" },
	{ "Fenrir" },
	{ "Fianna" },
	{ "Red Talons" },
	{ "Shadow Lords" },
	{ "Silent Striders" },
	{ "Silver Fangs" },
	{ "Warders" },

	{ "\n" }
	};


const char *auspice[] = {
	"Ragabash",
	"Theurge",
	"Philodox",
	"Galliard",
	"Ahroun",
	"\n"
	};


const char *breed[] = {
	"Homid",
	"Metis",
	"Lupus",
	"\n"
	};


const char *forms[] = { "homid", "glabro", "crinos", "hispo", "lupus", "\n" };
int form_morphs[] = { MORPH_NONE, MORPH_GLABRO, MORPH_CRINOS, MORPH_HISPO, MORPH_LUPUS };


/* use: shift <form> */
ACMD(do_shift) {
	int to_form = MORPH_NONE;

	two_arguments(argument, arg, buf);

	/* buf WILL be used to use rage to morph */
	/* also, this SHOULD be based upon successes */

	if (!*arg)
		msg_to_char(ch, "What form would you like to shift to?\r\n");
	else if ((to_form = search_block(arg, forms, FALSE)) < 0)
		msg_to_char(ch, "The only valid forms are Homid, Glabro, Crinos, Hispo, and Lupus!\r\n");
	else if ((to_form = form_morphs[to_form]) == GET_MORPH(ch))
		msg_to_char(ch, "You're already in that form!\r\n");
	else if (to_form == GET_BREED_FORM(ch)) {
		sprintf(buf, "%s becomes $n!", PERS(ch, ch, 0));

		perform_morph(ch, to_form);

		act(buf, TRUE, ch, 0, 0, TO_ROOM);
		act("You shift into $n!", FALSE, ch, 0, 0, TO_CHAR);
		}
	else if (GET_ACTION(ch) != ACT_NONE)
		msg_to_char(ch, "You're busy right now.\r\n");
	else if (ww_dice(GET_STAMINA(ch) + GET_PRIMAL_URGE(ch), 6) < 2) {
		msg_to_char(ch, "You try to shift, but lack the link to your primal side!\r\n");
		WAIT_STATE(ch, PULSE_VIOLENCE * 2);
		}
	else {
		GET_ACTION(ch) = ACT_MORPHING;
		GET_ACTION_ROOM(ch) = NOWHERE;
		GET_ACTION_VNUM(ch, 0) = to_form;
		GET_ACTION_TIMER(ch) = 3;
		GET_ACTION_ROTATION(ch) = last_action_rotation;

		msg_to_char(ch, "You begin to shift..\r\n");
		}
	}