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: utils.c                                        EmpireMUD AD 1.0 *
*  Usage: various internal functions of a utility nature                  *
*                                                                         *
*  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 "db.h"
#include "comm.h"
#include "handler.h"
#include "interpreter.h"
#include "skills.h"
#include "empire.h"
#include "vnums.h"


extern struct time_data time_info;

int last_action_rotation = 0;


/* creates a random number in interval [from;to] */
int number(int from, int to) {
	unsigned long empire_random();

	/* error checking in case people call number() incorrectly */
	if (from > to) {
		int tmp = from;
		from = to;
		to = tmp;
		}

	return ((empire_random() % (to - from + 1)) + from);
	}


/* simulates dice roll */
int dice(int number, int size) {
	unsigned long empire_random();

	int sum = 0;

	if (size <= 0 || number <= 0)
		return (0);

	while (number-- > 0)
		sum += ((empire_random() % size) + 1);

	return (sum);
	}


/*
 * simulates White Wolf 10-sided dice roll
 *  It rolls num 10-sided dice and returns the number
 *  of dice with results >= diff.  diff is, by default,
 *  max 10, min 2.  Each result of 1 subtracts one
 *  "success" so the function may return 0 or a negative
 *  number
 */
int ww_dice(int num, int diff) {
	int result = 0;
	int i, n;

	for (i = 0; i < num; i++)
		if ((n = number(1, 13)) == 1)
			result -= 1;
		else if (((n) > 10 ? (n - 5) : (n)) >= MIN(10, MAX(2, diff)))
			result += 1;

	return (result);
	}


char *CAP(char *txt) {
	*txt = UPPER(*txt);
	return (txt);
	}


/* Create a duplicate of a string */
char *str_dup(const char *source) {
	char *new_z;

	CREATE(new_z, char, strlen(source) + 1);
	return (strcpy(new_z, source));
	}


/*
 * Strips \r\n from end of string.
 */
void prune_crlf(char *txt) {
	int i = strlen(txt) - 1;

	while (txt[i] == '\n' || txt[i] == '\r')
		txt[i--] = '\0';
	}


/*
 * str_cmp: a case-insensitive version of strcmp().
 * Returns: 0 if equal, > 0 if arg1 > arg2, or < 0 if arg1 < arg2.
 *
 * Scan until strings are found different or we reach the end of both.
 */
int str_cmp(const char *arg1, const char *arg2) {
	int chk, i;

	if (arg1 == NULL || arg2 == NULL) {
		log("SYSERR: str_cmp() passed a NULL pointer, %p or %p.", arg1, arg2);
		return (0);
		}

	for (i = 0; arg1[i] || arg2[i]; i++)
		if ((chk = LOWER(arg1[i]) - LOWER(arg2[i])) != 0)
			return (chk);	/* not equal */

	return (0);
	}


/*
 * strn_cmp: a case-insensitive version of strncmp().
 * Returns: 0 if equal, > 0 if arg1 > arg2, or < 0 if arg1 < arg2.
 *
 * Scan until strings are found different, the end of both, or n is reached.
 */
int strn_cmp(const char *arg1, const char *arg2, int n) {
	int chk, i;

	if (arg1 == NULL || arg2 == NULL) {
		log("SYSERR: strn_cmp() passed a NULL pointer, %p or %p.", arg1, arg2);
		return (0);
		}

	for (i = 0; (arg1[i] || arg2[i]) && (n > 0); i++, n--)
		if ((chk = LOWER(arg1[i]) - LOWER(arg2[i])) != 0)
			return (chk);	/* not equal */

	return (0);
	}

/*
 * str_str: a case-insensitive version of strstr().
 * One note is that it doesn't return a pointer like strstr, just
 * 1 or 0 where 1 is found, 0 is not
 */
bool str_str(const char *arg1, const char *arg2) {
	char *new1, *new2;
	int i;

	if (!arg1 || !arg2 || !*arg1 || !*arg2)
		return 0;

	CREATE(new1, char, strlen(arg1) + 1);
	strncpy(new1, arg1, strlen(arg1));
	CREATE(new2, char, strlen(arg2) + 1);
	strncpy(new2, arg2, strlen(arg2));

	for (i = 0; i < strlen(new1); i++)
		new1[i] = LOWER(new1[i]);

	for (i = 0; i < strlen(new2); i++)
		new2[i] = LOWER(new2[i]);

	if (strstr(new1, new2))
		return 1;
	return 0;
	}
	

/*
 * New variable argument log() function.  Works the same as the old for
 * previously written code but is very nice for new code.
 */
void basic_mud_log(const char *format, ...) {
	va_list args;
	time_t ct = time(0);
	char *time_s = asctime(localtime(&ct));

	if (logfile == NULL)
		puts("SYSERR: Using log() before stream was initialized!");
	if (format == NULL)
		format = "SYSERR: log() received a NULL format.";

	time_s[strlen(time_s) - 1] = '\0';

	fprintf(logfile, "%-15.15s :: ", time_s + 4);

	va_start(args, format);
	vfprintf(logfile, format, args);
	va_end(args);

	fprintf(logfile, "\n");
	fflush(logfile);
	}


/* the "touch" command, essentially. */
int touch(const char *path) {
	FILE *fl;

	if (!(fl = fopen(path, "a"))) {
		log("SYSERR: %s: %s", path, strerror(errno));
		return (-1);
		}
	else {
		fclose(fl);
		return (0);
		}
	}


void syslog(int level, bool file, const char *str, ...) {
	char output[MAX_STRING_LENGTH];
	Descr i;
	va_list tArgList;

	if (!str)
		return;

	va_start(tArgList, str);
	vsprintf(output, str, tArgList);

	if (file)
		log(output);

	for (i = descriptor_list; i; i = i->next) {
		if (STATE(i) != CON_PLAYING || IS_NPC(i->character)) /* switch */
			continue;
		if (PLR_FLAGGED(i->character, PLR_WRITING))
			continue;
		if (GET_LEVEL(i->character) < LVL_START_IMM)
			continue;
		if (GET_LEVEL(i->character) < level)
			continue;

		msg_to_char(i->character, "&2[ %s ]&0\r\n", output);
		}
	}


void mortlog(Creature ch, const char *str, ...) {
	char output[MAX_STRING_LENGTH];
	Descr i;
	va_list tArgList;

	if (!str)
		return;

	va_start(tArgList, str);
	vsprintf(output, str, tArgList);

	for (i = descriptor_list; i; i = i->next) {
		if (STATE(i) != CON_PLAYING || IS_NPC(i->character)) /* switch */
			continue;
		if (PLR_FLAGGED(i->character, PLR_WRITING))
			continue;
		if (!PRF_FLAGGED(i->character, PRF_MORTLOG))
			continue;

		msg_to_char(i->character, "&6[ %s ]&0\r\n", output);
		}
	va_end(tArgList);
	}


/*
 * If you don't have a 'const' array, just cast it as such.  It's safer
 * to cast a non-const array as const than to cast a const one as non-const.
 * Doesn't really matter since this function doesn't change the array though.
 */
void sprintbit(bitvector_t bitvector, const char *names[], char *result, byte space) {
	long nr;

	*result = '\0';

	for (nr = 0; bitvector; bitvector >>= 1) {
		if (IS_SET(bitvector, 1)) {
			if (*names[nr] != '\n') {
				strcat(result, names[nr]);
				if (space)
					strcat(result, " ");
				}
			else
				strcat(result, "UNDEFINED ");
			}
		if (*names[nr] != '\n')
			nr++;
		}

	if (!*result && space)
		strcpy(result, "NOBITS ");
	}


void sprinttype(int type, const char *names[], char *result) {
	int nr = 0;

	while (type && *names[nr] != '\n') {
		type--;
		nr++;
		}

	if (*names[nr] != '\n')
		strcpy(result, names[nr]);
	else
		strcpy(result, "UNDEFINED");
	}


/* Calculate the REAL time passed over the last t2-t1 centuries (secs) */
struct time_info_data *real_time_passed(time_t t2, time_t t1) {
	long secs;
	static struct time_info_data now;

	secs = (long) (t2 - t1);

	now.hours = (secs / SECS_PER_REAL_HOUR) % 24;	/* 0..23 hours */
	secs -= SECS_PER_REAL_HOUR * now.hours;

	now.day = (secs / SECS_PER_REAL_DAY);	/* 0..29 days  */
	/* secs -= SECS_PER_REAL_DAY * now.day; - Not used. */

	now.month = -1;
	now.year = -1;

	return (&now);
	}


/* Calculate the MUD time passed over the last t2-t1 centuries (secs) */
struct time_info_data *mud_time_passed(time_t t2, time_t t1) {
	long secs;
	static struct time_info_data now;

	secs = (long) (t2 - t1);

	now.hours = (secs / SECS_PER_MUD_HOUR) % 24;	/* 0..23 hours */
	secs -= SECS_PER_MUD_HOUR * now.hours;

	now.day = (secs / SECS_PER_MUD_DAY) % 30;		/* 0..29 days  */
	secs -= SECS_PER_MUD_DAY * now.day;

	now.month = (secs / SECS_PER_MUD_MONTH) % 12;	/* 0..11 months */
	secs -= SECS_PER_MUD_MONTH * now.month;

	now.year = YEAR_ADD + (secs / SECS_PER_MUD_YEAR);		/* YEAR_ADD..XX? years */

	return (&now);
	}


struct time_info_data *age(Creature ch) {
	static struct time_info_data player_age;

	player_age = *mud_time_passed(time(0), ch->player.time.birth);

	player_age.year += 17;	/* All players start at 17 */
	player_age.year -= YEAR_ADD;

	return (&player_age);
	}


/* Check if making CH follow VICTIM will create an illegal */
/* Follow "Loop/circle"                                    */
bool circle_follow(Creature ch, Creature victim) {
	Creature k;

	for (k = victim; k; k = k->master) {
		if (k == ch)
			return (TRUE);
		}

	return (FALSE);
	}


/* Called when stop following persons, or stopping charm */
/* This will NOT do if a character quits/dies!!          */
void stop_follower(Creature ch) {
	struct follow_type *j, *k;

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

	act("You stop following $N.", FALSE, ch, 0, ch->master, TO_CHAR);
	act("$n stops following $N.", TRUE, ch, 0, ch->master, TO_NOTVICT);
	act("$n stops following you.", TRUE, ch, 0, ch->master, TO_VICT);

	if (ch->master->followers->follower == ch) {	/* Head of follower-list? */
		k = ch->master->followers;
		ch->master->followers = k->next;
		free(k);
		}
	else {			/* locate follower who is not head of list */
		for (k = ch->master->followers; k->next->follower != ch; k = k->next);

		j = k->next;
		k->next = j->next;
		free(j);
		}

	ch->master = NULL;
	REMOVE_BIT(AFF_FLAGS(ch), AFF_CHARM | AFF_PARTY);
	}


/* Called when a character that follows/is followed dies */
void die_follower(Creature ch) {
	struct follow_type *j, *k;

	if (ch->master)
		stop_follower(ch);

	for (k = ch->followers; k; k = j) {
		j = k->next;
		stop_follower(k->follower);
		}
	}


/*
 * Do NOT call this before having checked if a circle of followers
 * will arise. CH will follow leader
 */
void add_follower(Creature ch, Creature leader) {
	struct follow_type *k;

	if (ch->master)
		return;

	ch->master = leader;

	CREATE(k, struct follow_type, 1);

	k->follower = ch;
	k->next = leader->followers;
	leader->followers = k;

	act("You now follow $N.", FALSE, ch, 0, leader, TO_CHAR);
	if (CAN_SEE(leader, ch))
		act("$n starts following you.", TRUE, ch, 0, leader, TO_VICT);
	act("$n starts to follow $N.", TRUE, ch, 0, leader, TO_NOTVICT);
	}


/*
 * get_line reads the next non-blank line off of the input stream.
 * The newline character is removed from the input.  Lines which begin
 * with '*' are considered to be comments.
 *
 * Returns the number of lines advanced in the file.
 */
int get_line(FILE *fl, char *buf) {
	char temp[256];
	int lines = 0;

	do {
		fgets(temp, 256, fl);
		if (feof(fl))
			return (0);
		lines++;
		} while (*temp == '*' || *temp == '\n');

	temp[strlen(temp) - 1] = '\0';
	strcpy(buf, temp);
	return (lines);
	}


int get_filename(char *orig_name, char *filename, int mode) {
	const char *prefix, *middle, *suffix;
	char name[64], *ptr;

	if (orig_name == NULL || *orig_name == '\0' || filename == NULL) {
		log("SYSERR: NULL pointer or empty string passed to get_filename(), %p or %p.", orig_name, filename);
		return (0);
		}

	switch (mode) {
		case CRASH_FILE:
			prefix = LIB_PLROBJS;
			suffix = SUF_OBJS;
			break;
		case ALIAS_FILE:
			prefix = LIB_PLRALIAS;
			suffix = SUF_ALIAS;
			break;
		case ETEXT_FILE:
			prefix = LIB_PLRTEXT;
			suffix = SUF_TEXT;
			break;
		case REC_FILE:
			prefix = LIB_PLRREC;
			suffix = SUF_REC;
			break;
		case LORE_FILE:
			prefix = LIB_PLRLORE;
			suffix = SUF_LORE;
			break;
		default:
			return (0);
		}

	strcpy(name, orig_name);
	for (ptr = name; *ptr; ptr++)
		*ptr = LOWER(*ptr);

	if (LOWER(*name) <= 'e')
		middle = "A-E";
	else if (LOWER(*name) <= 'j')
		middle = "F-J";
	else if (LOWER(*name) <= 'o')
		middle = "K-O";
	else if (LOWER(*name) <= 't')
		middle = "P-T";
	else if (LOWER(*name) <= 'z')
		middle = "U-Z";
	else
		middle = "ZZZ";

	/* If your compiler gives you shit about <= '', use this switch:
		switch (LOWER(*name)) {
			case 'a':  case 'b':  case 'c':  case 'd':  case 'e':
				middle = "A-E";
				break;
			case 'f':  case 'g':  case 'h':  case 'i':  case 'j':
				middle = "F-J";
				break;
			case 'k':  case 'l':  case 'm':  case 'n':  case 'o':
				middle = "K-O";
				break;
			case 'p':  case 'q':  case 'r':  case 's':  case 't':
				middle = "P-T";
				break;
			case 'u':  case 'v':  case 'w':  case 'x':  case 'y':  case 'z':
				middle = "U-Z";
				break;
			default:
				middle = "ZZZ";
				break;
			}
	*/

	sprintf(filename, "%s%s/%s.%s", prefix, middle, name, suffix);
	return (1);
	}


int num_pc_in_room(Room room) {
	int i = 0;
	Creature ch;

	for (ch = room->people; ch != NULL; ch = ch->next_in_room)
		if (!IS_NPC(ch))
			i++;

	return (i);
	}


char *PERS(Creature ch, Creature vict, bool real) {
	static char output[MAX_INPUT_LENGTH];

	if (!CAN_SEE(vict, ch))
		return "someone";

	if (!IS_NPC(ch) && GET_MORPH(ch) && !real)
		return morph_string(ch, MORPH_STRING_NAME);

	strcpy(output, GET_NAME(ch));
	if (!IS_NPC(ch) && GET_LASTNAME(ch))
		sprintf(output + strlen(output), " %s", GET_LASTNAME(ch));

	return output;
	}


int shift_room(int origin, int x, int y) {
	int o = origin;

	if (X_COORD(origin) + x < 0)
		o = origin + x + MAP_WIDTH;
	else if (X_COORD(origin) + x >= MAP_WIDTH)
		o = origin + x - MAP_WIDTH;
	else
		o = origin + x;

	if ((o + y * MAP_HEIGHT) >= MAP_SIZE)
		o = o + y * MAP_HEIGHT - MAP_SIZE;
	else if ((o + y * MAP_HEIGHT) < 0)
		o = o + y * MAP_HEIGHT + MAP_WIDTH * MAP_HEIGHT;
	else
		o = o + y * MAP_HEIGHT;

	if (o < 0)
		return 0;

	return o;
	}


/* Return resources? */
void cancel_action(Creature ch) {
	void cancel_forging(Creature ch);
	void cancel_melting(Creature ch);

	switch (GET_ACTION(ch)) {
		case ACT_FORGING:
			cancel_forging(ch);
			break;
		case ACT_MILLING:
			obj_to_char(read_object(o_WHEAT, VIRTUAL), ch);
			break;
		case ACT_MELTING:
			cancel_melting(ch);
			break;
		case ACT_CRAFTING:
			obj_to_char(read_object(GET_ACTION_VNUM(ch, 0), VIRTUAL), ch);
			break;
		case ACT_SCRAPING:
			obj_to_char(read_object(o_TREE, VIRTUAL), ch);
			break;
		case ACT_CHIPPING:
			obj_to_char(read_object(GET_ACTION_VNUM(ch, 0), VIRTUAL), ch);
			break;
		}

	GET_ACTION(ch) = ACT_NONE;
	}


void update_actions(void) {
	void finish_forging(Creature ch);
	void finish_melting(Creature ch);
	void process_dismantling(Creature ch, room_rnum room);
	void process_build(Creature ch, room_rnum room);
	void process_manufacturing(Creature ch);
	void cancel_forging(Creature ch);
	void embrace_char(Creature ch, Creature victim);
	extern Object make_corpse(Creature ch);

	extern const char *crops[];
	extern const int dam_type[];

	Descr d;
	Creature ch, c;
	Object obj = NULL, obj2 = NULL;
	int i, j, vnum = NOTHING, count;
	bool found = FALSE;

	last_action_rotation++;
	if (last_action_rotation > 4)
		last_action_rotation = 0;

	for (d = descriptor_list; d; d = d->next) {
		if (STATE(d) != CON_PLAYING || !(ch = d->character) || IS_NPC(ch) || GET_ACTION(ch) == ACT_NONE)
			continue;
		if ((GET_ACTION_ROOM(ch) != ch->in_room && GET_ACTION_ROOM(ch) != NOWHERE) || ((GET_FEEDING_FROM(ch) || GET_FED_ON_BY(ch)) && GET_ACTION(ch) != ACT_EMBRACE) || FIGHTING(ch) || GET_POS(ch) < POS_STANDING || IS_WRITING(ch)) {
			cancel_action(ch);
			continue;
			}
		if (GET_ACTION_ROTATION(ch) != last_action_rotation)
			continue;

		switch (GET_ACTION(ch)) {
			case ACT_BATHING:
				if (GET_DAYS_SINCE_BATHING(ch) > 0) {
					if (SECT(ch->in_room) == SECT_RIVER && !number(0, 2))
						GET_DAYS_SINCE_BATHING(ch) -= 1;
					else if ((BUILDING_TYPE(ch->in_room) == BUILDING_BATHS || ROOM_TYPE(ch->in_room) == RTYPE_BATHS) && !number(0, 1))
						GET_DAYS_SINCE_BATHING(ch) -= 1;
					}

				if (GET_DAYS_SINCE_BATHING(ch) == 0 && !IS_GOD(ch) && !IS_IMMORTAL(ch)) {
					msg_to_char(ch, "You finish bathing and climb out of the water to dry off.\r\n");
					act("$n finishes bathing and climbs out of the water to dry off.", FALSE, ch, 0, 0, TO_ROOM);
					GET_ACTION(ch) = ACT_NONE;
					}
				else {
					switch(number(0, 2)) {
						case 0:
							msg_to_char(ch, "You wash yourself off...\r\n");
							act("$n washes $mself carefully...", FALSE, ch, 0, 0, TO_ROOM);
							break;
						case 1:
							msg_to_char(ch, "You scrub your hair to get out any dirt and insects...\r\n");
							act("$n scrubs $s hair to get out any dirt and insects...", FALSE, ch, 0, 0, TO_ROOM);
							break;
						case 2:
							msg_to_char(ch, "You swim through the water...\r\n");
							act("$n swims through the water...", FALSE, ch, 0, 0, TO_ROOM);
							break;
						}
					}
				break;
			case ACT_EMBRACE:
				if (!GET_FEEDING_FROM(ch))
					GET_ACTION(ch) = ACT_NONE;
				else if (GET_BLOOD(GET_FEEDING_FROM(ch)) <= 0)
					embrace_char(ch, GET_FEEDING_FROM(ch));
				break;
			case ACT_DIGGING:
				vnum = NOTHING;

				if (GET_EQ(ch, WEAR_WIELD) && GET_OBJ_VNUM(GET_EQ(ch, WEAR_WIELD)) == f_SHOVEL)
					found = TRUE;
				else if (GET_EQ(ch, WEAR_HOLD) && GET_OBJ_VNUM(GET_EQ(ch, WEAR_HOLD)) == f_SHOVEL)
					found = TRUE;

				GET_ACTION_TIMER(ch) -= ww_dice(GET_STRENGTH(ch) + GET_ATHLETICS(ch) + found, 6);
				if (GET_ACTION_TIMER(ch) <= 0) {
					GET_ACTION(ch) = 0;
					if (vnum == NOTHING)
						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)
								if (!number(0, 3)) {
									vnum = o_CLAY;
									break;
									}
					if (vnum == NOTHING)
						vnum = !number(0, 10) ? o_FLINT : o_ROCK;
					obj = read_object(vnum, VIRTUAL);
					if (IS_CARRYING_N(ch) >= CAN_CARRY_N(ch) || GET_OBJ_WEIGHT(obj) + IS_CARRYING_W(ch) > CAN_CARRY_W(ch))
						obj_to_room(obj, ch->in_room);
					else
						obj_to_char(obj, ch);
					act("You pull $p from the ground!", FALSE, ch, obj, 0, TO_CHAR);
					act("$n pulls $p from the ground!", FALSE, ch, obj, 0, TO_ROOM);
					break;
					}
				send_to_char("You dig vigorously at the ground.\r\n", ch);
				act("$n digs vigorously at the ground.", FALSE, ch, 0, 0, TO_ROOM);
				break;
			case ACT_MORPHING:
				GET_ACTION_TIMER(ch) -= 1;

				if (GET_ACTION_TIMER(ch) <= 0) {
					sprintf(buf, "%s has become $n!", PERS(ch, ch, 0));

					perform_morph(ch, (ubyte) GET_ACTION_VNUM(ch, 0));

					act(buf, TRUE, ch, 0, 0, TO_ROOM);
					act("You have become $n!", FALSE, ch, 0, 0, TO_CHAR);

					GET_ACTION(ch) = ACT_NONE;
					}
				else {
					msg_to_char(ch, "Your body warps and distorts painfully!\r\n");
					act("$n's body warps and distorts hideously!", TRUE, ch, 0, 0, TO_ROOM);
					}
				break;
			case ACT_GATHERING:
				send_to_char("You search the ground for sticks...\r\n", ch);
				act("$n searches around on the ground...", TRUE, ch, 0, 0, TO_ROOM);
				GET_ACTION_TIMER(ch) -= ww_dice(GET_PERCEPTION(ch) + GET_ALERTNESS(ch), 5 - SENSES_BONUS(ch));
				if (GET_ACTION_TIMER(ch) <= 0) {
					if (SECT(ch->in_room) == SECT_FOREST_1 || SECT(ch->in_room) == SECT_FOREST_2 || SECT(ch->in_room) == SECT_FOREST_3 || SECT(ch->in_room) == SECT_FOREST_4 || (SECT(ch->in_room) == SECT_CROP && world[ch->in_room].type == CROP_FRUIT))
						GET_ACTION_TIMER(ch) = number(5, 9);
					else
						GET_ACTION(ch) = ACT_NONE;
					obj = read_object(o_STICK, VIRTUAL);
					if (IS_CARRYING_N(ch) >= CAN_CARRY_N(ch) || GET_OBJ_WEIGHT(obj) + IS_CARRYING_W(ch) > CAN_CARRY_W(ch))
						obj_to_room(obj, ch->in_room);
					else
						obj_to_char(obj, ch);
					act("You find $p!", FALSE, ch, obj, 0, TO_CHAR);
					act("$n finds $p!", TRUE, ch, obj, 0, TO_ROOM);
					break;
					}
				break;
			case ACT_PICKING:
				if (SECT(ch->in_room) == SECT_CROP && world[ch->in_room].type == CROP_FRUIT) {
					send_to_char("You search the trees for apples...\r\n", ch);
					act("$n searches the trees for apples...", TRUE, ch, 0, 0, TO_ROOM);
					}
				else {
					send_to_char("You search the ground for nice flowers...\r\n", ch);
					act("$n searches around the ground...", TRUE, ch, 0, 0, TO_ROOM);
					}
					
				GET_ACTION_TIMER(ch) -= ww_dice(GET_PERCEPTION(ch) + GET_ALERTNESS(ch), 5 - SENSES_BONUS(ch));
				if (GET_ACTION_TIMER(ch) <= 0) {
					if (SECT(ch->in_room) == SECT_FIELD || (SECT(ch->in_room) == SECT_CROP && world[ch->in_room].type == CROP_FRUIT))
						GET_ACTION_TIMER(ch) = number(5, 9);
					else
						GET_ACTION(ch) = ACT_NONE;

					if (SECT(ch->in_room) == SECT_CROP && world[ch->in_room].type == CROP_FRUIT)
						obj = read_object(o_APPLES, VIRTUAL);
					else
						obj = read_object(o_FLOWER, VIRTUAL);

					if (IS_CARRYING_N(ch) >= CAN_CARRY_N(ch) || GET_OBJ_WEIGHT(obj) + IS_CARRYING_W(ch) > CAN_CARRY_W(ch))
						obj_to_room(obj, ch->in_room);
					else
						obj_to_char(obj, ch);
					act("You find $p!", FALSE, ch, obj, 0, TO_CHAR);
					act("$n finds $p!", TRUE, ch, obj, 0, TO_ROOM);
					break;
					}
				break;
			case ACT_CRAFTING:
				if ((!(obj = GET_EQ(ch, WEAR_WIELD)) || dam_type[GET_OBJ_VAL(obj, 2)] < DAM_LETHAL) && (obj || !(obj = GET_EQ(ch, WEAR_HOLD)) || dam_type[GET_OBJ_VAL(obj, 2)] < DAM_LETHAL)) {
					msg_to_char(ch, "You need to be using a sharp tool to scrape it.\r\n");
					obj_to_char(read_object(GET_ACTION_VNUM(ch, 0), VIRTUAL), ch);
					GET_ACTION(ch) = ACT_NONE;
					break;
					}

				msg_to_char(ch, "You scrape at %s...\r\n", GET_OBJ_NAME_BY_PROTO(real_object(GET_ACTION_VNUM(ch, 0))));
				GET_ACTION_TIMER(ch) -= ww_dice(GET_DEXTERITY(ch) + GET_CRAFTS(ch), 5);

				if (GET_ACTION_TIMER(ch) <= 0) {
					GET_ACTION(ch) = 0;
					obj_to_char((obj = read_object(GET_ACTION_VNUM(ch, 1), VIRTUAL)), ch);
					act("You craft $p!", FALSE, ch, obj, 0, TO_CHAR);
					act("$n crafts $p!", TRUE, ch, obj, 0, TO_ROOM);
					}
				break;
			case ACT_SCRAPING:
				if ((!(obj = GET_EQ(ch, WEAR_WIELD)) || dam_type[GET_OBJ_VAL(obj, 2)] < DAM_LETHAL) && (obj || !(obj = GET_EQ(ch, WEAR_HOLD)) || dam_type[GET_OBJ_VAL(obj, 2)] < DAM_LETHAL)) {
					msg_to_char(ch, "You need to be using a sharp tool to scrape it.\r\n");
					obj_to_char(read_object(o_TREE, VIRTUAL), ch);
					GET_ACTION(ch) = ACT_NONE;
					break;
					}

				msg_to_char(ch, "You scrape at %s...\r\n", GET_OBJ_NAME_BY_PROTO(real_object(o_TREE)));
				GET_ACTION_TIMER(ch) -= 1;

				if (GET_ACTION_TIMER(ch) <= 0) {
					GET_ACTION(ch) = 0;
					obj_to_char((obj = read_object(o_LOG, VIRTUAL)), ch);
					for (i = 0; i < number(2, 5); i++)
						obj_to_char((obj2 = read_object(o_STICK, VIRTUAL)), ch);
					sprintf(buf, "You finish scraping off $p and manage to get $P (%dx)!", i);
					act(buf, FALSE, ch, obj, obj2, TO_CHAR);
					act("$n finishes scraping off $p!", TRUE, ch, obj, 0, TO_ROOM);
					}
				break;

			case ACT_CHIPPING:
				if ((!(obj = GET_EQ(ch, WEAR_WIELD)) || GET_OBJ_VAL(obj, 2) != TYPE_HAMMER) && (obj || !(obj = GET_EQ(ch, WEAR_HOLD)) || GET_OBJ_VAL(obj, 2) != TYPE_HAMMER)) {
					msg_to_char(ch, "You need to be using some kind of hammer to chip it.\r\n");
					obj_to_char(read_object(GET_ACTION_VNUM(ch, 0), VIRTUAL), ch);
					GET_ACTION(ch) = ACT_NONE;
					break;
					}

				msg_to_char(ch, "You chip away at the piece of rock...\r\n");
				GET_ACTION_TIMER(ch) -= ww_dice(GET_STRENGTH(ch) + GET_CRAFTS(ch), 5);

				if (GET_ACTION_TIMER(ch) <= 0) {
					GET_ACTION(ch) = 0;
					switch (GET_ACTION_VNUM(ch, 0)) {
						case o_ROCK:
							obj_to_char((obj = read_object(o_CHIPPED, VIRTUAL)), ch);
							msg_to_char(ch, "It splits open!\r\n");
							break;
						case o_CHIPPED:
							obj_to_char((obj = read_object(o_HANDAXE, VIRTUAL)), ch);
							act("You have crafted $p!", FALSE, ch, obj, 0, TO_CHAR);
							break;
						case o_HANDAXE:
							obj_to_char((obj = read_object(o_SPEARHEAD, VIRTUAL)), ch);
							act("You have crafted $p!", FALSE, ch, obj, 0, TO_CHAR);
							break;
						}
					}
				break;

			case ACT_CHOPPING:
				for (count = 0; count < 1 + ATTACK_BONUS(ch) && GET_ACTION(ch) == ACT_CHOPPING; count++) {
					if (!GET_EQ(ch, WEAR_WIELD) || GET_OBJ_VAL(GET_EQ(ch, WEAR_WIELD), 2) != TYPE_SLICE) {
						send_to_char("You need to be using an axe to chop.\r\n", ch);
						GET_ACTION(ch) = ACT_NONE;
						break;
						}

					if ((i = ww_dice(GET_DEXTERITY(ch) + GET_MELEE(ch), 6)) > 0) {
						GET_BUILD_VALUE(ch->in_room) -= (ww_dice(GET_STRENGTH(ch) + GET_OBJ_VAL(GET_EQ(ch, WEAR_WIELD), 1) + i, 6) + GET_POTENCE(ch));
						act("You swing $p hard into the tree!", FALSE, ch, GET_EQ(ch, WEAR_WIELD), 0, TO_CHAR);
						act("$n swings $p hard into the tree!", FALSE, ch, GET_EQ(ch, WEAR_WIELD), 0, TO_ROOM);
						}
					else {
						act("You swing $p, but miss the tree!", FALSE, ch, GET_EQ(ch, WEAR_WIELD), 0, TO_CHAR);
						act("$n swings $p, but misses the tree!", FALSE, ch, GET_EQ(ch, WEAR_WIELD), 0, TO_ROOM);
						}

					if (GET_BUILD_VALUE(ch->in_room) > 0)
						continue;
					GET_BUILD_VALUE(ch->in_room) = 0;
					for (c = world[ch->in_room].people; c; c = c->next_in_room)
						if (!IS_NPC(c) && GET_ACTION(c) == ACT_CHOPPING)
							GET_ACTION(c) = ACT_NONE;
					obj_to_room(read_object(o_TREE, VIRTUAL), ch->in_room);

					SECT(ch->in_room)--;
					if (SECT(ch->in_room) < SECT_FIELD) {
						SECT(ch->in_room) = SECT_FIELD;
						act("With a loud crack, it falls to the ground and the area is depleted!", FALSE, ch, 0, 0, TO_CHAR);
						act("With a loud crack, it falls to the ground and the area is depleted!", FALSE, ch, 0, 0, TO_ROOM);
						}
					else {
						act("With a loud crack, it falls to the ground!", FALSE, ch, 0, 0, TO_CHAR);
						act("With a loud crack, it falls to the ground!", FALSE, ch, 0, 0, TO_ROOM);
						}

					if (world[ch->in_room].type2 && SECT(ch->in_room) == SECT_FIELD) {
						SECT(ch->in_room) = SECT_DESERT;
						world[ch->in_room].type2 = 0;
						}
					}

				break;
			case ACT_BUILDING:
				for (count = 0; count < 1 + ATTACK_BONUS(ch) && GET_ACTION(ch) == ACT_BUILDING; count++)
					process_build(ch, ch->in_room);
				break;
			case ACT_DISMANTLING:
				for (count = 0; count < 1 + ATTACK_BONUS(ch) && GET_ACTION(ch) == ACT_DISMANTLING; count++)
					process_dismantling(ch, ch->in_room);
				break;
			case ACT_HARVESTING:
				if (!GET_EQ(ch, WEAR_WIELD) || (GET_OBJ_VAL(GET_EQ(ch, WEAR_WIELD), 2) != TYPE_SLICE && GET_OBJ_VAL(GET_EQ(ch, WEAR_WIELD), 2) != TYPE_SLASH)) {
					send_to_char("You're not using the proper tool for harvesting.\r\n", ch);
					GET_ACTION(ch) = ACT_NONE;
					break;
					}

				switch(number(0, 2)) {
					case 0:
						msg_to_char(ch, "You walk through the field, harvesting the %s.\r\n", crops[(int) world[ch->in_room].type]);
						sprintf(buf, "$n walks through the field, harvesting the %s.", crops[(int) world[ch->in_room].type]);
						act(buf, FALSE, ch, 0, 0, TO_ROOM);
						break;
					case 1:
						msg_to_char(ch, "You carefully harvest the %s.\r\n", crops[(int) world[ch->in_room].type]);
						sprintf(buf, "$n carefully harvests the %s.", crops[(int) world[ch->in_room].type]);
						act(buf, FALSE, ch, 0, 0, TO_ROOM);
						break;
					}
				GET_BUILD_VALUE(ch->in_room) -= ww_dice(GET_DEXTERITY(ch) + GET_MELEE(ch), 6);

				if (GET_BUILD_VALUE(ch->in_room) > 0)
					break;
				GET_BUILD_VALUE(ch->in_room) = 0;
				for (c = world[ch->in_room].people; c; c = c->next_in_room)
					if (!IS_NPC(c) && GET_ACTION(c) == ACT_HARVESTING)
						GET_ACTION(c) = ACT_NONE;
				act("You finish harvesting the crop!", FALSE, ch, 0, 0, TO_CHAR);
				act("$n finished harvesting the crop!", FALSE, ch, 0, 0, TO_ROOM);

				switch (world[ch->in_room].type) {
					case CROP_FRUIT:	vnum = o_APPLES;				break;
					case CROP_WHEAT:	vnum = o_WHEAT;					break;
					case CROP_CORN:		vnum = o_CORN;					break;
					default:			vnum = o_APPLES;				break;
					}
				j = number(1, 5);

				/* Give three trees for harvesting an orchard */
				if(world[ch->in_room].type == CROP_FRUIT){
					for(i =0; i<=3; i++){
						obj_to_room(read_object(o_TREE, VIRTUAL), ch->in_room);
					}
				}
				for (i = 0; i <= j; i++)
					obj_to_room(read_object(vnum, VIRTUAL), ch->in_room);

				SECT(ch->in_room) = SECT_FIELD;
				world[ch->in_room].type = 0;

				break;
			case ACT_PLANTING:
				GET_ACTION_TIMER(ch)--;
				if (GET_ACTION_TIMER(ch))
					GET_BUILD_VALUE(ch->in_room) /= 2;
				else {
					msg_to_char(ch, "You finish planting!\r\n");
					act("$n finishes planting!", FALSE, ch, 0, 0, TO_ROOM);
					GET_ACTION(ch) = ACT_NONE;
					}
				break;
			case ACT_MINING:
				for (count = 0; count < 1 + ATTACK_BONUS(ch) && GET_ACTION(ch) == ACT_MINING; count++) {
					if (!GET_EQ(ch, WEAR_WIELD) || ((GET_OBJ_VAL(GET_EQ(ch, WEAR_WIELD), 2) != TYPE_PICK) && GET_OBJ_VNUM(GET_EQ(ch, WEAR_WIELD)) != o_HANDAXE)) {
						send_to_char("You're not using the proper tool for mining!\r\n", ch);
						GET_ACTION(ch) = ACT_NONE;
						break;
						}
					GET_ACTION_TIMER(ch) -= ww_dice(GET_STRENGTH(ch) + GET_ATHLETICS(ch), 6) + GET_POTENCE(ch);
					act("You pick at the walls with $p, looking for ore.", FALSE, ch, GET_EQ(ch, WEAR_WIELD), 0, TO_CHAR);
					act("$n picks at the walls with $p, looking for ore.", FALSE, ch, GET_EQ(ch, WEAR_WIELD), 0, TO_ROOM);
					if (GET_ACTION_TIMER(ch) > 0)
						continue;
					switch(world[ch->in_room].type2) {
						case ITEM_MAT_SILVER:		vnum = o_SILVER;	break;
						case ITEM_MAT_IRON:			vnum = o_IRON;		break;
						default:					vnum = o_IRON;		break;
						}
					/* Gold is now found at random *only* */
					if (!number(0, 100))
						vnum = o_GOLD;

					obj = read_object(vnum, VIRTUAL);
					world[ch->in_room].spare -= 1;
					if (IS_CARRYING_N(ch) >= CAN_CARRY_N(ch) || GET_OBJ_WEIGHT(obj) + IS_CARRYING_W(ch) > CAN_CARRY_W(ch))
						obj_to_room(obj, ch->in_room);
					else
						obj_to_char(obj, ch);
					act("With that last stroke, $p falls from the wall!", FALSE, ch, obj, 0, TO_CHAR);
					act("With $s last stroke, $p falls from the wall where $n was picking!", FALSE, ch, obj, 0, TO_ROOM);
					GET_ACTION(ch) = ACT_NONE;
					}
				break;
			case ACT_MILLING:
				GET_ACTION_TIMER(ch) -= ww_dice(GET_INTELLIGENCE(ch) + GET_CRAFTS(ch), 5);
				if (GET_ACTION_TIMER(ch) > 0) {
					if (!number(0, 4)) {
						msg_to_char(ch, "You grind the millstone hard against the grain.\r\n");
						act("$n grinds the millstone hard against the grain.", FALSE, ch, 0, 0, TO_ROOM);
						}
					break;
					}
				GET_ACTION(ch) = 0;
				msg_to_char(ch, "You finish grinding the wheat.\r\n");
				act("$n finishes grinding the wheat.", FALSE, ch, 0, 0, TO_ROOM);
				obj_to_char(read_object(o_FLOUR, VIRTUAL), ch);
				break;
			case ACT_BAKING:
				GET_ACTION_TIMER(ch) -= ww_dice(GET_INTELLIGENCE(ch) + GET_CRAFTS(ch), 5);
				if (GET_ACTION_TIMER(ch) > 0) {
					if (!number(0, 4))
						msg_to_char(ch, "Your bread is baking...\r\n");
					break;
					}
				GET_ACTION(ch) = 0;
				msg_to_char(ch, "You pull the bread from the oven!\r\n");
				act("$n pulls a loaf of bread from the oven!", FALSE, ch, 0, 0, TO_ROOM);
				obj_to_char(read_object(o_BREAD, VIRTUAL), ch);
				break;
			case ACT_FORGING:
				if (!GET_EQ(ch, WEAR_WIELD) || GET_OBJ_VAL(GET_EQ(ch, WEAR_WIELD), 2) != TYPE_HAMMER) {
					send_to_char("You need to be using a hammer to forge.\r\n", ch);
					cancel_forging(ch);
					break;
					}

				GET_ACTION_TIMER(ch) -= ww_dice(GET_DEXTERITY(ch) + GET_CRAFTS(ch), 6);

				if (GET_ACTION_TIMER(ch) <= 0)
					finish_forging(ch);
				else {
					act("You hit the anvil hard with $p!", FALSE, ch, GET_EQ(ch, WEAR_WIELD), 0, TO_CHAR);
					act("$n hits the anvil hard with $p!", FALSE, ch, GET_EQ(ch, WEAR_WIELD), 0, TO_ROOM);
					}
				break;
			case ACT_FISHING:
				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");
					GET_ACTION(ch) = ACT_NONE;
					break;
					}
				GET_ACTION_TIMER(ch) -= ww_dice(GET_DEXTERITY(ch) + GET_ATHLETICS(ch), 6);
				if (GET_ACTION_TIMER(ch) > 0) {
					switch(number(0, 5)) {
						case 0:
							msg_to_char(ch, "A fish darts past you, but you narrowly miss it!\r\n");
							break;
						case 1:
							msg_to_char(ch, "The water waves peacefully, but you don't see any fish..\r\n");
							break;
						case 2:
							msg_to_char(ch, "The fish are jumping off in the distance, but you can't seem to catch one!\r\n");
							break;
						}
					}
				else {
					GET_ACTION(ch) = ACT_NONE;
					char_to_room((c = read_mobile(GET_ACTION_VNUM(ch, 0) == 1 ? OCEAN_FISH : RIVER_FISH, VIRTUAL)), ch->in_room);
					obj_to_char((obj = make_corpse(c)), ch);
					SET_BIT(GET_OBJ_VAL(obj, 2), CORPSE_SKINNED);
					extract_char(c);
					msg_to_char(ch, "A fish darts past you..\r\n");
					act("You jab your spear into the water and when you extract it you find $p on the end!", FALSE, ch, obj, 0, TO_CHAR);
					act("$n jabs $s spear into the water and when $e draws it out, it has $p on the end!", TRUE, ch, obj, 0, TO_ROOM);
					}
				break;
			case ACT_MELTING:
				GET_ACTION_TIMER(ch) -= 1;
				if (GET_ACTION_TIMER(ch) > 0) {
					if (!number(0, 2))
						msg_to_char(ch, "You watch as it melts in the fire.\r\n");
					break;
					}
				finish_melting(ch);
				break;
			case ACT_GLASS:
				GET_ACTION_TIMER(ch) -= 1;
				strcpy(buf1, GET_OBJ_NAME_BY_PROTO(real_object(GET_ACTION_VNUM(ch, 0))));
				switch (GET_ACTION_TIMER(ch)) {
					case 0:
						obj_to_char((obj = read_object(GET_ACTION_VNUM(ch, 0), VIRTUAL)), ch);
						act("You complete $p.", FALSE, ch, obj, 0, TO_CHAR);
						act("$n has made $p.", TRUE, ch, obj, 0, TO_ROOM);
						GET_ACTION(ch) = ACT_NONE;
						break;
					case 1:
						msg_to_char(ch, "You set the glass on the table to cool.\r\n");
						act("$n sets the glass on the table to cool.", TRUE, ch, 0, 0, TO_ROOM);
						break;
					case 2:
						msg_to_char(ch, "You roll the glass into the shape of %s.\r\n", buf1);
						sprintf(buf, "$n rolls the glass into the shape of %s.", buf1);
						act(buf, TRUE, ch, 0, 0, TO_ROOM);
						break;
					case 3:
						msg_to_char(ch, "You blow into the tube and inflate the glass.\r\n");
						act("$n blows into the tube and inflates the glass.", TRUE, ch, 0, 0, TO_ROOM);
						break;
					case 4:
						msg_to_char(ch, "You roll the glass into the shape of a ball.\r\n");
						act("$n rolls the glass into the shape of a ball.", TRUE, ch, 0, 0, TO_ROOM);
						break;
					case 5:
						msg_to_char(ch, "You blow into the tube and inflate the glass.\r\n");
						act("$n blows into the tube and inflates the glass.", TRUE, ch, 0, 0, TO_ROOM);
						break;
					case 6:
						msg_to_char(ch, "You pull a wad of molten glass from the oven.\r\n");
						act("$n pulls a wad of molten glass from the oven.", TRUE, ch, 0, 0, TO_ROOM);
						break;
					case 8:
						msg_to_char(ch, "You place a pile of sand in the oven.\r\n");
						act("$n places a pile of sand in the oven.", TRUE, ch, 0, 0, TO_ROOM);
						break;
					}
				break;
			case ACT_MANUFACTURING:
				process_manufacturing(ch);
				break;
			case ACT_PANNING:
				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");
					GET_ACTION(ch) = ACT_NONE;
					break;
					}
				GET_ACTION_TIMER(ch) -= 1;
				msg_to_char(ch, "You sift through the sand and pebbles, looking for gold...\r\n");
				act("$n sifts through the river bed, looking for gold...", TRUE, ch, 0, 0, TO_ROOM);

				if (GET_ACTION_TIMER(ch) <= 0) {
					GET_ACTION(ch) = 0;
					if (!number(0, 19)) {
						obj_to_char((obj = read_object(o_GOLD_SMALL, VIRTUAL)), ch);
						act("You find $p!", FALSE, ch, obj, 0, TO_CHAR);
						break;
						}
					msg_to_char(ch, "You find nothing of value.\r\n");
					}
				break;
			case ACT_MUSIC:
				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");
					GET_ACTION(ch) = ACT_NONE;
					break;
					}

				switch (GET_MUSIC(ch)) {
					case 1:
						strcpy(buf, " very poorly.");
						break;
					case 2:
						strcpy(buf, ".");
						break;
					case 3:
						strcpy(buf, " with flair.");
						break;
					case 4:
						strcpy(buf, " exceptionally well.");
						break;
					case 5:
						strcpy(buf, " majestically.");
						break;
					default:
						strcpy(buf, " angelically!");
						break;
					}

				switch (GET_OBJ_VAL(obj, 0)) {
					case INSTR_LYRE:
						act("You strum at $p and hum to the tune.", FALSE, ch, obj, 0, TO_CHAR);
						if (number(0, 1))
							sprintf(buf1, "$n hums a tune and strums at $p%s", buf);
						else
							sprintf(buf1, "$n plucks at $p%s", buf);
						act(buf1, FALSE, ch, obj, 0, TO_ROOM);
						break;
					case INSTR_FLUTE:
						if (number(0, 1))
							act("You whistle a tune through $p.", FALSE, ch, obj, 0, TO_CHAR);
						else
							act("You play $p.", FALSE, ch, obj, 0, TO_CHAR);
						if (number(0, 1))
							sprintf(buf1, "$n plays $p%s", buf);
						else {
							sprintf(buf1, "$n whistles a tune on $p.");
							if (GET_MUSIC(ch) != 2)
								sprintf(buf1 + strlen(buf1), "\r\nYou think $e plays it%s", buf);
							}
						act(buf1, FALSE, ch, obj, 0, TO_ROOM);
						break;
					default:
						msg_to_char(ch, "This instrument type is uncoded.\r\n");
						GET_ACTION(ch) = ACT_NONE;
						break;
					}
				break;
			case ACT_EXCAVATING:
				for (count = 0; count < 1 + ATTACK_BONUS(ch) && GET_ACTION(ch) == ACT_EXCAVATING; count++) {
					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");
						GET_ACTION(ch) = ACT_NONE;
						break;
						}
					if (!number(0, 1)) {
						msg_to_char(ch, "You jab your shovel into the dirt..\r\n");
						act("$n jabs $s shovel into the dirt..", FALSE, ch, 0, 0, TO_ROOM);
						}
					else {
						msg_to_char(ch, "You toss a shovel-full of dirt out of the trench.\r\n");
						act("$n tosses a shovel-full of dirt out of the trench.", FALSE, ch, 0, 0, TO_ROOM);
						}
					GET_BUILD_VALUE(ch->in_room) += 1;

					if (GET_BUILD_VALUE(ch->in_room) >= 0) {
						msg_to_char(ch, "You finish excavating the trench!\r\n");
						act("$n finishes excavating the trench!", FALSE, ch, 0, 0, TO_ROOM);
						for (c = world[ch->in_room].people; c; c = c->next_in_room)
							if (!IS_NPC(c) && GET_ACTION(c) == ACT_EXCAVATING)
								GET_ACTION(c) = ACT_NONE;
						}
					}
				break;
			}
		}
	}


/* strips \r's from line */
char *stripcr(char *dest, const char *src) {
	int i, length;
	char *temp;

	if (!dest || !src) return NULL;
	temp = &dest[0];
	length = strlen(src);
	for (i = 0; *src && (i < length); i++, src++)
		if (*src != '\r') *(temp++) = *src;
	*temp = '\0';
	return dest;
	}


void update_reboot(void) {
	extern const char *reboot_type[];
	ACMD(do_reboot);
	ACMD(do_confirm);
	extern int wizlock_level;
	extern char *wizlock_message;

	if (reboot.time < 0)
		return;

	reboot.time -= 1;

	if (reboot.time <= 0) {
		do_reboot(NULL, "", 0, reboot.type);
		return;
		}

	if (reboot.time <= 5) {
		wizlock_level = 1;
		sprintf(buf, "This mud is preparing to %s.  The %s will happen in about %d minutes.\r\n", reboot_type[(int) reboot.type], reboot_type[(int) reboot.type], reboot.time);
		wizlock_message = str_dup(buf);
		}

	if (reboot.time <= 5 || (reboot.time <= 15 && (reboot.time % 2))) {
		syslog(0, FALSE, "The mud will %s in %d minute%s", reboot_type[(int) reboot.type], reboot.time, reboot.time != 1 ? "s" : "");
		syslog(0, FALSE, "This may be accelerated if all players type 'confirm'");
		mortlog(0, "The mud will %s in %d minute%s", reboot_type[(int) reboot.type], reboot.time, reboot.time != 1 ? "s" : "");
		mortlog(0, "This may be accelerated if all players type 'confirm'");
		}
	do_confirm(NULL, "", 0, 0);
	}


struct new_eq_set_data new_eq[] = {
	{ "\r", -1, -1, -1, -1, -1, -1, -1, -1 },

	{ "Archer",		o_HAT,	o_SHIRT,	NOTHING,	o_PANTS,	o_SHOES,	f_SHORTSWORD,	o_SHORTBOW,	o_QUIVER},
	{ "Bandit",		o_HOOD,	o_SHIRT,	NOTHING,	o_PANTS,	o_SHOES,	f_DAGGER,		f_DIRK,		NOTHING	},
	{ "Civilian",	o_HAT,	o_SHIRT,	NOTHING,	o_PANTS,	o_SHOES,	f_PICK,			f_DIRK,		NOTHING	},
	{ "Knight",		o_HAT,	o_SHIRT,	o_TUNIC,	o_PANTS,	o_SHOES,	f_SWORD,		f_SWORD,	NOTHING	},
	{ "Maiden",		o_HOOD,	o_DRESS,	NOTHING,	NOTHING,	o_SHOES,	o_TORCH,		NOTHING,	NOTHING	},
	{ "Woodsman",	o_HAT,	o_SHIRT,	NOTHING,	o_PANTS,	o_SHOES,	f_AXE,			o_TORCH,	NOTHING },

	{ "\n", -1, -1, -1, -1, -1, -1, -1, -1 }
	};


/* Some initializations for characters, including initial skills */
void do_start(Creature ch) {
	void set_title(Creature ch, char *title);
	extern int siteok_everyone;

	int i;
	Object obj;

	set_title(ch, NULL);

	/* Default Flags */
	SET_BIT(PRF_FLAGS(ch), PRF_MORTLOG);
	if (siteok_everyone)
		SET_BIT(PLR_FLAGS(ch), PLR_SITEOK);

	GET_DAMAGE(ch) = 0;
	GET_MOVE(ch) = GET_MAX_MOVE(ch);

	/* Standard conditions */
	GET_COND(ch, THIRST) = 0;
	GET_COND(ch, FULL) = 0;
	GET_COND(ch, DRUNK) = 0;
	GET_COND(ch, TIRED) = 0;

	/* Start playtime */
	ch->player.time.played = 0;
	ch->player.time.logon = time(0);

	/* Set up initial lore */
	if (IS_VAMPIRE(ch))
		add_lore(ch, LORE_START_VAMPIRE, -1);

	/* Give EQ, if applicable */
	if (GET_NEW_EQ_SET(ch) != 0) {
		if (new_eq[(int) GET_NEW_EQ_SET(ch)].head != NOTHING)
			equip_char(ch, read_object(new_eq[(int) GET_NEW_EQ_SET(ch)].head, VIRTUAL), WEAR_HEAD);
		if (new_eq[(int) GET_NEW_EQ_SET(ch)].body != NOTHING)
			equip_char(ch, read_object(new_eq[(int) GET_NEW_EQ_SET(ch)].body, VIRTUAL), WEAR_BODY);
		if (new_eq[(int) GET_NEW_EQ_SET(ch)].armor != NOTHING)
			equip_char(ch, read_object(new_eq[(int) GET_NEW_EQ_SET(ch)].armor, VIRTUAL), WEAR_ARMOR);
		if (new_eq[(int) GET_NEW_EQ_SET(ch)].legs != NOTHING)
			equip_char(ch, read_object(new_eq[(int) GET_NEW_EQ_SET(ch)].legs, VIRTUAL), WEAR_LEGS);
		if (new_eq[(int) GET_NEW_EQ_SET(ch)].feet != NOTHING)
			equip_char(ch, read_object(new_eq[(int) GET_NEW_EQ_SET(ch)].feet, VIRTUAL), WEAR_FEET);
		if (new_eq[(int) GET_NEW_EQ_SET(ch)].wield != NOTHING)
			equip_char(ch, read_object(new_eq[(int) GET_NEW_EQ_SET(ch)].wield, VIRTUAL), WEAR_WIELD);
		if (new_eq[(int) GET_NEW_EQ_SET(ch)].hold != NOTHING)
			equip_char(ch, read_object(new_eq[(int) GET_NEW_EQ_SET(ch)].hold, VIRTUAL), WEAR_HOLD);
		if (new_eq[(int) GET_NEW_EQ_SET(ch)].quiver != NOTHING) {
			equip_char(ch, read_object(new_eq[(int) GET_NEW_EQ_SET(ch)].quiver, VIRTUAL), WEAR_QUIVER);
			for (i = 0; i < GET_OBJ_VAL(GET_EQ(ch, WEAR_QUIVER), 0); i++)
				obj_to_obj(read_object(o_ARROW, VIRTUAL), GET_EQ(ch, WEAR_QUIVER));
			}

		/* Food/Water */
		obj_to_char(read_object(o_BREAD, VIRTUAL), ch);
		obj_to_char(read_object(o_BREAD, VIRTUAL), ch);
		obj = read_object(c_BOWL, VIRTUAL);
		GET_OBJ_VAL(obj, 1) = GET_OBJ_VAL(obj, 0);
		GET_OBJ_VAL(obj, 2) = LIQ_WATER;
		obj_to_char(obj, ch);
		}
	}


void update_rooms(void) {
	extern int last_zone_rotation;
	int i, j, update;
	room_rnum room;

	update = last_zone_rotation + 1;
	if (update > NUM_ZONE_UPDATES)
		update = 0;

	for (i = 0; i <= NUM_MAP_ZONES; i++) {
		if (zone_table[i].rotation != update)
			continue;
		for (room = i * SIZE_MAP_ZONES; room < (i+1) * SIZE_MAP_ZONES; room++) {
			if (room > top_of_world)
				break;

			/* Growing Seeds */
			if (SECT(room) == SECT_SEEDED) {
				GET_BUILD_VALUE(room)--;
				if (GET_BUILD_VALUE(room) <= 0) {
					GET_BUILD_VALUE(room) = 0;
					SECT(room) = SECT_CROP;
					}
				}

			/* Regrowing forests */
			if (SECT(room) == SECT_FOREST_3 || SECT(room) == SECT_FOREST_2 || SECT(room) == SECT_FOREST_1)
				if (!number(0, 99) && !world[room].type2 && !ROOM_AFF_FLAGGED(room, ROOM_AFF_NO_GROW))
					SECT(room)++;

			/* Growing new forests */
			if (SECT(room) == SECT_FOREST_4)
				for (j = 0; j < NUM_2D_DIRS; j++)
					if (SECT(real_shift(room, shift_dir[j][0], shift_dir[j][1])) == SECT_FIELD)
						if (!number(0, 249) && !ROOM_AFF_FLAGGED(room, ROOM_AFF_NO_GROW)) {
							SECT(real_shift(room, shift_dir[j][0], shift_dir[j][1])) = SECT_FOREST_1;
							GET_BUILD_VALUE(real_shift(room, shift_dir[j][0], shift_dir[j][1])) = 0;
							}
			}
		}
	}


void display_statistics_to_char(Creature ch) {
	extern int calculate_wealth(int e);
	extern int top_of_p_table;
	extern time_t boot_time;

	Creature vict;
	Object obj;
	int e, i, j;
	int best_empire = -1, wealthiest_empire = -1, famous_empire = -1;
	bool found = 0;

	char *tmstr;
	time_t mytime;
	int d, h, m;

	if (!ch)
		return;

	msg_to_char(ch, "\r\nEmpireMUD Statistics:\r\n");

	mytime = boot_time;

	tmstr = (char *) asctime(localtime(&mytime));
	*(tmstr + strlen(tmstr) - 1) = '\0';

	mytime = time(0) - boot_time;
	d = mytime / 86400;
	h = (mytime / 3600) % 24;
	m = (mytime / 60) % 60;

	msg_to_char(ch, "Current uptime: Up since %s: %d day%s, %d:%02d\r\n", tmstr, d, ((d == 1) ? "" : "s"), h, m);

	/* Find best scores.. */
	for (e = 0; e <= top_of_empiret; e++) {
		if (empire[e].imm_only)
			continue;

		if (empire[e].members > best_empire)
			best_empire = empire[e].members;
		if (calculate_wealth(e) > wealthiest_empire)
			wealthiest_empire = calculate_wealth(e);
		if (empire[e].fame > famous_empire)
			famous_empire = empire[e].fame;
		}

	if (best_empire > 1) {
		for (found = 0, e = 0; e <= top_of_empiret; e++)
			if (empire[e].members >= best_empire && !empire[e].imm_only) {
				msg_to_char(ch, "%s %s%s&0", found ? "," : "Most powerful empire:", empire[e].banner, empire[e].name);
				found = 1;
				}
		msg_to_char(ch, "\r\n");
		}
	if (wealthiest_empire > 0) {
		for (found = 0, e = 0; e <= top_of_empiret; e++)
			if (calculate_wealth(e) >= wealthiest_empire && !empire[e].imm_only) {
				msg_to_char(ch, "%s %s%s&0", found ? "," : "Most wealthy empire:", empire[e].banner, empire[e].name);
				found = 1;
				}
		msg_to_char(ch, "\r\n");
		}
	if (famous_empire > 0) {
		for (found = 0, e = 0; e <= top_of_empiret; e++)
			if (empire[e].fame >= famous_empire && !empire[e].imm_only) {
				msg_to_char(ch, "%s %s%s&0", found ? "," : "Most famous empire:", empire[e].banner, empire[e].name);
				found = 1;
				}
		msg_to_char(ch, "\r\n");
		}

	for (e = 0, i = 0, j = 0; e <= top_of_empiret; e++) {
		i += empire[e].territory;
		j += empire[e].members;
		}

	msg_to_char(ch, "Total Empires:	     %3d     Claimed Area:       %d\r\n", top_of_empiret + 1, i);
	msg_to_char(ch, "Total Players:    %5d   Players in Empires: %d\r\n", top_of_p_table + 1, j);

	for (vict = character_list, i = 0; vict; vict = vict->next)
		if (IS_NPC(vict))
			i++;

	msg_to_char(ch, "Unique Creatures:   %3d     Total Mobs:         %d\r\n", top_of_mobt + 1, i);

	for (obj = object_list, i = 0; obj; obj = obj->next)
		i++;

	msg_to_char(ch, "Unique Objects:     %3d     Total Objects:      %d\r\n", top_of_objt + 1, i);
	}


/* Count total bits in a bitvector_t */
int count_bits(bitvector_t bits) {
	bitvector_t b = bits, count = 0;

	for (; b; b >>= 1)
		if (IS_SET(b, 1))
			count++;
	return count;
	}


/* Finds total number of mobs in the game */
int total_mobs(void) {
	int count, i;

	for (i = 0, count = 0; i <= top_of_mobt; i++)
		count += mob_index[i].number;

	return count;
	}


/* find out if a person has resources available */
bool has_resources(Creature ch, Resource list[], bool ground) {
	Object obj;
	int i, total = 0;
	bool skin = FALSE;

	for (i = 0; list[i].vnum != -1; i++, skin = FALSE, total = 0) {
		if (list[i].vnum == o_SKIN)
			skin = TRUE;

		for (obj = ch->carrying; obj; obj = obj->next_content)
			if (GET_OBJ_VNUM(obj) == list[i].vnum) {
				if (skin)
					total += GET_OBJ_VAL(obj, 0);
				else
					total++;
				}
		if (ground)
			for (obj = world[ch->in_room].contents; obj; obj = obj->next_content)
				if (GET_OBJ_VNUM(obj) == list[i].vnum) {
					if (skin)
						total += GET_OBJ_VAL(obj, 0);
					else
						total++;
					}

		if (total < list[i].amount) {
			msg_to_char(ch, "You need %d more of: %s\r\n", list[i].amount - total, GET_OBJ_NAME_BY_PROTO(real_object(list[i].vnum)));
			return 0;
			}
		}

	return 1;
	}

/* extract resources from the list, hopefully having checked has_resources */
void extract_resources(Creature ch, Resource list[], bool ground) {
	Object obj, next_obj;
	int i, remaining;
	bool skin = FALSE;

	for (i = 0; list[i].vnum != -1; i++, skin = FALSE) {
		if (list[i].vnum == o_SKIN)
			skin = TRUE;

		remaining = list[i].amount;

		for (obj = ch->carrying; obj && remaining > 0; obj = next_obj) {
			next_obj = obj->next_content;

			if (GET_OBJ_VNUM(obj) == list[i].vnum) {
				if (skin)
					remaining -= GET_OBJ_VAL(obj, 0);
				else
					remaining--;
				extract_obj(obj);
				}
			}
		if (ground)
			for (obj = world[ch->in_room].contents; obj && remaining > 0; obj = next_obj) {
				next_obj = obj->next_content;

				if (GET_OBJ_VNUM(obj) == list[i].vnum) {
					if (skin)
						remaining -= GET_OBJ_VAL(obj, 0);
					else
						remaining--;
					extract_obj(obj);
					}
				}
		}
	}

/* give resources from a resource list */
void give_resources(Creature ch, Resource list[], bool split) {
	Object obj;
	int i, j, remaining;

	for (i = 0; list[i].vnum != -1; i++) {
		remaining = list[i].amount;

		if (list[i].vnum == o_SKIN) {
			obj_to_char((obj = read_object(o_SKIN, VIRTUAL)), ch);
			GET_OBJ_VAL(obj, 0) = remaining;
			continue;
			}

		if (split)
			remaining /= 2;

		for (j = 0; j < remaining; j++) {
			obj = read_object(list[i].vnum, VIRTUAL);
			if (IS_CARRYING_N(ch) >= CAN_CARRY_N(ch) || GET_OBJ_WEIGHT(obj) + IS_CARRYING_W(ch) > CAN_CARRY_W(ch))
				obj_to_room(obj, ch->in_room);
			else
				obj_to_char(obj, ch);
			}
		}
	}


/* finds the amount of a certain resource in a list */
int get_amount_of_resource(Resource list[], obj_vnum vnum) {
	int i;

	for (i = 0; list[i].vnum != -1; i++)
		if (list[i].vnum == vnum)
			return list[i].amount;
	return 0;
	}


/* gets the total amount of resources (used for timers */
int get_total_resources(Resource list[]) {
	int i, total = 0;

	for (i = 0; list[i].vnum != -1; i++)
		total += list[i].amount;
	return total;
	}