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: act.movement.c                                 EmpireMUD AD 1.0 *
*  Usage: movement commands, door handling, & sleep/rest/etc state        *
*                                                                         *
*  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 "empire.h"
#include "skills.h"

/* external vars  */
extern const char *dirs[];
extern int rev_dir[];


#define MOVE_NORMAL		0		/* Normal move message		*/
#define MOVE_LEAD		1		/* Leading message			*/
#define MOVE_FOLLOW		2		/* Follower message			*/
#define MOVE_CART		3		/* Cart message				*/
#define MOVE_EARTHMELD	4		/* Earthmeld - magic-numbered in vampire.c	*/

#define WATER_SECT(room)		(SECT(room) == SECT_RIVER || SECT(room) == SECT_OCEAN || (SECT(room) == SECT_ROAD && world[room].type == 1 && !IS_COMPLETE(room)))


/* simple function to determine if char can walk on water */
int has_boat(Creature ch) {
	Object obj;

	if (GET_LEVEL(ch) >= LVL_GOD)
		return (1);

	if (AFF_FLAGGED(ch, AFF_FLY) && !GET_RIDING(ch))
		return (1);

	for (obj = ch->carrying; obj; obj = obj->next_content)
		if (GET_OBJ_TYPE(obj) == ITEM_BOAT)
			return (1);
	return (0);
	}

int can_move(Creature ch, int dir, room_rnum to_room, int need_specials_check) {
	if (!PLR_FLAGGED(ch, PLR_UNRESTRICT) && WATER_SECT(ch->in_room))
		if (SECT(to_room) == SECT_MOUNTAIN && (GET_RIDING(ch) || !AFF_FLAGGED(ch, AFF_FLY)) && (!GET_RIDING(ch) || !MOB_FLAGGED(GET_RIDING(ch), MOB_FLYING))) {
			send_to_char("You are unable to scale the cliff.\r\n", ch);
			return 0;
			}
	if (!PLR_FLAGGED(ch, PLR_UNRESTRICT) && (SECT(to_room) == SECT_BUILDING || SECT(to_room) == SECT_MONUMENT_CLOSED || SECT(to_room) == SECT_MULTI) && SECT(ch->in_room) != SECT_INSIDE && world[to_room].building_entrance != dir && (IS_COMPLETE(to_room) || ALWAYS_CLOSED(to_room)) && ((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 && BUILDING_TYPE(to_room) != BUILDING_GATEHOUSE) || world[to_room].building_entrance != rev_dir[dir])) {
		send_to_char("You can't enter it from this side.\r\n", ch);
		return 0;
		}
	if (!PLR_FLAGGED(ch, PLR_UNRESTRICT) && SECT(ch->in_room) != SECT_INSIDE && (SECT(to_room) == SECT_BUILDING || SECT(to_room) == SECT_MULTI) && !CAN_USE_ROOM(ch, to_room, 0) && (IS_COMPLETE(to_room) || ALWAYS_CLOSED(to_room)) && !need_specials_check) {
		msg_to_char(ch, "You can't enter a building without permission.\r\n");
		return 0;
		}
	if (GET_RIDING(ch) && !MOB_FLAGGED(GET_RIDING(ch), MOB_FLYING | MOB_MOUNTAINWALK) && SECT(to_room) == SECT_MOUNTAIN) {
		msg_to_char(ch, "You can't ride in the mountains.\r\n");
		return 0;
		}
	if (GET_RIDING(ch) && !MOB_FLAGGED(GET_RIDING(ch), MOB_FLYING | MOB_AQUATIC) && WATER_SECT(to_room)) {
		msg_to_char(ch, "Your mount won't ride into the water.\r\n");
		return 0;
		}
	if (GET_RIDING(ch) && MOB_FLAGGED(GET_RIDING(ch), MOB_AQUATIC) && WATER_SECT(to_room)) {
		msg_to_char(ch, "Your mount won't go onto the land.\r\n");
		return 0;
		}
	if (GET_RIDING(ch) && IS_COMPLETE(to_room) && (SECT(to_room) == SECT_MULTI || SECT(to_room) == SECT_INSIDE || SECT(to_room) == SECT_BUILDING || SECT(to_room) == SECT_MONUMENT_CLOSED))
		if (BUILDING_TYPE(to_room) != BUILDING_STABLE && BUILDING_TYPE(to_room) != BUILDING_TUNNEL2 && BUILDING_TYPE(to_room) != BUILDING_DOCKS && BUILDING_TYPE(to_room) != BUILDING_GATEHOUSE) {
			msg_to_char(ch, "You can't ride indoors.\r\n");
			return 0;
			}
	if (GET_RIDING(ch) && ((SECT(to_room) == SECT_TRENCH && SECT(ch->in_room) != SECT_TRENCH) || (SECT(to_room) != SECT_TRENCH && SECT(ch->in_room) == SECT_TRENCH))) {
		msg_to_char(ch, "You can't ride up or down the steep slopes of the trench.\r\n");
		return 0;
		}
	return 1;
	}


/* do_simple_move assumes
 *    1. That there is no master and no followers.
 *    2. That the direction exists.
 *
 *   Returns :
 *   1 : If succes.
 *   0 : If fail
 */
int do_simple_move(Creature ch, int dir, room_rnum to_room, int need_specials_check, byte mode) {
	extern const char *from_dir[];
	extern int movement_loss[];

	room_rnum was_in;
	int need_movement;
	Creature animal = NULL, c;
	Object cart = NULL;


	/* First things first: Are we pulling a cart? */
	if ((cart = GET_PULLING(ch))) {
		mode = MOVE_CART;
		if (ch == GET_PULLED_BY(cart, 0))
			animal = GET_PULLED_BY(cart, 1);
		else
			animal = GET_PULLED_BY(cart, 0);
		if (animal && animal->in_room != ch->in_room)
			animal = NULL;
		}

	/* Make sure there's enough work animals */
	if (cart)
		if (GET_OBJ_VAL(cart, 1) > 1 && !animal) {
			act("You need two animals to move $p.", FALSE, ch, cart, 0, TO_CHAR);
			return 0;
			}

	/* Presence: Awe */
	if (mode != MOVE_EARTHMELD)
		for (c = world[ch->in_room].people; c; c = c->next_in_room)
			if (c != ch && DSC_FLAGGED(c, DSC_AWE) && ww_dice(GET_WILLPOWER(ch), 7) < 2 && !IS_IMMORTAL(c) && !IS_GOD(c)) {
				msg_to_char(ch, "You can't seem to leave!\r\n");
				WAIT_STATE(ch, PULSE_VIOLENCE);
				return FALSE;
				}

	if (IS_INJURED(ch, INJ_TIED)) {
		msg_to_char(ch, "You can't seem to move!\r\n");
		return FALSE;
		}

	if (GET_RIDING(ch) && MOB_FLAGGED(GET_RIDING(ch), MOB_TIED)) {
		act("Your mount is currently tied right where $E is.", FALSE, ch, 0, GET_RIDING(ch), TO_CHAR);
		return FALSE;
		}

	/* charmed? */
	if (AFF_FLAGGED(ch, AFF_CHARM) && ch->master && ch->in_room == ch->master->in_room) {
		send_to_char("The thought of leaving your master makes you weep.\r\n", ch);
		act("$n bursts into tears.", FALSE, ch, 0, 0, TO_ROOM);
		return (0);
		}

	if (GET_FEEDING_FROM(ch) || GET_FED_ON_BY(ch)) {
		msg_to_char(ch, "You can't seem to move!\r\n");
		return 0;
		}

	/* if the room we're going to needs a boat, check for one.  You can wade if you're already in one */
	if (WATER_SECT(to_room)) {
		if (!has_boat(ch) && !GET_RIDING(ch)) {
     		send_to_char("You need a boat to go there.\r\n", ch);
			return (0);
			}
		if (cart) {
			msg_to_char(ch, "You can't pull anything into the water.\r\n");
			return 0;
			}
		}

	if (mode != MOVE_EARTHMELD && !REAL_NPC(ch) && !PLR_FLAGGED(ch, PLR_UNRESTRICT) && SECT(ch->in_room) == SECT_BARRIER && IS_COMPLETE(ch->in_room) && dir != rev_dir[GET_LAST_DIR(ch)] && !AFF_FLAGGED(ch, AFF_FLY) && (!GET_RIDING(ch) || !MOB_FLAGGED(GET_RIDING(ch), MOB_FLYING))) {
		msg_to_char(ch, "You are unable to pass.\r\n");
		return 0;
		}

	if (SECT(to_room) == SECT_MOUNTAIN && cart) {
		msg_to_char(ch, "You can't pull anything over the mountains!\r\n");
		return 0;
		}
	if (SECT(to_room) == SECT_BARRIER && cart) {
		msg_to_char(ch, "You can't pull anything that close to the barrier.\r\n");
		return 0;
		}

	if (!REAL_NPC(ch) && mode != MOVE_EARTHMELD && !can_move(ch, dir, to_room, need_specials_check))
		return 0;

	/* move points needed is avg. move loss for src and destination sect type */
	need_movement = (movement_loss[SECT(ch->in_room)] + movement_loss[SECT(to_room)]) / 2;

	if (IS_VAMPIRE(ch))
		need_movement /= 3;
	else if (IS_GHOUL(ch))
		need_movement /= 2;

	if (AFF_FLAGGED(ch, AFF_FLY) || mode == MOVE_EARTHMELD)
		need_movement = 1;

	if (GET_MOVE(ch) < need_movement && !IS_NPC(ch) && !GET_RIDING(ch)) {
		if (need_specials_check && ch->master)
			send_to_char("You are too exhausted to follow.\r\n", ch);
		else
			send_to_char("You are too exhausted.\r\n", ch);

		return (0);
		}

	if ((cart || GET_LED_BY(ch)) && (SECT(to_room) == SECT_MULTI || SECT(to_room) == SECT_BUILDING || SECT(to_room) == SECT_MONUMENT_CLOSED) && BUILDING_TYPE(to_room) != BUILDING_CARPENTER && BUILDING_TYPE(to_room) != BUILDING_STABLE && BUILDING_TYPE(to_room) != BUILDING_GATEHOUSE && BUILDING_TYPE(to_room) != BUILDING_DOCKS)
		return 0;

	if (GET_RIDDEN_BY(ch)) {
		msg_to_char(ch, "Your rider might not approve.\r\n");
		return 0;
		}

	if (MOB_FLAGGED(ch, MOB_TIED)) {
		msg_to_char(ch, "You're tied up.\r\n");
		return 0;
		}

	if (GET_RIDING(ch) && ww_dice(GET_DEXTERITY(ch) + GET_RIDE(ch), 6) <= 0) {
		msg_to_char(ch, "Your mount refuses to move!\r\n");
		act("$N shakes its head and bucks as $n tries to convince it to move.", FALSE, ch, 0, GET_RIDING(ch), TO_NOTVICT);
		return 0;
		}

	/* Now we know we're allow to go into the room. */
	if (GET_LEVEL(ch) < LVL_GOD && !IS_NPC(ch) && !GET_RIDING(ch))
		GET_MOVE(ch) -= need_movement;

	REMOVE_BIT(AFF_FLAGS(ch), AFF_HIDE);

	if (!AFF_FLAGGED(ch, AFF_SNEAK)) {
		switch (mode) {
			case MOVE_CART:
				sprintf(buf2, "$p is pulled %s.", dirs[dir]);
				break;
			case MOVE_LEAD:
				sprintf(buf2, "$E leads $n with $M.");
				break;
			case MOVE_FOLLOW:
				sprintf(buf2, "$n follows $M.");
				break;
			case MOVE_EARTHMELD:
				break;
			default:
				if (GET_RIDING(ch))
					sprintf(buf2, "$n rides %s.", dirs[dir]);
				else {
					if (MOB_FLAGGED(ch, MOB_FLYING))
						sprintf(buf2, "$n flies %s.", dirs[dir]);
					else if (AFF_FLAGGED(ch, AFF_FLY))
						sprintf(buf2, "$n hovers %s.", dirs[dir]);
					else
						switch(SECT(ch->in_room)) {
							case SECT_RIVER:
							case SECT_OCEAN:	sprintf(buf2, "$n paddles %s.", dirs[dir]);	break;
							case SECT_MOUNTAIN:	sprintf(buf2, "$n climbs %s.", dirs[dir]);	break;
							default:			sprintf(buf2, "$n walks %s.", dirs[dir]);	break;
							}
					}
				break;
			}
		if (!ROOM_AFF_FLAGGED(ch->in_room, ROOM_AFF_SILENT) || number(0, 4))
			act(buf2, TRUE, ch, cart, (mode == MOVE_LEAD ? GET_LED_BY(ch) : ch->master), TO_ROOM);
		}

	was_in = ch->in_room;
	char_from_room(ch);
	char_to_room(ch, to_room);
	if (!REAL_NPC(ch))
		GET_LAST_DIR(ch) = dir;

	if (animal) {
		char_from_room(animal);
		char_to_room(animal, to_room);
		}
	if (cart)
		obj_to_room(cart, to_room);

	if (!AFF_FLAGGED(ch, AFF_SNEAK)) {
		switch (mode) {
			case MOVE_CART:
				sprintf(buf2, "$p is pulled up from %s.", from_dir[dir]);
				act(buf2, TRUE, ch, cart, 0, TO_ROOM);
				break;
			case MOVE_LEAD:
				act("$n is led with $M.", TRUE, ch, 0, GET_LED_BY(ch), TO_NOTVICT);
				act("$n is led with you.", TRUE, ch, 0, GET_LED_BY(ch), TO_VICT);
				break;
			case MOVE_FOLLOW:
				act("$N follows $m in.", TRUE, ch->master, 0, ch, TO_NOTVICT);
				act("$n follows you in.", TRUE, ch, 0, ch->master, TO_VICT);
				break;
			case MOVE_EARTHMELD:
				break;
			default:
				if (GET_RIDING(ch))
					sprintf(buf2, "$n rides up from %s.", from_dir[dir]);
				else {
					if (MOB_FLAGGED(ch, MOB_FLYING))
						sprintf(buf2, "$n flies in from %s.", from_dir[dir]);
					else if (AFF_FLAGGED(ch, AFF_FLY))
						sprintf(buf2, "$n hovers in from %s.", from_dir[dir]);
					else
						switch(SECT(ch->in_room)) {
							case SECT_RIVER:
							case SECT_OCEAN:	sprintf(buf2, "$n paddles up from %s.", from_dir[dir]);		break;
							case SECT_MOUNTAIN:	sprintf(buf2, "$n climbs in from %s.", from_dir[dir]);		break;
							default:			sprintf(buf2, "$n walks up from %s.", from_dir[dir]);		break;
							}
					}
				if (!ROOM_AFF_FLAGGED(ch->in_room, ROOM_AFF_SILENT) || number(0, 4))
					act(buf2, TRUE, ch, cart, 0, TO_ROOM);
				break;
			}
		}

	if (ch->desc != NULL)
		look_at_room(ch);
	if (animal && animal->desc != NULL)
		look_at_room(animal);

	return (1);
	}


int perform_move(Creature ch, int dir, int need_specials_check, byte mode) {
	room_rnum was_in, to_room = ch->in_room;
	struct follow_type *k, *next;

	if (ch == NULL)
		return FALSE;

	if ((PLR_FLAGGED(ch, PLR_UNRESTRICT) && SECT(ch->in_room) != SECT_INSIDE) || (SECT(ch->in_room) != SECT_BUILDING && SECT(ch->in_room) != SECT_MONUMENT_CLOSED && SECT(ch->in_room) != SECT_MULTI && SECT(ch->in_room) != SECT_INSIDE) || !(IS_COMPLETE(ch->in_room) || ALWAYS_CLOSED(ch->in_room))) {
		if (dir == UP || dir == DOWN || dir < 0 || dir > NUM_OF_DIRS) {
			send_to_char("Alas, you cannot go that way...\r\n", ch);
			return FALSE;
			}
		to_room = real_shift(ch->in_room, shift_dir[dir][0], shift_dir[dir][1]);
		}
	else {
		if (!EXIT(ch, dir) || EXIT(ch, dir)->to_room == NOWHERE) {
			msg_to_char(ch, "Alas, you cannot go that way...\r\n");
			return FALSE;
			}
		to_room = EXIT(ch, dir)->to_room;
		}

	/* Store old room */
	was_in = ch->in_room;

	if (!do_simple_move(ch, dir, to_room, need_specials_check, mode))
		return FALSE;

	if (GET_LEADING(ch) && GET_LEADING(ch)->in_room == was_in)
		perform_move(GET_LEADING(ch), dir, 1, MOVE_LEAD);

	for (k = ch->followers; k; k = next) {
		next = k->next;
		if ((k->follower->in_room == was_in) && (GET_POS(k->follower) >= POS_STANDING)) {
			act("You follow $N.\r\n", FALSE, k->follower, 0, ch, TO_CHAR);
			perform_move(k->follower, dir, 1, MOVE_FOLLOW);
			}
		}
	return TRUE;
	}


ACMD(do_move) {
	/*
	 * Perform move when typed, no specials check
	 */
	perform_move(ch, subcmd, FALSE, MOVE_NORMAL);
	}


/* Sneak is now sneak <direction>.  Note that entire parties may not sneak together. */
ACMD(do_sneak) {
	int dir;
	bool sneaking = FALSE;

	if (GET_PULLING(ch)) {
		msg_to_char(ch, "You can't sneak while pulling something.\r\n");
		return;
		}
	if (GET_LEADING(ch)) {
		msg_to_char(ch, "You can't sneak while leading something.\r\n");
		return;
		}

	one_argument(argument, arg);

	/* Already sneaking? */
	if (AFF_FLAGGED(ch, AFF_SNEAK))
		sneaking = TRUE;

	if ((dir = parse_direction(arg)) < 0) {
		msg_to_char(ch, "Sneak which direction?\r\n");
		return;
		}

	/* Check successfulness */
	if (ww_dice(GET_DEXTERITY(ch) + GET_STEALTH(ch), 6) > 0)
		SET_BIT(AFF_FLAGS(ch), AFF_SNEAK);
	else
		/* If we fail to sneak, we also lose hide.  Else, hide stays */
		REMOVE_BIT(AFF_FLAGS(ch), AFF_HIDE);

	/* Delay after sneaking */
	WAIT_STATE(ch, PULSE_VIOLENCE);

	perform_move(ch, dir, FALSE, MOVE_NORMAL);

	if (!sneaking)
		REMOVE_BIT(AFF_FLAGS(ch), AFF_SNEAK);
	}


int find_door(Creature ch, const char *type, char *dir, const char *cmdname) {
	int door;

	if (*dir) {			/* a direction was specified */
		if ((door = parse_direction(dir)) == -1) {	/* Partial Match */
			send_to_char("That's not a direction.\r\n", ch);
			return (-1);
			}
		if (EXIT(ch, door)) {	/* Braces added according to indent. -gg */
			if (EXIT(ch, door)->keyword) {
				if (isname((char *) type, EXIT(ch, door)->keyword))
					return (door);
				else {
					sprintf(buf2, "I see no %s there.\r\n", type);
					send_to_char(buf2, ch);
					return (-1);
					}
				}
			else
				return (door);
			}
		else {
			sprintf(buf2, "I really don't see how you can %s anything there.\r\n", cmdname);
			send_to_char(buf2, ch);
			return (-1);
			}
		}
	else {			/* try to locate the keyword */
		if (!*type) {
			sprintf(buf2, "What is it you want to %s?\r\n", cmdname);
			send_to_char(buf2, ch);
			return (-1);
			}
		for (door = 0; door < NUM_OF_DIRS; door++)
			if (EXIT(ch, door))
				if (EXIT(ch, door)->keyword)
					if (isname((char *) type, EXIT(ch, door)->keyword))
						return (door);

		sprintf(buf2, "There doesn't seem to be %s %s here.\r\n", AN(type), type);
		send_to_char(buf2, ch);
		return (-1);
		}
	}


#define NEED_OPEN	(1 << 0)
#define NEED_CLOSED	(1 << 1)

const char *cmd_door[] = {
	"open",
	"close",
	};

const int flags_door[] = {
	NEED_CLOSED,
	NEED_OPEN,
	};


#define EXITN(room, door)		(world[room].dir_option[door])
#define OPEN_DOOR(room, obj, door)	((obj) ?\
		(TOGGLE_BIT(GET_OBJ_VAL(obj, 1), CONT_CLOSED)) :\
		(TOGGLE_BIT(EXITN(room, door)->exit_info, EX_CLOSED)))

void do_doorcmd(Creature ch, Object obj, int door, int scmd) {
	int other_room = 0;
	struct room_direction_data *back = 0;

	sprintf(buf, "$n %ss ", cmd_door[scmd]);
	if (!obj && ((other_room = EXIT(ch, door)->to_room) != NOWHERE))
		if ((back = world[other_room].dir_option[rev_dir[door]]) != NULL)
			if (back->to_room != ch->in_room)
				back = 0;

	OPEN_DOOR(ch->in_room, obj, door);
	if (back)
		OPEN_DOOR(other_room, obj, rev_dir[door]);
	send_to_char(OK, ch);

	/* Notify the room */
	sprintf(buf + strlen(buf), "%s%s.", ((obj) ? "" : "the "), (obj) ? "$p" : (EXIT(ch, door)->keyword ? "$F" : "door"));
	if (!(obj) || (obj->in_room != NOWHERE))
		act(buf, FALSE, ch, obj, obj ? 0 : EXIT(ch, door)->keyword, TO_ROOM);

	/* Notify the other room */
	if (back) {
		sprintf(buf, "The %s is %s%s from the other side.", (back->keyword ? fname(back->keyword) : "door"), cmd_door[scmd], (scmd == SCMD_CLOSE) ? "d" : "ed");
		if (world[EXIT(ch, door)->to_room].people) {
			act(buf, FALSE, world[EXIT(ch, door)->to_room].people, 0, 0, TO_ROOM);
			act(buf, FALSE, world[EXIT(ch, door)->to_room].people, 0, 0, TO_CHAR);
			}
		}
	}


#define DOOR_IS_OPENABLE(ch, obj, door)	((obj) ? \
			((GET_OBJ_TYPE(obj) == ITEM_CONTAINER) && \
			OBJVAL_FLAGGED(obj, CONT_CLOSEABLE)) :\
			(EXIT_FLAGGED(EXIT(ch, door), EX_ISDOOR)))
#define DOOR_IS_OPEN(ch, obj, door)	((obj) ? \
			(!OBJVAL_FLAGGED(obj, CONT_CLOSED)) :\
			(!EXIT_FLAGGED(EXIT(ch, door), EX_CLOSED)))

#define DOOR_IS_CLOSED(ch, obj, door)	(!(DOOR_IS_OPEN(ch, obj, door)))

ACMD(do_gen_door) {
	int door = -1;
	char type[MAX_INPUT_LENGTH], dir[MAX_INPUT_LENGTH];
	Object obj = NULL;
	Creature victim = NULL;

	skip_spaces(&argument);
	if (!*argument) {
		sprintf(buf, "%s what?\r\n", cmd_door[subcmd]);
		send_to_char(CAP(buf), ch);
		return;
		}
	two_arguments(argument, type, dir);
	if (!generic_find(type, FIND_OBJ_INV | FIND_OBJ_ROOM, ch, &victim, &obj))
		door = find_door(ch, type, dir, cmd_door[subcmd]);

	if ((obj) || (door >= 0)) {
		if (!(DOOR_IS_OPENABLE(ch, obj, door)))
			act("You can't $F that!", FALSE, ch, 0, cmd_door[subcmd], TO_CHAR);
		else if (!DOOR_IS_OPEN(ch, obj, door) && IS_SET(flags_door[subcmd], NEED_OPEN))
			send_to_char("But it's already closed!\r\n", ch);
		else if (!DOOR_IS_CLOSED(ch, obj, door) && IS_SET(flags_door[subcmd], NEED_CLOSED))
			send_to_char("But it's currently open!\r\n", ch);
		else
			do_doorcmd(ch, obj, door, subcmd);
		}
	return;
	}


ACMD(do_stand) {
	extern int char_from_chair(Creature ch);
	switch (GET_POS(ch)) {
		case POS_STANDING:
			send_to_char("You are already standing.\r\n", ch);
			break;
		case POS_SITTING:
			send_to_char("You stand up.\r\n", ch);
			act("$n clambers to $s feet.", TRUE, ch, 0, 0, TO_ROOM);
			char_from_chair(ch);
			/* Will be sitting after a successful bash and may still be fighting. */
			GET_POS(ch) = FIGHTING(ch) ? POS_FIGHTING : POS_STANDING;
			break;
		case POS_RESTING:
			send_to_char("You stop resting, and stand up.\r\n", ch);
			act("$n stops resting, and clambers on $s feet.", TRUE, ch, 0, 0, TO_ROOM);
			char_from_chair(ch);
			GET_POS(ch) = POS_STANDING;
			break;
		case POS_SLEEPING:
			send_to_char("You have to wake up first!\r\n", ch);
			break;
		case POS_FIGHTING:
			send_to_char("Do you not consider fighting as standing?\r\n", ch);
			break;
		default:
			send_to_char("You stop floating around, and put your feet on the ground.\r\n", ch);
			act("$n stops floating around, and puts $s feet on the ground.", TRUE, ch, 0, 0, TO_ROOM);
			GET_POS(ch) = POS_STANDING;
			break;
		}
	}


ACMD(do_sit) {
	extern int char_to_chair(Creature ch, Object chair);
	Object chair;

	one_argument(argument, arg);

	switch (GET_POS(ch)) {
		case POS_STANDING:
			if (GET_RIDING(ch))
				msg_to_char(ch, "You're already sitting!\r\n");
			else if (!*arg) {
				send_to_char("You sit down.\r\n", ch);
				act("$n sits down.", FALSE, ch, 0, 0, TO_ROOM);
				GET_POS(ch) = POS_SITTING;
				break;
				}
			else if (!(chair = get_obj_in_list_vis(ch, arg, world[ch->in_room].contents)))
				send_to_char("You don't see anything like that here.\r\n", ch);
			else if (!IS_OBJ_STAT(chair, ITEM_CHAIR))
				send_to_char("You can't sit on that!\r\n", ch);
			else if (IN_CHAIR(chair))
				send_to_char("There's already someone sitting there.\r\n", ch);
			else {
				act("You sit on $p.", FALSE, ch, chair, 0, TO_CHAR);
				act("$n sits on $p.", FALSE, ch, chair, 0, TO_ROOM);
				char_to_chair(ch, chair);
				GET_POS(ch) = POS_SITTING;
				break;
				}
			break;
		case POS_SITTING:
			send_to_char("You're sitting already.\r\n", ch);
			break;
		case POS_RESTING:
			if (*arg) {
				send_to_char("You need to stand up before you can sit on something.\r\n", ch);
				return;
				}
			send_to_char("You stop resting, and sit up.\r\n", ch);
			act("$n stops resting.", TRUE, ch, 0, 0, TO_ROOM);
			GET_POS(ch) = POS_SITTING;
			break;
		case POS_SLEEPING:
			send_to_char("You have to wake up first.\r\n", ch);
			break;
		case POS_FIGHTING:
			send_to_char("Sit down while fighting? Are you MAD?\r\n", ch);
			break;
		default:
			send_to_char("You stop floating around, and sit down.\r\n", ch);
			act("$n stops floating around, and sits down.", TRUE, ch, 0, 0, TO_ROOM);
			GET_POS(ch) = POS_SITTING;
			break;
		}
	}


ACMD(do_rest) {
	extern int char_from_chair(Creature ch);

	switch (GET_POS(ch)) {
		case POS_STANDING:
			if (GET_RIDING(ch)) {
				msg_to_char(ch, "You climb down from your mount..\r\n");
				perform_dismount(ch);
				}
			send_to_char("You sit down and rest your tired bones.\r\n", ch);
			act("$n sits down and rests.", TRUE, ch, 0, 0, TO_ROOM);
			GET_POS(ch) = POS_RESTING;
			break;
		case POS_SITTING:
			char_from_chair(ch);
			send_to_char("You rest your tired bones on the ground.\r\n", ch);
			act("$n rests on the ground.", TRUE, ch, 0, 0, TO_ROOM);
			GET_POS(ch) = POS_RESTING;
			break;
		case POS_RESTING:
			send_to_char("You are already resting.\r\n", ch);
			break;
		case POS_SLEEPING:
			send_to_char("You have to wake up first.\r\n", ch);
			break;
		case POS_FIGHTING:
			send_to_char("Rest while fighting?  Are you MAD?\r\n", ch);
			break;
		default:
			send_to_char("You stop floating around, and stop to rest your tired bones.\r\n", ch);
			act("$n stops floating around, and rests.", FALSE, ch, 0, 0, TO_ROOM);
			GET_POS(ch) = POS_SITTING;
			break;
		}
	}


ACMD(do_sleep) {
	extern int char_from_chair(Creature ch);

	switch (GET_POS(ch)) {
		case POS_STANDING:
		case POS_SITTING:
			char_from_chair(ch);
		case POS_RESTING:
			if (GET_RIDING(ch)) {
				msg_to_char(ch, "You climb down from your mount..\r\n");
				perform_dismount(ch);
				}
			send_to_char("You lay down and go to sleep.\r\n", ch);
			act("$n lies down and falls asleep.", TRUE, ch, 0, 0, TO_ROOM);
			GET_POS(ch) = POS_SLEEPING;
			break;
		case POS_SLEEPING:
			send_to_char("You are already sound asleep.\r\n", ch);
			break;
		case POS_FIGHTING:
			send_to_char("Sleep while fighting?  Are you MAD?\r\n", ch);
			break;
		default:
			send_to_char("You stop floating around, and lie down to sleep.\r\n", ch);
			act("$n stops floating around, and lie down to sleep.", TRUE, ch, 0, 0, TO_ROOM);
			GET_POS(ch) = POS_SLEEPING;
			break;
		}
	}


ACMD(do_wake) {
	Creature vict;
	int self = 0;

	one_argument(argument, arg);
	if (*arg) {
		if (GET_POS(ch) == POS_SLEEPING)
			send_to_char("Maybe you should wake yourself up first.\r\n", ch);
		else if ((vict = get_char_vis(ch, arg, FIND_CHAR_ROOM)) == NULL)
			send_to_char(NOPERSON, ch);
		else if (vict == ch)
			self = 1;
		else if (AWAKE(vict))
			act("$E is already awake.", FALSE, ch, 0, vict, TO_CHAR);
		else if (GET_POS(vict) < POS_SLEEPING || IS_INJURED(vict, INJ_TORPOR))
			act("$E's in pretty bad shape!", FALSE, ch, 0, vict, TO_CHAR);
		else if (GET_COND(vict, TIRED) > 200 && number(0, 3))
			act("$E shrugs you off.", FALSE, ch, 0, vict, TO_CHAR);
		else {
			act("You wake $M up.", FALSE, ch, 0, vict, TO_CHAR);
			act("You are awakened by $n.", FALSE, ch, 0, vict, TO_VICT | TO_SLEEP);
			GET_POS(vict) = POS_SITTING;
			}
		if (!self)
			return;
		}
	if (GET_POS(ch) > POS_SLEEPING)
		send_to_char("You are already awake...\r\n", ch);
	else if (GET_COND(ch, TIRED) > 200)
		msg_to_char(ch, "You're too tired to wake up.\r\n");
	else {
		send_to_char("You awaken, and sit up.\r\n", ch);
		act("$n awakens.", TRUE, ch, 0, 0, TO_ROOM);
		GET_POS(ch) = POS_SITTING;
		}
	}


ACMD(do_follow) {
	bool circle_follow(Creature ch, Creature victim);
	void add_follower(Creature ch, Creature leader);
	Creature leader;

	one_argument(argument, buf);

	if (*buf) {
		if (!(leader = get_char_vis(ch, buf, FIND_CHAR_ROOM))) {
			send_to_char(NOPERSON, ch);
			return;
			}
		}
	else {
		send_to_char("Whom do you wish to follow?\r\n", ch);
		return;
		}

	if (ch->master == leader) {
		act("You are already following $M.", FALSE, ch, 0, leader, TO_CHAR);
		return;
		}
	if ((AFF_FLAGGED(ch, AFF_CHARM) || DSC_FLAGGED(ch, DSC_ENTRANCEMENT)) && (ch->master))
		act("But you only feel like following $N!", FALSE, ch, 0, ch->master, TO_CHAR);
	else {			/* Not Charmed follow person */
		if (leader == ch) {
			if (!ch->master) {
				send_to_char("You are already following yourself.\r\n", ch);
				return;
				}
			stop_follower(ch);
			}
		else {
			if (circle_follow(ch, leader)) {
				send_to_char("Sorry, but following in loops is not allowed.\r\n", ch);
				return;
				}
			if (ch->master)
				stop_follower(ch);
			REMOVE_BIT(AFF_FLAGS(ch), AFF_PARTY);
			add_follower(ch, leader);
			}
		}
	}


ACMD(do_mount) {
	Creature mob;

	one_argument(argument, arg);

	if (IS_NPC(ch))
		msg_to_char(ch, "You can't ride anything!\r\n");
	else if (GET_MORPH(ch) != MORPH_NONE)
		msg_to_char(ch, "You can't ride anything!\r\n");
	else if (GET_RIDING(ch))
		msg_to_char(ch, "You're already mounted.\r\n");
	else if (!*arg)
		msg_to_char(ch, "What did you want to mount?\r\n");
	else if (!(mob = get_char_vis(ch, arg, FIND_CHAR_ROOM)))
		msg_to_char(ch, NOPERSON);
	else if (ch == mob)
		msg_to_char(ch, "You can't mount yourself.\r\n");
	else if (!IS_NPC(mob))
		msg_to_char(ch, "You can't ride on other players.\r\n");
	else if (!MOB_FLAGGED(mob, MOB_MOUNTABLE))
		act("You can't ride $N!", FALSE, ch, 0, mob, TO_CHAR);
	else if (GET_RIDDEN_BY(mob))
		act("Someone is already riding $M.", FALSE, ch, 0, mob, TO_CHAR);
	else if (GET_LED_BY(mob))
		msg_to_char(ch, "You can't ride someone's who's being led around.\r\n");
	else if (GET_PULLING(mob))
		msg_to_char(ch, "You can't ride a harnessed mob.\r\n");
	else if (mob->desc)
		act("You can't ride $N!", FALSE, ch, 0, mob, TO_CHAR);
	else if (ww_dice(GET_CHARISMA(ch) + GET_ANIMAL_KEN(ch), 6) <= 0) {
		act("You try to climb onto $N, but slip and fall off.", FALSE, ch, 0, mob, TO_CHAR);
		act("$n tries to climb onto $N, but slips and falls off.", TRUE, ch, 0, mob, TO_ROOM);
		}
	else {
		act("You clamber onto $N's back.", FALSE, ch, 0, mob, TO_CHAR);
		act("$n clambers onto $N's back.", TRUE, ch, 0, mob, TO_ROOM);
		perform_mount(ch, mob);
		}
	}


ACMD(do_dismount) {
	if (!GET_RIDING(ch))
		msg_to_char(ch, "You're not riding anything right now.\r\n");
	else {
		act("You jump down off of $N.", FALSE, ch, 0, GET_RIDING(ch), TO_CHAR);
		act("$n jumps down off of $N.", FALSE, ch, 0, GET_RIDING(ch), TO_ROOM);
		perform_dismount(ch);
		}
	}


ACMD(do_lead) {
	Creature mob;

	one_argument(argument, arg);

	if (GET_LEADING(ch)) {
		act("You stop leading $N.", FALSE, ch, 0, GET_LEADING(ch), TO_CHAR);
		act("$n stops leading $N.", FALSE, ch, 0, GET_LEADING(ch), TO_ROOM);
		GET_LED_BY(GET_LEADING(ch)) = NULL;
		GET_LEADING(ch) = NULL;
		}
	else if (!*arg)
		msg_to_char(ch, "Lead whom?\r\n");
	else if (!(mob = get_char_vis(ch, arg, FIND_CHAR_ROOM)))
		msg_to_char(ch, NOPERSON);
	else if (ch == mob)
		msg_to_char(ch, "You can't lead yourself.\r\n");
	else if (!IS_NPC(mob))
		msg_to_char(ch, "You can't lead other players around.\r\n");
	else if (!MOB_FLAGGED(mob, MOB_MOUNTABLE))
		act("You can't lead $N!", FALSE, ch, 0, mob, TO_CHAR);
	else if (GET_LED_BY(mob))
		act("Someone is already leading $M.", FALSE, ch, 0, mob, TO_CHAR);
	else if (GET_RIDDEN_BY(mob))
		msg_to_char(ch, "You can't lead someone who's being ridden.\r\n");
	else if (mob->desc)
		act("You can't lead $N!", FALSE, ch, 0, mob, TO_CHAR);
	else {
		act("You begin to lead $N.", FALSE, ch, 0, mob, TO_CHAR);
		act("$n begins to lead $N.", TRUE, ch, 0, mob, TO_ROOM);
		GET_LEADING(ch) = mob;
		GET_LED_BY(mob) = ch;
		}
	}


int parse_direction(char *dir) {
	extern const char *alt_dirs[];
	extern const char *dirs[];

	int d;

	if ((d = search_block(dir, dirs, FALSE)) < 0)
		d = search_block(dir, alt_dirs, FALSE);

	return d;
	}


void perform_transport(Creature ch, room_rnum to_room) {
	room_rnum was_in = ch->in_room;
	struct follow_type *k;

	msg_to_char(ch, "You step into the Soul Stream and dematerialize!\r\n");
	act("$n steps into the Soul Stream and dematerializes!", TRUE, ch, 0, 0, TO_ROOM);

	char_to_room(ch, to_room);
	look_at_room(ch);

	act("$n materializes in front of the tower!", TRUE, ch, 0, 0, TO_ROOM);

	for (k = ch->followers; k; k = k->next) {
		if ((k->follower->in_room == was_in) && (GET_POS(k->follower) >= POS_STANDING)) {
			act("You follow $N.\r\n", FALSE, k->follower, 0, ch, TO_CHAR);
			perform_transport(k->follower, to_room);
			}
		}
	}


ACMD(do_transport) {
	extern room_rnum find_load_room(Creature ch);

	if (SECT(ch->in_room) != SECT_TOWER_OF_SOULS)
		msg_to_char(ch, "You need a Tower of Souls to transport!\r\n");
	else
		perform_transport(ch, find_load_room(ch));
	}


ACMD(do_bathe) {
	extern int last_action_rotation;

	if (GET_ACTION(ch) == ACT_BATHING) {
		msg_to_char(ch, "You stop bathing and climb out of the water.\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 (ROOM_TYPE(ch->in_room) != RTYPE_BATHS && BUILDING_TYPE(ch->in_room) != BUILDING_BATHS && SECT(ch->in_room) != SECT_RIVER)
		msg_to_char(ch, "You can't bathe here!\r\n");
	else if (!IS_COMPLETE(ch->in_room))
		msg_to_char(ch, "There isn't even any water in the baths yet!\r\n");
	else if (GET_DAYS_SINCE_BATHING(ch) == 0 && !IS_GOD(ch) && !IS_IMMORTAL(ch))
		msg_to_char(ch, "You're already pretty clean!\r\n");
	else {
		GET_ACTION(ch) = ACT_BATHING;
		GET_ACTION_ROOM(ch) = ch->in_room;
		GET_ACTION_ROTATION(ch) = last_action_rotation;

		msg_to_char(ch, "You undress and climb into the water!\r\n");
		act("$n undresses and climbs into the water!", FALSE, ch, 0, 0, TO_ROOM);
		}
	}