Mud20/accounts/
Mud20/accounts/c/
Mud20/accounts/f/
Mud20/accounts/k/
Mud20/accounts/s/
Mud20/accounts/t/
Mud20/area_current/
Mud20/area_current/newareas/
Mud20/bin/
Mud20/clans/
Mud20/gods/
Mud20/old-sources/
Mud20/player/
Mud20/player/a/del/
Mud20/player/b/
Mud20/player/b/bak/
Mud20/player/b/del/
Mud20/player/f/
Mud20/player/f/bak/
Mud20/player/f/del/
Mud20/player/k/
Mud20/player/k/bak/
Mud20/player/k/del/
Mud20/player/k/dmp/
Mud20/player/m/
Mud20/player/m/bak/
Mud20/player/o/
Mud20/player/o/bak/
Mud20/player/p/
Mud20/player/s/
Mud20/player/s/bak/
Mud20/player/s/del/
Mud20/player/t/
Mud20/player/t/del/
Mud20/player/v/
Mud20/public_html/
Mud20/races/
Mud20/skilltables/
__MACOSX/Mud20/accounts/
__MACOSX/Mud20/accounts/c/
__MACOSX/Mud20/accounts/f/
__MACOSX/Mud20/accounts/k/
__MACOSX/Mud20/accounts/s/
__MACOSX/Mud20/area_current/
__MACOSX/Mud20/area_current/core_areas/
__MACOSX/Mud20/area_current/helps/
__MACOSX/Mud20/area_current/newareas/
__MACOSX/Mud20/backups/
__MACOSX/Mud20/bin/
__MACOSX/Mud20/clans/
__MACOSX/Mud20/gods/
__MACOSX/Mud20/log/
__MACOSX/Mud20/old-sources/
__MACOSX/Mud20/player/
__MACOSX/Mud20/player/a/del/
__MACOSX/Mud20/player/b/
__MACOSX/Mud20/player/b/bak/
__MACOSX/Mud20/player/f/
__MACOSX/Mud20/player/f/bak/
__MACOSX/Mud20/player/f/del/
__MACOSX/Mud20/player/k/
__MACOSX/Mud20/player/k/bak/
__MACOSX/Mud20/player/k/del/
__MACOSX/Mud20/player/k/dmp/
__MACOSX/Mud20/player/m/
__MACOSX/Mud20/player/m/bak/
__MACOSX/Mud20/player/o/
__MACOSX/Mud20/player/o/bak/
__MACOSX/Mud20/player/p/
__MACOSX/Mud20/player/s/
__MACOSX/Mud20/player/s/bak/
__MACOSX/Mud20/player/t/del/
__MACOSX/Mud20/player/v/
__MACOSX/Mud20/public_html/
__MACOSX/Mud20/races/
__MACOSX/Mud20/skilltables/
/***************************************************************************
 * Mud20 1.0 by Todd H. Johnson (Kregor) a derivative of the Open Gaming   *
 * License by Wizards of the Coast. All comments referring to D20, OGL,    *
 * and SRD refer to the System Reference Document for the Open Gaming      *
 * system. Any inclusion of these derivatives must include credit to the   *
 * Mud20 system, the full and complete Open Gaming LIcense, and credit to  *
 * the respective authors. See ../doc/srd.txt for more information.        *
 *                                                                         *
 * Emud  2.2 by Igor van den Hoven, Michiel Lange, and Martin Bethlehem.   *
 *                                                                         *
 * MrMud 1.4 by David Bills, Dug Michael and Martin Gallwey                *
 *                                                                         *
 * Merc  2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael      *
 * Chastain, Michael Quan, and Mitchell Tse.                               *
 *                                                                         *
 * Original Diku Mud copyright (C) 1990 1991 by Sebastian Hammer,          *
 * Michael Seifert, Hans Henrik St{rfeld, Tom Madsen, and Katje Nyboe.     *
 ***************************************************************************/

/***************************************************************************
 * act_move.c: player-initiated movement functions.											   *
 ***************************************************************************/

#include "mud.h"

const int trap_dir[] = 
{
   TRAP_TRIG_MOVE_NORTH, TRAP_TRIG_MOVE_EAST, TRAP_TRIG_MOVE_SOUTH,
   TRAP_TRIG_MOVE_WEST, TRAP_TRIG_MOVE_UP, TRAP_TRIG_MOVE_DOWN
};

const int trap_door[] = 
{
   TRAP_TRIG_OPEN_NORTH, TRAP_TRIG_OPEN_EAST, TRAP_TRIG_OPEN_SOUTH,
   TRAP_TRIG_OPEN_WEST, TRAP_TRIG_OPEN_UP, TRAP_TRIG_OPEN_DOWN
};

/*
	Local functions.
*/
int	scan_door				args( ( CHAR_DATA *ch, char *arg ) );
OBJ_DATA *find_key	args( ( CHAR_DATA *ch, int key ) );

bool move_char( CHAR_DATA *ch, int door, bool forreal, char *argument )
{
	CHAR_DATA *fch, *fch_next, *fch_last, *sch, *mount;
	ROOM_INDEX_DATA *in_room, *to_room;
	ROOM_TIMER_DATA *rtd;
	int	in_room_vnum, to_room_vnum;
	EXIT_DATA *pExit;
	char buf[MAX_STRING_LENGTH];
	int sdoor, move;
	bool drunk, found, track, CanSee;
	OBJ_DATA *boat, *cart;

	push_call("move_char(%p,%p)",ch,door);

	if (door < 0 || door > 5)
	{
		bug( "do_move: bad door %d.", door );
		pop_call();
		return FALSE;
	}

	move         = 1;
	drunk        = !IS_NPC(ch) && drunk_level(ch) >= DRUNK_DRUNK;
	found        = FALSE;
	in_room_vnum = ch->in_room->vnum;
	in_room      = ch->in_room;
	mount        = is_mounting(ch) ? ch->mounting : ch;
	cart				 = ch->hitched;
	CanSee			 = can_see_in_room(ch, in_room) || learned(ch, gsn_blind_fight);

	if (!IS_NPC(ch) && forreal && is_string(ch->pcdata->pose))
	{
		STRFREE (ch->pcdata->pose);
		ch->pcdata->pose = STRALLOC("");
		send_to_char( "Your pose has been removed.\n\r", ch );
	}
	
	/* added buildwalk function - Kregor */
	
	if (argument)
	{
		if (!IS_NPC(ch) && IS_PLR(ch, PLR_BUILDWALK) && can_olc_modify(ch, ch->in_room->vnum))
		{
			if ((pExit = get_exit(in_room->vnum, door)) == NULL)
			{
				if (*argument == '\0')
				{
					bool fMatch = FALSE;
	
					for (to_room_vnum = in_room->area->olc_range_lo ; to_room_vnum <= in_room->area->olc_range_hi ; to_room_vnum++)
					{
						if (get_room_index(to_room_vnum) == NULL && can_olc_modify(ch, to_room_vnum))
						{
							fMatch = TRUE;
							break;
						}
					}
					if (!fMatch)
					{
						ch_printf_color(ch, "Next room index not found.\n\r");
						pop_call();
						return FALSE;
					}				
				}
				else if (!is_number(argument) || (to_room_vnum = atoi(argument)) <= 0)
				{
					send_to_char( "Invalid vnum argument.\n\r",ch);
					pop_call();
					return FALSE;
				}
				if (!can_olc_modify(ch, to_room_vnum))
				{
					ch_printf_color(ch, "That vnum is not in your allocated range.\n\r");
					pop_call();
					return FALSE;
				}
				do_link( ch, format("%s %d both", dir_name[door], to_room_vnum) );
				do_goto( ch, format("%d", to_room_vnum) );
				
				pop_call();
				return TRUE;
			}
		}
	}

	if (IS_SET(in_room->room_flags, ROOM_BLOCK))
	{
		if ((rtd = get_room_affect(in_room, ROOM_BLOCK)) == NULL || rtd->modifier == -1 || rtd->modifier == door)
		{
			if (forreal)
			{
				if (rtd && is_string(skill_table[rtd->type].vis_affect))
					act( skill_table[rtd->type].vis_affect, ch, dir_name[door], NULL, TO_CHAR);
				else
					act("You can't seem to move in that direction!", ch, NULL, NULL, TO_CHAR);
			}
			pop_call();
			return FALSE;
		}
	}
	
	if (forreal)
	{
		if (IS_SET(in_room->room_flags, ROOM_ICE) && !IS_FLYING(mount) && !IS_AFFECTED(mount, AFF_WATER_WALK))
		{
			if (!tumble_check(ch, NULL, tumble_roll(ch), 10))
			{
				if (!refl_save(ch, NULL, 10, -1))
				{
					switch (number_bits(1))
					{
						case 0:
							act( "You slip and fall flat.", ch, NULL, NULL, TO_CHAR);
							act( "$n slips and falls flat.", ch, NULL, NULL, TO_ROOM);
							break;
						case 1:
							act( "You lose your footing and fall.", ch, NULL, NULL, TO_CHAR);
							act( "$n loses $s footing and falls.", ch, NULL, NULL, TO_ROOM);
							break;
					}
					TAKE_ACTION(ch, ACTION_MOVE);
					mount->position = POS_RESTING;
				}
				else
				{
					act( "You lose your balance, and manage to right yourself.", ch, NULL, NULL, TO_CHAR);
					TAKE_ACTION(ch, ACTION_MOVE);
				}				
				pop_call();
				return FALSE;
			}
		}
		else if (drunk)
		{
			if (mount == ch && drunk_level(ch) * drunk_level(ch) > number_percent() && !tumble_check(ch, NULL, tumble_roll(ch), 10))
			{
				act( "You stumble over in your drunken state.", ch, NULL, NULL, TO_CHAR);
				act( "$n stumbles over in $s drunken state.", ch, NULL, NULL, TO_ROOM);
				ch->position = POS_RESTING;
				TAKE_ACTION(ch, ACTION_MOVE);
				pop_call();
				return FALSE;
			}
			else
			{
				if (drunk_level(ch) * drunk_level(ch) > number_percent())
				{
					door = number_range(0, 5);
				}
			}
		}
		else if (!CanSee)
		{
			if (mount == ch && number_percent() < 15 && !tumble_check(ch, NULL, tumble_roll(ch), 10))
			{
				act( "You blindly stumble over yourself.", ch, NULL, NULL, TO_CHAR);
				act( "$n blindly stumbles over $mself.", ch, NULL, NULL, TO_ROOM);
				ch->position = POS_RESTING;
				TAKE_ACTION(ch, ACTION_MOVE);
				pop_call();
				return FALSE;
			}
			else
			{
				if (number_percent() < 15 && !perception_check(ch, NULL, perception_roll(ch, SENSE_SIGHT), 20))
				{
					door = number_range(0, 5);
				}
			}
		}
	}

	pExit = get_exit(in_room->vnum, door);

	if (pExit == NULL || !can_use_exit(ch, pExit))
	{
		if (forreal)
		{
			if (drunk && !IS_OUTSIDE(ch))
			{
				send_to_char( "You slam into somthing in your drunken state!\n\r", ch );
				act( "$n slams $mself into something in $s drunken state!", ch, NULL, NULL, TO_ROOM);
			}
			else if (!CanSee && !IS_OUTSIDE(ch))
			{
				send_to_char( "You blindly slam into something!\n\r", ch );
				act( "$n slams blindly into something!", ch, NULL, NULL, TO_ROOM);
			}
			else
			{
				send_to_char( "Alas, you cannot go that way.\n\r", ch );
			}
		}
		pop_call();
		return FALSE;
	}
	
	to_room_vnum = pExit->to_room;
	to_room = room_index[to_room_vnum];

	if (IS_SET(to_room->room_flags, ROOM_BLOCK))
	{
		if ((rtd = get_room_affect(to_room, ROOM_BLOCK)) == NULL || rtd->modifier == -1 || rtd->modifier == rev_dir[door])
		{
			if (forreal)
			{
				if (rtd && is_string(skill_table[rtd->type].vis_affect))
					act( skill_table[rtd->type].vis_affect, ch, dir_name[door], NULL, TO_CHAR);
				else
					act("You can't seem to move in that direction!", ch, NULL, NULL, TO_CHAR);
			}
			pop_call();
			return FALSE;
		}
	}
	
	if (to_room->area->low_r_vnum != ROOM_VNUM_SCHOOL)
	{
		if(!IS_NPC(ch))
		{
			if (NOT_AUTHED(ch))
			{
				if (forreal)
				{
					send_to_char( "You have not been authorized to enter the realms.\n\r", ch);
				}
				pop_call();
				return FALSE;
			}
			if (IS_SET(pvnum_index[ch->pcdata->pvnum]->flags, PVNUM_CHNG_DESC))
			{
				if (forreal)
				{
					send_to_char( "You have been asked to change your desc by the GMs. See HELP DESCRIPTION.\n\r", ch);
				}
				pop_call();
				return FALSE;
			}	
			if (ch->description[0] == '\0')
			{
				if (forreal)
				{
					send_to_char( "You need to set your description first. See HELP DESCRIPTION.\n\r", ch);
				}
				pop_call();
				return FALSE;
			}	
			if (ch->pcdata->adjective[0] == '\0')
			{
				if (forreal)
				{
					send_to_char( "You need to set your adjective. See HELP ADJ.\n\r", ch);
				}
				pop_call();
				return FALSE;
			}	
		}
	}
	
	if (!IS_PLR(ch, PLR_HOLYLIGHT) && !IS_NPC(ch) && ch->in_room->area != to_room->area )
	{
		if (ch->level < to_room->area->low_hard_range)
		{
			if (forreal)
			{
				switch( to_room->area->low_hard_range - ch->level )
				{
					case 1:
						send_to_char( "A voice in your mind says, 'You are nearly ready to go that way...'\n\r", ch );
						break;
					case 2:
					case 3:
					case 4:
						send_to_char( "A voice in your mind says, 'Soon you shall be ready to travel down this path... soon.'\n\r", ch );
						break;
					case 5:
						send_to_char( "A voice in your mind says, 'You are not ready to go down that path... yet.'.\n\r", ch);
						break;
					default:
						send_to_char( "A voice in your mind says, 'You are not ready to go down that path.'.\n\r", ch);
				}
			}
			pop_call();
			return FALSE;
		}
		else if ( ch->level > to_room->area->hi_hard_range )
		{
			if (forreal)
			{
				send_to_char( "A voice in your head says, 'There is nothing more for you down that path.'\n\r", ch );
			}
			pop_call();
			return FALSE;
		}
	}

	if (IS_SET(pExit->exit_info, EX_CLOSED)
	&& !IS_AFFECTED(ch, AFF_GASEOUS)
	&& !IS_INCORPOREAL(ch))
	{
		if (IS_NPC(ch) || !IS_PLR(ch, PLR_HOLYLIGHT))
		{
			if (IS_SET(pExit->exit_info, EX_HIDDEN))
			{
				if (forreal)
				{
					if (drunk && !IS_OUTSIDE(ch))
					{
						send_to_char( "You hit a wall in your drunken state.\n\r", ch );
						act( "$n slams $mself into a wall in $s drunken state!", ch, NULL, NULL, TO_ROOM);
					}
					else
					{
						send_to_char( "Alas, you cannot go that way.\n\r", ch );
					}
				}
			}
			else
			{
				if (forreal)
				{
					act( "The $d is closed.", ch, NULL, pExit->keyword, TO_CHAR );
				}
			}
			pop_call();
			return FALSE;
		}
	}

	if (IS_SET(pExit->exit_info, EX_CLOSED) && IS_SET(pExit->exit_info, EX_PASSPROOF))
	{
		if (forreal)
		{
			act( "The $d remains firm.", ch, NULL, pExit->keyword, TO_CHAR );
		}
		pop_call();
		return FALSE;
	}

	if (IS_SET(pExit->exit_info, EX_JUMP) && !IS_FLYING(ch))
	{
		if (forreal)
		{
			send_to_char("You would have to JUMP that way.\n\r", ch);
			pop_call();
			return FALSE;
		}
	}

	if (IS_AFFECTED(ch, AFF_DOMINATE)
	&& !IS_NPC(ch)
	&& ch->master != NULL
	&& in_room == ch->master->in_room )
	{
		if (forreal)
		{
			send_to_char( "What?  And leave your beloved master?\n\r", ch );
		}
		pop_call();
		return FALSE;
	}

	if (!IS_NPC(ch) && ch->level < LEVEL_IMMORTAL && room_is_private(to_room))
	{
		if (forreal)
		{
			send_to_char( "That room is private right now.\n\r", ch );
		}
		pop_call();
		return FALSE;
	}

	if (in_room->sector_type == SECT_AIR
	|| to_room->sector_type == SECT_AIR
	|| (door == DIR_UP && IS_SET(to_room->room_flags, ROOM_NOFLOOR) && !IS_SET(pExit->exit_info, EX_CLIMB) && !is_affected(ch, gsn_spider_climb)))
	{
		if (!is_mounting(ch) && !IS_FLYING(ch))
		{
			if (forreal)
			{
				send_to_char( "You would have to fly.\n\r", ch );
			}
			pop_call();
			return FALSE;
		}
		else if (is_mounting(ch) && !IS_FLYING(ch->mounting))
		{
			if (forreal)
			{
				send_to_char("Your mount would need to fly.\n\r", ch);
			}
			pop_call();
			return FALSE;
		}
	}

	if (to_room->sector_type == SECT_UNDER_WATER
	&& in_room->sector_type != SECT_UNDER_WATER
	&& IS_AFFECTED(ch, AFF_WATER_WALK))
	{
		if (forreal)
		{
			send_to_char("You cannot submerge while affected by Water Walking.\n\r", ch);
		}
		pop_call();
		return FALSE;
	}

	if (in_room->sector_type == SECT_OCEAN
	||  to_room->sector_type == SECT_OCEAN
	||  in_room->sector_type == SECT_LAKE
	||  to_room->sector_type == SECT_LAKE)
	{
		if (!is_mounting(ch) && !IS_FLYING(ch) && !CAN_SWIM(ch))
		{
			if (in_room->sector_type != SECT_UNDER_WATER)
			{
				for (boat = ch->first_carrying ; boat ; boat = boat->next_content)
				{
					if (IS_OBJ_TYPE(boat, ITEM_BOAT))
					{
						found = TRUE;
						break;
					}
				}
				if (!boat && forreal && !swim_check(ch, 0))
				{
					pop_call();
					return FALSE;
				}
			}
		}
		if (is_mounting(ch) && !IS_FLYING(ch->mounting) && !CAN_SWIM(ch->mounting))
		{
			if (in_room->sector_type != SECT_UNDER_WATER)
			{
				if (forreal)
				{
					send_to_char("Your mount can't swim.\n\r", ch);
				}
				pop_call();
				return FALSE;
			}
		}
	}

	if (!IS_NPC(ch))
	{
		if (to_room->sector_type == SECT_ASTRAL)
		{
			if (!CAN_ASTRAL_WALK(ch))
			{
				if (forreal)
				{
					send_to_char( "You cannot traverse the astral plane.\n\r", ch );
				}
				pop_call();
				return FALSE;
			}
		}

		if (in_room->sector_type == SECT_ETHEREAL
		|| to_room->sector_type == SECT_ETHEREAL)
		{
			if (!CAN_ETHEREAL_WALK(ch))
			{
				if (forreal)
				{
					send_to_char( "You cannot traverse the ether.\n\r", ch );
				}
				pop_call();
				return FALSE;
			}
		}

		if (IS_SET(to_room->room_flags, ROOM_RIP) && ch->level < LEVEL_IMMORTAL)
		{
			if (ch->pcdata->pvnum != to_room->creator_pvnum)
			{
				if (forreal)
				{
					send_to_char( "Mystical wards prevent you from entering.\n\r", ch );
				}
				pop_call();
				return FALSE;
			}
		}
	}

	if (mount != ch)
	{
		switch (mount->position)
		{
			case POS_DEAD:
				if (forreal)
				{
					send_to_char("Your mount is dead!\n\r", ch);
				}
				pop_call();
				return FALSE;
			case POS_MORTAL:
			case POS_INCAP:
				if (forreal)
				{
					send_to_char("Your mount is hurt far too badly to move.\n\r", ch);
				}
				pop_call();
				return FALSE;
			case POS_STUNNED:
				if (forreal)
				{
					send_to_char("Your mount is too stunned to move.\n\r", ch);
				}
				pop_call();
				return FALSE;
			case POS_SLEEPING:
				if (forreal)
				{
					send_to_char("Your mount is sleeping.\n\r", ch);
				}
				pop_call();
				return FALSE;
			case POS_RESTING:
			case POS_CROUCHING:
			case POS_KNEELING:
			case POS_SITTING:
				if (forreal)
				{
					send_to_char("Your mount is not standing.\n\r", ch);
				}
				pop_call();
				return FALSE;
		}
	}

	if (IS_ENTANGLED(mount))
	{
		if (forreal)
			act("You are too tangled up to move!", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}

	if (ch->grappling)
	{
		if (forreal)
		{
			if (!in_same_room(ch, ch->grappling))
			{
				stop_grapple(ch);
			}
			else
			{
				act("You will have to RELEASE your grapple, or DRAG $N with you!", ch, NULL, ch->grappling, TO_CHAR);
				pop_call();
				return FALSE;
			}
		}
		else
		{
			pop_call();
			return FALSE;
		}
	}
	if (ch->grappled_by)
	{
		if (forreal)
		{
			if (!in_same_room(ch, ch->grappled_by))
			{
				stop_grapple(ch);
			}
			else
			{
				act("You failed to break $N's hold this turn.", ch, NULL, ch->grappled_by, TO_CHAR);
				act("$n struggles against your hold.", ch, NULL, ch->grappled_by, TO_VICT);
				pop_call();
				return FALSE;
			}
		}
		else
		{
			pop_call();
			return FALSE;
		}
	}
	if (!IS_AFFECTED(ch, AFF_FREEDOM) && (is_affected(ch, gsn_crushing_hand) || is_affected(ch, gsn_grasping_hand)))
	{
		if (forreal)
		{
			act("The giant hand holds you fast!!", ch, NULL, NULL, TO_CHAR);
		}
		pop_call();
		return FALSE;
	}
	if (!IS_FLYING(mount))
	{
		move = sector_table[in_room->sector_type].movement;

		if (IS_SET(pExit->exit_info, EX_CLIMB)
		&& !is_affected(ch, gsn_spider_climb)
		&& !race_table[get_race(ch)].climb_speed)
		{
			move *= 4;
		}

		switch (ch->in_room->sector_type)
		{
			case SECT_FIELD:
			case SECT_FOREST:
			case SECT_HILLS:
			case SECT_DESERT:
			case SECT_BEACH:
			case SECT_MOUNTAIN:
			case SECT_TUNDRA:
				if (learned(ch, gsn_woodland_stride))
				{
					move = UMAX(1, move / 2);
				}
				break;
		 	case SECT_SWAMP:
				if (IS_AFFECTED(mount, AFF_WATER_WALK) || IS_AFFECTED(mount, AFF_FREEDOM))
				{
					move = 1;
				}
				else if (learned(ch, gsn_woodland_stride))
				{
					move = UMAX(1, move / 2);
				}
				else if (CAN_SWIM(mount))
				{
					move = UMAX(1, move / 2);
				}
				break;
			case SECT_LAKE:
			case SECT_OCEAN:
		 	case SECT_RIVER:
				if (IS_AFFECTED(mount, AFF_WATER_WALK) || IS_AFFECTED(mount, AFF_FREEDOM))
				{
					move = 1;
				}
				else if (CAN_SWIM(mount))
				{
					move = UMAX(1, move / 2);
				}
				break;
			default:
				break;
		}
	}
	else
	{
		move = 1;
	}
	
	move = UMAX(1, move * mount->speed * mount->speed);

	/*
	 * Make movement based on carry weight
	 */
	switch (encumberance(mount))
	{
		case OVERLOADED:
			move *= 3;
			break;
		case VERY_ENCUMBERED:
			move *= 2;
			break;
		case ENCUMBERED:
			move = move * 3 / 2;
			break;
	}
	
	if (ch->in_room->sector_type == SECT_FOREST
	&& to_room->sector_type == SECT_FOREST
	&& is_affected(mount, gsn_tree_stride))
	{
		move = 0; // Tree Stride = no move loss
	}
	if (IS_INCORPOREAL(ch) || CAN_ETHEREAL_WALK(ch) || IS_AFFECTED(ch, AFF_GASEOUS))
	{
		move = 0; // Incorporeal = no move loss
	}

	/*
	 * SRD rule: pushing a mount beyond their endurance is a DC25 handle animal check - Kregor
	 */
	if (is_mounting(ch) && ch->mounting->move < move)
	{
		if (forreal && !handle_animal_check(ch, ch->mounting, handle_animal_roll(ch), 25))
		{
			send_to_char( "Your mount refuses to be pushed farther.\n\r", ch);
			pop_call();
			return FALSE;
		}
	}

	if (forreal == FALSE)
	{
		pop_call();
		return TRUE;
	}
	
	// reduce speed for difficult terrain
	check_speed(mount, ch->in_room);
	
	if (!IS_NPC(ch) || ch->desc)
	{
		if (ch->in_room->sector_type != SECT_FOREST
		|| to_room->sector_type != SECT_FOREST
		|| !is_affected(mount, gsn_tree_stride)) // Tree Stride = no wait state
		{
			if (IS_INCORPOREAL(ch) || CAN_ETHEREAL_WALK(ch) || IS_AFFECTED(ch, AFF_GASEOUS))
			{
				wait_state(ch, 11 - mount->speed * 2);
			}
			else if (ch->in_room->sector_type == SECT_UNDER_WATER)
			{
				if (!CAN_SWIM(mount) && !IS_AFFECTED(mount, AFF_FREEDOM))
				{
					wait_state(ch, (16 - mount->speed * 2) * 2);
				}
				else
				{
					wait_state(ch, (16 - mount->speed * 2) * 1);
				}
			}
			else if (ch->in_room->sector_type == SECT_LAKE && !IS_FLYING(mount))
			{
				if (!CAN_SWIM(mount) && !IS_AFFECTED(mount, AFF_FREEDOM))
				{
					wait_state(ch, (14 - mount->speed * 2) * 2);
				}
				else
				{
					wait_state(ch, (14 - mount->speed * 2) * 1);
				}
			}
			else if (ch->in_room->sector_type == SECT_OCEAN && !IS_FLYING(mount))
			{
				if (!CAN_SWIM(mount) && !IS_AFFECTED(mount, AFF_FREEDOM))
				{
					wait_state(ch, (16 - mount->speed * 2) * 2);
				}
				else
				{
					wait_state(ch, (16 - mount->speed * 2) * 1);
				}
			}
			else
			{
				wait_state(ch, 11 - mount->speed * 2);
			}
		}
	}
// 	if (move > mount->move)
// 		mount->nonlethal += (move - mount->move);
// 	mount->move = UMAX(0, mount->move - move);
	if (!move_loss(ch, mount, move))
	{
		pop_call();
		return FALSE;
	}
	
	if (IS_SET(pExit->exit_info, EX_CLIMB) && !IS_FLYING(mount) && !is_affected(mount, gsn_spider_climb))
	{
		if (forreal)
		{
			if (mount != ch)
			{
				send_to_char("You will have to dismount to climb there.\n\r", ch);
				pop_call();
				return FALSE;
			}
			if (ch->hitched)
			{
				act("You will have to unhitch $p to climb.", ch, ch->hitched, NULL, TO_CHAR);
				pop_call();
				return FALSE;
			}
			int check = climb_check(ch, NULL, climb_roll(ch), pExit->climb_dc);
			
			if (check == -2)
			{
				SET_AFFECT(ch, AFF2_FALLING);

				switch(door)
				{
					case DIR_UP:
						act( "You slip and fall back down.", ch, NULL, NULL, TO_CHAR);
						char_from_room(ch);
						char_to_room(ch, to_room_vnum, TRUE);
						pop_call();
						return FALSE;
					case DIR_DOWN:
						act( "You slip and fall downwards.", ch, NULL, NULL, TO_CHAR);
						char_from_room(ch);
						char_to_room(ch, in_room_vnum, TRUE);
						pop_call();
						return FALSE;
					default:
						break;
				}
			}
			else if (!check)
			{
				act( "You attempt to climb $t, but fail to make any progress.", ch, dir_name[door], NULL, TO_CHAR);
				act( "$n attempts to climb, and doesn't make any progress.", ch, NULL, NULL, TO_ROOM);
				pop_call();
				return FALSE;
			}
		}
	}
		
	if (trig_room_trap(ch, trap_dir[door]))
	{
		if (!IS_AWAKE(ch))
		{
			pop_call();
			return FALSE;
		}
	}

	if (IS_SET(pExit->exit_info, EX_CLIMB) && !IS_FLYING(mount))
	{
		sprintf(buf, " climbs %s", dir_name[door]);
	}
	else if (!IS_FLYING(mount)
	&& ch->in_room->sector_type != SECT_UNDER_WATER
	&& ch->in_room->sector_type != SECT_LAKE
	&& ch->in_room->sector_type != SECT_RIVER
	&& ch->in_room->sector_type != SECT_OCEAN)
	{
		if (is_mounting(ch))
		{
			sprintf(buf, " rides %s", dir_name[door]);
		}
		else if (!drunk && get_race(ch) == RACE_NONE)
		{
			sprintf(buf, " leaves %s", dir_name[door]);
		}
		else if (drunk)
		{
			sprintf(buf, " stumbles satiated %swards", dir_name[door]);
		}
		else
		{
			switch (ch->speed)
			{
				case 0: sprintf(buf, " leaves slowly %sward",	dir_name[door]); break;
				case 1: sprintf(buf, " leaves %sward",	dir_name[door]); break;
				case 2: sprintf(buf, " hustles %sward",	dir_name[door]); break;
				case 3: sprintf(buf, " rushes %sward",	dir_name[door]); break;
				case 4: sprintf(buf, " leaves hastily %sward",	dir_name[door]); break;
				case 5: sprintf(buf, " blazes %sward",	dir_name[door]); break;
			}
		}
	}
	else if (IS_FLYING(mount) && ch->in_room->sector_type != SECT_UNDER_WATER)
	{
		if (drunk)
		{
			sprintf(buf, " flies zigzagging %swards", dir_name[door]);
		}
		else
		{
			sprintf(buf, " flies %s", dir_name[door]);
		}
	}
	else
	{
		if (found && ch->in_room->sector_type != SECT_UNDER_WATER)
		{
			if (drunk)
			{
				sprintf(buf, " sails zigzagging %swards", dir_name[door]);
			}
			else
			{
				sprintf(buf," sails %s", dir_name[door]);
			}
		}
		if (IS_AFFECTED(mount, AFF_WATER_WALK))
		{
			if (is_mounting(ch))
			{
				sprintf(buf," rides %s", dir_name[door]);
			}
			else if (drunk)
			{
				sprintf(buf," stumbles %swards", dir_name[door]);
			}
			else
			{
				sprintf(buf," walks %s", dir_name[door]);
			}
		}
		else
		{
			if (drunk)
			{
				sprintf(buf," swims zigzagging %swards", dir_name[door]);
			}
			else
			{
				sprintf(buf," swims %s", dir_name[door]);
			}
		}
	}
	if (is_mounting(ch))
	{
		cat_sprintf(buf, " upon %s.", get_name(ch->mounting));
	}
	else
	{
		cat_sprintf(buf, ".");
	}
	act( "$n$t", ch, buf, NULL, TO_SEEHEAR);


	/*
	 * Chaos - code for last_left - track skill   11/14/93
	 * Revamped by Kregor - 3/9/07
	 */
	switch (in_room->sector_type)
	{
		case SECT_LAKE:
		case SECT_OCEAN:
		case SECT_LAVA:
		case SECT_AIR:
		case SECT_ASTRAL:
		case SECT_ETHEREAL:
			track = FALSE;
			break;
		default:
			track = TRUE;
			break;
	}
	
	if ((!IS_NPC(ch) && IS_IMMORTAL(ch)) || IS_ACT(ch, ACT_MOBINVIS)
	|| IS_INCORPOREAL(ch) || CAN_ETHEREAL_WALK(ch) || IS_AFFECTED(ch, AFF_GASEOUS)
	|| (IS_AFFECTED(ch, AFF_SNEAK) && learned(ch, gsn_trackless_step))
	|| is_affected(ch, gsn_pass_without_trace))
		track = FALSE;

	if (track)
	{
		int cnt;

		STRFREE(in_room->last_left[0]);

		for (cnt = 0 ; cnt < MAX_LAST_LEFT-1 ; cnt++)
		{
			in_room->last_left[cnt]      	= in_room->last_left[cnt+1];
			in_room->last_left_bits[cnt] 	= in_room->last_left_bits[cnt+1];
			in_room->last_left_pvnum[cnt] = in_room->last_left_pvnum[cnt+1];
		}
		in_room->last_left[MAX_LAST_LEFT-1] = STRDUPE( ch->name );

		if (!IS_NPC(ch))
		{
			in_room->last_left_pvnum[MAX_LAST_LEFT-1] = ch->pcdata->pvnum;
		}
		else
		{
			in_room->last_left_pvnum[MAX_LAST_LEFT-1] = 0;
		}

		in_room->last_left_bits[MAX_LAST_LEFT-1] = 1 << door;

		if (100 * ch->hit / get_max_hit(ch) < 20 && !IS_UNDEAD(ch))
		{
			SET_BIT(in_room->last_left_bits[MAX_LAST_LEFT-1], TRACK_BLOOD);
		}
		else if (IS_FLYING(ch))
		{
			SET_BIT(in_room->last_left_bits[MAX_LAST_LEFT-1], TRACK_FLY);
		}
	}

	if (!IS_NPC(ch))
	{
		mprog_exit_trigger(ch, door);
		rprog_exit_trigger(ch, door);

		if (ch->in_room != in_room)
		{
			pop_call();
			return FALSE;
		}
	}

	char_from_room( ch );

	char_to_room(ch, to_room_vnum, TRUE);

	in_room = room_index[in_room_vnum];
	to_room = room_index[to_room_vnum];
	
	if (to_room != ch->in_room)
	{
		pop_call();
		return TRUE;
	}

	if (IS_SET(pExit->exit_info, EX_CLIMB) && !IS_FLYING(mount))
	{
		if (door == DIR_DOWN)
			sprintf(buf, " climbs down from %s", rev_dir_name[door]);
		else if (door == DIR_UP)
			sprintf(buf, " climbs up from %s", rev_dir_name[door]);
		else
			sprintf(buf, " climbs in from %s", rev_dir_name[door]);
	}
	else if (!IS_FLYING(mount)
	&& ch->in_room->sector_type != SECT_UNDER_WATER
	&& ch->in_room->sector_type != SECT_LAKE
	&& ch->in_room->sector_type != SECT_RIVER
	&& ch->in_room->sector_type != SECT_OCEAN)
	{
		if (is_mounting(ch))
		{
			sprintf(buf, " rides in from %s", rev_dir_name[door]);
		}
		else if (drunk)
		{
			sprintf(buf, " stumbles in satiated from %s", rev_dir_name[door]);
		}
		else
		{
			switch (ch->speed)
			{
				case 0: sprintf(buf," arrives slowly from %s", rev_dir_name[door]);			break;
				case 1: sprintf(buf," arrives from %s", rev_dir_name[door]);						break;
				case 2: sprintf(buf," hustles in from %s", rev_dir_name[door]);					break;
				case 3: sprintf(buf," rushes in from %s", rev_dir_name[door]);					break;
				case 4: sprintf(buf," arrives hastily from %s", rev_dir_name[door]);		break;
				case 5: sprintf(buf," arrives in a blaze from %s", rev_dir_name[door]);	break;
			}
		}
	}
	else if (IS_FLYING(mount) && ch->in_room->sector_type != SECT_UNDER_WATER)
	{
		if (drunk)
		{
			sprintf(buf, " flies in zigzagging from %s", rev_dir_name[door]);
		}
		else
		{
			sprintf(buf, " flies in from %s", rev_dir_name[door]);
		}
	}
	else
	{
		if (found && ch->in_room->sector_type != SECT_UNDER_WATER)
		{
			if (drunk)
			{
				sprintf(buf, " sails in zigzagging from the %s", rev_dir_name[door]);
			}
			else
			{
				sprintf(buf," sails in from %s", rev_dir_name[door]);
			}
		}
		else if (IS_AFFECTED(mount, AFF_WATER_WALK))
		{
			if (is_mounting(ch))
			{
				sprintf(buf," rides in from %s", rev_dir_name[door]);
			}
			else if (drunk)
			{
				sprintf(buf," stumbles in from %s", rev_dir_name[door]);
			}
			else
			{
				sprintf(buf," walks in from %s", rev_dir_name[door]);
			}
		}
		else
		{
			if (drunk)
			{
				sprintf(buf," swims zigzagging in from %s", rev_dir_name[door]);
			}
			else
			{
				sprintf(buf," swims in from %s", rev_dir_name[door]);
			}
		}
	}
	if (is_mounting(ch))
	{
		cat_sprintf(buf, " on %s.", get_name(ch->mounting));
	}
	else if (found && ch->in_room->sector_type != SECT_UNDER_WATER)
	{
		cat_sprintf(buf, " on %s.", boat->short_descr);
	}
	else if (cart)
	{
		cat_sprintf(buf, " pulling %s.", cart->short_descr);
	}		
	else
	{
		cat_sprintf(buf, ".");
	}
	act( "$n$t", ch, buf, NULL, TO_SEEHEAR);

	do_look( ch, "" );

	if (ch->in_room->sector_type != SECT_ASTRAL && !IS_NPC(ch) && ch->in_room->vnum > 5)
	{
		ch->pcdata->last_real_room = ch->in_room->vnum;
	}

	if (IS_AFFECTED(ch, AFF_DETECT_TRAPS) || learned(ch, gsn_trap_spotter))
	{
		OBJ_DATA *obj;
		for (obj = ch->in_room->first_content ; obj ; obj = obj->next_content)
		{
			if (IS_OBJ_TYPE(obj, ITEM_TRAP) && is_trap_armed(obj))
			{
				if (IS_AFFECTED(ch, AFF_DETECT_TRAPS))
				{
					send_to_char_color( "{118}You detect a trap here!", ch );
					break;
				}
				else if (perception_check(ch, NULL, perception_roll(ch, SENSE_SIGHT), trap_dc(obj)))
				{
					send_to_char_color( "{118}You spot a trap here!", ch );
					break;
				}
			}
		}
	}

// 	if (!IS_IMMORTAL(ch) && !IS_NPC(ch) && in_room->area != to_room->area)
// 	{
// 		if (ch->level < to_room->area->low_soft_range)
// 		{
// 			send_to_char("You feel uncomfortable being in this strange land...\n\r", ch);
// 		}
// 		else	if (ch->level > to_room->area->hi_soft_range)
// 		{
// 			send_to_char("You feel there is not much to gain visiting this place...\n\r", ch);
// 		}
// 	}
// 
	if (!IS_FLYING(mount))
	{
		if (IS_SET(ch->in_room->room_flags, ROOM_ICE) && !IS_AFFECTED(mount, AFF_WATER_WALK))
		{
			if (!tumble_check(mount, NULL, tumble_roll(ch), 10))
			{
				if (!refl_save(mount, NULL, 10, -1))
				{
					switch (number_bits(1))
					{
						case 0:
							act( "You slip and fall flat on your back.", mount, NULL, NULL, TO_CHAR);
							act( "$n slips and falls flat on $s back.", mount, NULL, NULL, TO_ROOM);
							break;
						case 1:
							act( "You lose your footing and fall.", mount, NULL, NULL, TO_CHAR);
							act( "$n loses $s footing and falls.", mount, NULL, NULL, TO_ROOM);
							break;
					}
					TAKE_ACTION(ch, ACTION_FULL);
					mount->position = POS_RESTING;
				}
				else
				{
					act( "You lose your balance, and manage to right yourself.", mount, NULL, NULL, TO_CHAR);
					act( "$n loses $s balance, but manages to right $mrself.", mount, NULL, NULL, TO_CHAR);
					TAKE_ACTION(ch, ACTION_MOVE);
				}				
				pop_call();
				return FALSE;
			}
		}
		if ((rtd = get_room_affect(ch->in_room, ROOM_ENTANGLE)) != NULL)
		{
			CHAR_DATA *caster;
			
			if ((caster = get_caster_room(rtd)) == NULL || caster != ch || !is_same_group(caster, ch))
			{
				if (rtd->type == gsn_entangle)
					act( "{128}Plants and vines whip around and snag at you.", ch, NULL, NULL, TO_CHAR);
					
				if (!IS_AFFECTED(ch, AFF_FREEDOM))
				{
					if (!IS_AFFECTED(ch, AFF2_ENTANGLED) && !refl_save(ch, NULL, rtd->level, gsn_entangle))
					{
						AFFECT_DATA af;
						
						act( "{128}Plants and vines whip around and snag $n.", ch, NULL, NULL, TO_ROOM);
	
						af.type      = rtd->type;
						af.duration  = rtd->duration;
						af.modifier  = 0;
						af.location  = APPLY_NONE;
						af.bittype   = AFFECT_TO_CHAR;
						af.bitvector = AFF2_ENTANGLED;
						af.level		 = rtd->level;
						af.caster		 = STRALLOC(rtd->caster);
						affect_join( caster, ch, &af );
					}
				}
			}
		}
		if (is_room_affected(ch->in_room, gsn_faithful_hound))
		{
			CHAR_DATA *caster;
			
			if ((caster = get_caster_room(rtd)) != NULL && caster != ch)
			{
				act( "{138}The voice of a dog unseen by you barks furiously.", ch, NULL, NULL, TO_CHAR);
				act( "{138}Your spectral hound barks at $n furiously.", ch, NULL, caster, TO_VICT);
				act( "{138}The voice of an unseen dog barks at $n furiously.", ch, NULL, caster, TO_NOTVICT);
			}
		}
	}
	/*
	 * For swift tracker ability - Kregor 3/9/07
	 */
	if (!IS_NPC(ch) && ch->pcdata->tracking)
	{
		CHAR_DATA * rch;
		
		for (rch = ch->in_room->first_person ; rch ; rch = rch->next_in_room)
		{
			if (rch == ch->pcdata->tracking)
			{
				send_to_char_color("{138}You've found your prey!\n\r", ch);

				stop_tracking(ch);
				break;
			}
		}
		if (rch == NULL)
		{
			track_char(ch, ch->pcdata->tracking);
		}
	}

	if (mount != ch)
	{
		ch->mounting = mount;
	}
	if (cart)
	{
		ch->hitched = cart;
	}

	if (ch->in_room == in_room)
	{
		pop_call();
		return FALSE;
	}
	else if (mount != ch)
	{
		char_from_room(mount);
		char_to_room(mount, ch->in_room->vnum, TRUE);
	}
	if (cart)
	{
		send_to_room(format("%s trundles %sward.\n\r", cart->short_descr, dir_name[door]), cart->in_room); 
		obj_from_room(cart);
		obj_to_room(cart, ch->in_room->vnum);
	}

	/*
		Take followers along
	*/

	fch_last = in_room->last_person;

	for (fch = in_room->first_person ; fch ; fch = fch_next)
	{
		fch_next = fch->next_in_room;

		if (fch->in_room != in_room)
		{
			fch_next = in_room->first_person;
			continue;
		}

		if (fch->master == ch && fch->position == POS_STANDING && fch->in_room != ch->in_room)
		{
			fch->speed = mount->speed; // first, match leader's speed.
			check_speed(fch, fch->in_room);  // then, see if he can keep up.
			
			if (fch->speed < mount->speed)
			{
				if (fch->leader != ch)
				{
					act( "You move too fast for $N to follow.",  ch, NULL, fch, TO_CHAR);
					act( "$n moves too fast for you to follow.", ch, NULL, fch, TO_VICT);
					continue;
				}
				act( "$N can't move that fast, so you slow down.", ch, NULL, fch, TO_CHAR);
				mount->speed = fch->speed;

				for (sch = ch->master ; sch ; sch = sch->master)
				{
					if (sch->speed != mount->speed && sch->in_room == ch->in_room)
					{
						sch->speed = mount->speed;
					}
					else
					{
						break;
					}
				}
			}

			act( "You follow $N\n\r.", fch, NULL, ch, TO_CHAR );
			move_char( fch, door, TRUE, NULL );

		}
		if (fch == fch_last)
		{
			break;
		}
	}

	if (!IS_NPC(ch))
	{
		mprog_greet_trigger(ch);
		oprog_greet_trigger(ch);
		rprog_greet_trigger(ch);
	}

/* make shadowers shadow player -Dug */

	if (!IS_NPC(ch))
	{
		if ((fch = ch->pcdata->shadowed_by) != NULL)
		{
			if (fch->position == POS_STANDING && (fch->in_room != ch->in_room))
			{
				for (sdoor = 0 ; sdoor < 6 ; sdoor++)
				{
					if ((pExit = get_exit(fch->in_room->vnum, sdoor)) != NULL && room_index[pExit->to_room] == in_room)
					{
						CHAR_DATA *tch;

						ch_printf_color(fch, "%s leaves %s.\n\r", dir_name[door], get_name(ch));

						fch->speed = mount->speed;
						check_speed(fch, fch->in_room);

						if (mount->speed <= fch->speed)
						{
							fch->speed = ch->speed;
							ch_printf_color(fch, "You follow at a discrete distance.\n\r");
							tch = fch->pcdata->shadowing;
							fch->pcdata->shadowing = NULL;
							move_char( fch, sdoor, TRUE, NULL );
							fch->pcdata->shadowing = tch;
							if (can_see(ch, fch) && !bluff_check(fch, ch, bluff_roll(fch), 0))
							{
								act( "{138}You sense you are being followed by $n.", fch, NULL, ch, TO_VICT);
							}
						}
						else
						{
							act( "$N is moving too fast for you to follow.", fch, NULL, ch, TO_CHAR);
						}
						break;
					}
				}
				if (sdoor == 6)
				{
					stop_shadow(fch);
				}
			}
			else
			{
				stop_shadow(fch);
			}
		}

		if (ch->pcdata->shadowing)
		{
			stop_shadow(ch);
		}

		if (IS_SET(ch->act, PLR_WIZTIME))
		{
			ch_printf_color(ch, "{058}DEBUG: %d move point used\n\r", move);
		}
	}
	mprog_entry_trigger(ch);
	rprog_entry_trigger(ch);

	pop_call();
	return FALSE;
}

bool can_move_char( CHAR_DATA *ch, int door )
{
	return move_char(ch, door, FALSE, NULL);
}

/*
 * Subtract move points from character - return FALSE when unconscious
 * SRD adaptation: pushing yourself while exhausted is a forced march
 * Take 2 pts nonlethal damage per move pt below 0 - Kregor
 */
bool move_loss( CHAR_DATA *ch, CHAR_DATA *mount, int move )
{
	CHAR_DATA *victim;

	push_call("move_loss(%p,%p)",ch,move);

	if (mount == NULL)
		victim = ch;
	else
		victim = mount;

	if (victim->move < move)
	{
		if (mount == NULL || mount == ch)
			act( "You are pushing yourself beyond your endurance!", victim, NULL, NULL, TO_CHAR );
		else
			act( "$N is being pushed beyond $S endurance!", ch, NULL, victim, TO_CHAR );
		victim->nonlethal += (move - victim->move) * 2;
		victim->move = UMAX(0, victim->move - move);
		update_pos(victim,-1);
	}
	else
	{
		victim->move -= move;
	}
	
	if (!IS_AWAKE(victim))
	{
		send_to_char_color("{138}You fall unconscious from exhaustion.\n\r", victim);
		act( "$n falls unconscious from exhaustion.", victim, NULL, NULL, TO_ROOM);
		pop_call();
		return FALSE;
	}
	pop_call();
	return TRUE;
}

/*
 * Shove and Drag
 * Original code from Ferris@FootPrints.net
 * adapted to Mud20 by Kregor
 */
 
char *  const   shove_drag_verb [] =
{
	"shove",
	"drag",
	"*"
};


void shove_drag( CHAR_DATA *ch, char *argument, bool fDrag )
{
	char arg1[MAX_INPUT_LENGTH];
	char arg2[MAX_INPUT_LENGTH];
	char buf[MAX_STRING_LENGTH];
	ROOM_INDEX_DATA *in_room;
	ROOM_INDEX_DATA *to_room;
	CHAR_DATA *victim;
	EXIT_DATA *pExit;
	OBJ_DATA *obj;
	int door;

	push_call("shove_drag(%p,%s,%s)",ch,argument,fDrag);
	
	argument = one_argument(argument, arg1);
	argument = one_argument(argument, arg2);

	if (arg1[0] == '\0' || arg2[0] == '\0')
	{
		act("$t whom or what where?", ch, shove_drag_verb[fDrag], NULL, TO_CHAR);
		pop_call();
		return;
	}

	if ((obj = get_obj_list(ch, arg1, ch->in_room->first_content)) == NULL)
	{
		if ((victim = get_char_room(ch, arg1)) == NULL)
		{
			act("$t whom or what where?", ch, shove_drag_verb[fDrag], NULL, TO_CHAR);
			pop_call();
			return;
		}
	}

	if ((door = direction_door(arg2)) == -1)
	{
	  act("Alas, you cannot $t anything in that direction.", ch, shove_drag_verb[fDrag], NULL, TO_CHAR);
		pop_call();
	  return;
	}

	if ((pExit = get_exit(ch->in_room->vnum, door)) == NULL
	|| (to_room = room_index[pExit->to_room]) == NULL)
	{
		bug("shove_drag: NULL room",0);
		pop_call();
		return;
	}

	if (!can_move_char(ch, door))
	{
		act( "You are unable to $t anything $Tward.", ch, shove_drag_verb[fDrag], dir_name[door], TO_CHAR);
		pop_call();
		return;
	}

  if (obj)
  {
		if (!IS_PLR(ch, PLR_HOLYLIGHT) && !IS_SET(obj->wear_flags, CAN_WEAR_TAKE))
		{
			act("$p won't budge.", ch, obj, NULL, TO_CHAR);
			pop_call();
			return;
		}
	
		if (IS_SET(pExit->exit_info, EX_CLOSED))
		{
			act( "You cannot $t it through $d.", ch, shove_drag_verb[fDrag], pExit->keyword, TO_CHAR);
			act( "$n decides to $t $P around!", ch, shove_drag_verb[fDrag], obj, TO_ROOM);
			pop_call();
			return;
		}
	
		if (get_obj_weight(obj) > (5 * can_carry_w(ch)) - ch->carry_weight)
		{
			act( "$p is too heavy to $T.", ch, obj, shove_drag_verb[fDrag], TO_CHAR);
			sprintf(buf, "$n attempts to %s $p $Tward, but it's too heavy.", shove_drag_verb[fDrag]);
			act( buf, ch, obj, dir_name[door], TO_ROOM);
			pop_call();
			return;
		}
	
		if (!move_loss(ch, NULL, UMAX(1,get_obj_weight(obj) / 100)))
		{
			pop_call();
			return;
		}

		obj_from_room(obj);
		obj_to_room(obj, to_room->vnum);
		
		if (fDrag)
		{
			act( "$n drags $p $Tward.", ch, obj, dir_name[door], TO_ROOM);	
			move_char( ch, door, TRUE, NULL );
			act( "You drag $p in from the $T.", ch, obj, rev_dir_name[door], TO_CHAR);	
			act( "$n drags $p in from $T", ch, obj, rev_dir_name[door], TO_ROOM);
		}
		else
		{
			act( "You shove $p $Tward.", ch, obj, dir_name[door], TO_CHAR);
			act( "$n shoves $p $Tward.", ch, obj, dir_name[door], TO_ROOM);
			sprintf(buf, "%s is shoved in from %s.\n\r", obj->short_descr, rev_dir_name[door]);
			send_to_room(buf, to_room);
		}
		TAKE_ACTION(ch, ACTION_MOVE);
		pop_call();
		return;
  }

	if (ch == victim)
	{
		act( "You cannot $t yourself.", ch, shove_drag_verb[fDrag], NULL, TO_CHAR);
		pop_call();
		return;
	}

	in_room = victim->in_room;

	if (!IS_PLR(ch, PLR_HOLYLIGHT))
	{
		if (IS_AWAKE(victim) && is_safe(ch,victim))
		{
			pop_call();
			return;
		}
		if (IS_NPC(victim) 
		&& (IS_SET(victim->act,ACT_TRAIN|ACT_IS_HEALER)
		|| victim->pIndexData->pShop
		|| victim->pIndexData->pStable
		|| victim->pIndexData->pInn))
		{
			act("$N won't budge.", ch, NULL, victim, TO_CHAR);
			pop_call();
			return;
		}
	}

	if (IS_AWAKE(victim) && !can_move_char(victim, door))
	{
		act( "You are unable to $t them $Tward.", ch, shove_drag_verb[fDrag], dir_name[door], TO_CHAR);
		act( "$n tries to $t you around!", ch, shove_drag_verb[fDrag], victim, TO_VICT);
		act( "$n tries to $t $N around!", ch, shove_drag_verb[fDrag], victim, TO_NOTVICT);
		pop_call();
		return;
	}

	// for now simply intercepts, until grappling is coded.
	if (fDrag && IS_AWAKE(victim) && (!victim->grappled_by || victim->grappled_by != ch))
	{
		act( "If you want to drag $N around, you need to grapple $M first.", ch, NULL, victim, TO_CHAR);
		pop_call();
		return;
	}

	bool shoved = FALSE;
	
	/*
	 * Bull Rushing rules in play here.
	 * Shoving is hostile, so check murder and AoO are called - Kregor
	 */
	if (!fDrag)
	{
		if (!IS_AWAKE(victim))
		{
			act( "$N is not conscious, you would need to drag $M.", ch, NULL, victim, TO_CHAR);
			pop_call();
			return;
		}
		if (get_size(ch) + 1 < get_size(victim))
		{
			act( "$N is too big for you to shove around.", ch, NULL, victim, TO_CHAR);
			pop_call();
			return;
		}

		if (is_mounting(victim))
		{
			send_to_char( "You can't shove a mounted target... MAYBE you could shove the mount.\n\r", ch );
			pop_call();
			return;
		}

		int check;
		
		CHECK_TURN(ch, victim);
		
		if (in_combat(ch) && !is_active(ch) && !has_opportunity(ch, victim))
		{
			send_to_char("Just wait your turn.\n\r", ch);
			pop_call();
			return;
		}
	
		if (!check_murder(ch, victim))
		{
			pop_call();
			return;
		}

		if (in_combat(ch) && !is_active(ch))
			take_opportunity(ch, victim);
	
		TAKE_ACTION(ch, ACTION_STANDARD);

		if (!learned(ch, gsn_imp_bullrush) || (learned(victim, gsn_combat_reflexes) && victim->level > ch->level - 4))
		{
			if (attack_of_opportunity(victim, ch))
			{
				if (!IS_AWAKE(ch))
				{
					pop_call();
					return;
				}
			}
		}
		
		if ((check = combat_maneuver_check(ch, victim, gsn_bullrush)) >= 10)
			shoved = TRUE;
		else if (check >= 0)
		{
			act( "You shove $N back away from you.", ch, NULL, victim, TO_CHAR);
			act( "$n shoves you back away from $m.", ch, NULL, victim, TO_VICT);
			act( "$n shoves $N back away from $m.", ch, NULL, victim, TO_NOTVICT);
			victim->distracted = 2;
			if (IS_NPC(victim) && !in_combat(victim))
				fight(victim, ch);
			pop_call();
			return;
		}
	}

	if (!IS_AWAKE(victim) && victim->weight + victim->carry_weight > can_carry_w(ch) * 5)
	{
		sprintf(buf, "You attempt to %s $N $tward, but $E is too heavy.", shove_drag_verb[fDrag]);
		act( buf, ch, dir_name[door], victim, TO_CHAR);
		sprintf(buf, "$n attempts to %s $N $tward, but $E is too heavy.", shove_drag_verb[fDrag]);
		act( buf, ch, dir_name[door], victim, TO_NOTVICT);
		sprintf(buf, "$n attempts to %s you $tward, but $E is too heavy.", shove_drag_verb[fDrag]);
		act( buf, ch, dir_name[door], victim, TO_VICT);
		pop_call();
		return;
	}		

	if (IS_AWAKE(victim) && !shoved)
	{
		sprintf(buf, "You attempt to %s $N $tward, and fail.", shove_drag_verb[fDrag]);
		act( buf, ch, dir_name[door], victim, TO_CHAR);
		sprintf(buf, "$n attempts to %s $N $tward, and fails.", shove_drag_verb[fDrag]);
		act( buf, ch, dir_name[door], victim, TO_NOTVICT);
		sprintf(buf, "$n attempts to %s you $tward, and fails.", shove_drag_verb[fDrag]);
		act( buf, ch, dir_name[door], victim, TO_VICT);
		if (IS_NPC(victim) && !in_combat(victim))
			fight(victim, ch);
		pop_call();
		return;
	}

	if (!move_loss(ch, NULL, UMAX(1, get_weight(victim) / 10)))
	{
		pop_call();
		return;
	}

	char_from_room(victim);
	char_to_room(victim, to_room->vnum, TRUE);
	if (IS_AWAKE(victim))
		do_look(victim, "auto");
	else
		TAKE_ACTION(ch, ACTION_MOVE);
	if (fDrag)
	{
	  move_char( ch, door, TRUE, NULL );
		act( "$N is dragged in by $n from $t.", ch, rev_dir_name[door], victim, TO_NOTVICT);
		act( "You are dragged in by $n from $t.", ch, rev_dir_name[door], victim, TO_VICT);
		act( "You drag $N in from $t.", ch, rev_dir_name[door], victim, TO_CHAR);
	}
	else
	{
		act( "$n is shoved in from $t.", victim, rev_dir_name[door], NULL, TO_NOTVICT);
		act( "$N is shoved $tward.", ch, dir_name[door], victim, TO_CHAR);
		act( "$N is shoved $tward by $n.", ch, dir_name[door], victim, TO_ROOM);
		if (IS_NPC(victim) && !IS_NPC(ch) && ch != victim)
		{
			if (victim->npcdata->hate_fear != ch->pcdata->pvnum)
			{
				victim->npcdata->hate_fear = ch->pcdata->pvnum;
			}
		}
	}
	pop_call();
	return;
}
			   
void do_shove(CHAR_DATA *ch, char *argument)
{
	shove_drag(ch, argument, FALSE);
	return;
}

void do_drag(CHAR_DATA *ch, char *argument)
{
	shove_drag(ch, argument, TRUE);
	return;
}


void do_shadow (CHAR_DATA * ch, char *argument)
{
	char arg1[MAX_INPUT_LENGTH], arg2[MAX_INPUT_LENGTH];
	CHAR_DATA *victim;
	int door;
	ROOM_INDEX_DATA *old_room;

	push_call("do_shadow(%p,%p)",ch,argument);

	if (IS_NPC(ch))
	{
		send_to_char("You failed.\n\r", ch);
		pop_call();
		return;
	}

	argument = one_argument(argument, arg1);
	argument = one_argument(argument, arg2);

	if ((door = direction_door(arg1)) == -1)
	{
		if ((door = direction_door(arg2)) == -1)
		{
			send_to_char ("What direction?\n\r", ch);
			pop_call();
			return;
		}
		else
		{
			strcpy(arg2, arg1);
		}
	}

	if (arg2[0] == '\0')
	{
		send_to_char ("Shadow whom?\n\r", ch);
		pop_call();
		return;
	}

	if (!is_valid_exit(ch, ch->in_room, door))
	{
		send_to_char ("There is no exit there.\n\r", ch);
		pop_call();
		return;
	}

	old_room = ch->in_room;

	ch->in_room = room_index[get_exit(ch->in_room->vnum, door)->to_room];

	victim = get_char_room(ch, arg2);

	ch->in_room = old_room;

	if (victim == NULL || victim->in_room == ch->in_room)
	{
		send_to_char ("They aren't there.\n\r", ch);
		pop_call();
		return;
	}

	if (streetwise_check(ch, victim, streetwise_roll(ch), bluff_roll(victim)))
	{
		act ("You fail to shadow $m.", ch, NULL, victim, TO_CHAR);
		pop_call();
		return;
	}

	if (IS_NPC(victim) || victim->pcdata->shadowed_by || victim->pcdata->shadowing)
	{
		act ("You can't seem to shadow $m.", ch, NULL, victim, TO_CHAR);
		pop_call();
		return;
	}

	if (ch->master)
	{
		stop_follower(ch);
	}
	if (ch->pcdata->shadowing)
	{
		stop_shadow(ch);
	}
	add_shadow(ch, victim);
	pop_call();
	return;
}

void add_shadow (CHAR_DATA * ch, CHAR_DATA * master)
{
	push_call("add_shadow(%p,%p)",ch,master);

	if (ch->pcdata->shadowing)
	{
		bug("add_shadow : already shadowing.", 0);
		pop_call();
		return;
	}
	ch->pcdata->shadowing       = master;
	master->pcdata->shadowed_by = ch;
	act ("You are now shadowing $N.", ch, NULL, master, TO_CHAR);
	pop_call();
	return;
}


void stop_shadow (CHAR_DATA * ch)
{
	push_call("stop_shadow(%p)",ch);

	if (IS_NPC(ch) || ch->pcdata->shadowing == NULL)
	{
		pop_call();
		return;
	}
	act ("You stop shadowing $N.", ch, NULL, ch->pcdata->shadowing, TO_CHAR);

	ch->pcdata->shadowing->pcdata->shadowed_by = NULL;
	ch->pcdata->shadowing = NULL;

	pop_call();
	return;
}


void do_land( CHAR_DATA *ch, char *argument )
{
	push_call("do_land(%p,%p)",ch,argument);

	if (IS_AFFECTED(ch, AFF_FLYING))
	{
		act( "{168}$n descends and lands.{300}", ch, NULL, NULL, TO_ROOM );
		act( "{168}You descend and land.{300}", ch, NULL, NULL, TO_CHAR );
		REMOVE_BIT( ch->affected_by, AFF_FLYING );
	}
	else
	{
		send_to_char( "You are not currently airborne.\n\r", ch );
	}
	pop_call();
	return;
}

void do_fly( CHAR_DATA *ch, char *argument )
{
	push_call("do_land(%p,%p)",ch,argument);

	if (!CAN_FLY(ch))
	{
		send_to_char_color("Nice try, but you can't fly.\n\r", ch);
		pop_call();
		return;
	}
	else
	{
		if (!IS_AFFECTED(ch, AFF_FLYING))
		{
			act( "{168}$n launches $mself into the air!{300}", ch, NULL, NULL, TO_ROOM );
			act( "{168}You launch yourself into the air!{300}", ch, NULL, NULL, TO_CHAR );
			SET_BIT( ch->affected_by, AFF_FLYING );
		}
		else
		{
			send_to_char_color("{168}You are already airborne.{300}\n\r", ch);
		}			
	}
	pop_call();
	return;
}

/*
 * Automatically adjust speed at move_char
 * based on room conditions - Kregor 5/4/12
 */
void check_speed( CHAR_DATA *ch, ROOM_INDEX_DATA *room )
{
	CHAR_DATA *mount = NULL;
	int current;

	push_call("check_speed(%p,%p)",ch,room);
	
	// NPCs move at normal speed by default
	if (IS_NPC(ch) && !is_mounted(ch))
	{
		if (ch->npcdata->hate_fear) // unless chasing or fleeing someone
			ch->speed = UMAX(SPEED_RUN, ch->speed);
		else if (IS_AFFECTED(ch, AFF_HASTE))
			ch->speed = URANGE(SPEED_NORMAL, ch->speed, SPEED_HASTE);
		else
			ch->speed = URANGE(SPEED_NORMAL, ch->speed, SPEED_RUN);
	}
	else if (IS_PLR(ch, PLR_HOLYLIGHT))
	{
		pop_call();
		return;
	}
	else if (IS_AFFECTED(ch, AFF_HASTE))
	{
		ch->speed = UMIN(ch->speed, SPEED_HASTE);
	}
	else
	{
		ch->speed = UMIN(ch->speed, SPEED_RUN);
	}
	
	current = ch->speed;
	if (is_mounting(ch))
	{
		mount = ch->mounting;
	}
	else
	{
		mount = ch;
	}

	//can always move at half speed
	if (current == SPEED_HALF)
	{
		pop_call();
		return;
	}
	
	if (IS_FATIGUED(mount) && current > SPEED_NORMAL)
	{
		if (mount != ch)
			send_to_char("Your mount is too fatigued to run.", ch);
		else
			send_to_char("You are too fatigued to run.", ch);
		current = SPEED_NORMAL;
	}
	
	switch (encumberance(mount))
	{
		case ENCUMBERED:
		case VERY_ENCUMBERED:
			if (current > SPEED_NORMAL)
			{
				if (mount != ch)
					send_to_char("Your mount is encumbered and can only walk.", ch);
				else
					send_to_char("You are encumbered and can only walk.", ch);
				current = SPEED_NORMAL;
			}
			break;
	
		case OVERLOADED:
			if (current > SPEED_HALF)
			{
				if (mount != ch)
					send_to_char("Your mount is overloaded and can only move at half speed.\n\r", ch);
				else
					send_to_char("You are overloaded and can only move at half speed.\n\r", ch);
				current = SPEED_HALF;
			}
			break;
	}

	if (is_affected(mount, gsn_cripple) && current > SPEED_HALF)
	{
		if (mount != ch)
			send_to_char("Your mount is crippled and can only move at half speed.\n\r", ch);
		else
			send_to_char("You are crippled and can only move at half speed.\n\r", ch);
		current = SPEED_HALF;
	}

	if (IS_BLIND(ch) && current > SPEED_HALF && !learned(mount, gsn_blindsight) && !domain_apotheosis(ch, DOMAIN_CAVERN))
	{
		if (learned(ch, gsn_blind_fight) && current > SPEED_NORMAL)
		{
			act("You cannot run while you are blinded.", ch, NULL, NULL, TO_CHAR);
			current = SPEED_NORMAL;
		}
		else
		{
			act("You can only move at half speed while you are blinded.", ch, NULL, NULL, TO_CHAR);
			current = SPEED_HALF;
		}
	}

	if (!IS_NPC(ch) && ch->pcdata->tracking)
	{
		if (current > SPEED_NORMAL && !learned(ch, gsn_master_hunter))
		{
			send_to_char("You cannot move that quickly while tracking.\n\r", ch);
			current = SPEED_NORMAL;
		}
		if (current > SPEED_HUSTLE)
		{
			send_to_char("You cannot move that quickly while tracking.\n\r", ch);
			current = SPEED_HUSTLE;
		}
	}
	if (IS_AFFECTED(ch, AFF_HIDE) && current)
	{
		if (learned(ch, gsn_camouflage) && IS_OUTSIDE(ch) && current > SPEED_NORMAL)
		{
			send_to_char( "You can only move at normal or slower speed while hiding.\n\r" ,ch);
			current = SPEED_NORMAL;
		}
		else if (learned(ch, gsn_swift_stealth) && current > SPEED_NORMAL)
		{
			send_to_char( "You can only move at normal or slower speed while hiding.\n\r" ,ch);
			current = SPEED_NORMAL;
		}
		else
		{
			send_to_char( "You can only move at half speed while hiding.\n\r" ,ch);
			current = SPEED_HALF;
		}		
	}
	if (IS_AFFECTED(ch, AFF_SNEAK) && current > SPEED_HUSTLE)
	{
		send_to_char( "You cannot sneak while running.\n\r", ch);
		AFFECT_STRIP(ch, AFF_SNEAK);
	}
	
	if (IS_SET(room->room_flags, ROOM_ICE))
	{
		if (!IS_SHIFT(race_table[get_race(mount)].sectors, SECT_TUNDRA)
		&& !IS_AFFECTED(mount, AFF_WATER_WALK))
		{
			act("{168}You cannot move that fast on the ice.", ch, NULL, NULL, TO_CHAR);
			current = SPEED_HALF;
		}
	}
	
	if (!can_see_in_room(ch, room))
	{
		act("{108}You cannot see to move that fast!", ch, NULL, NULL, TO_CHAR);
		current = SPEED_HALF;
	}
	
	// Freedom trumps environmental conditions
	if (!IS_AFFECTED(mount, AFF_FREEDOM))
	{
		if (IS_SET(room->room_flags, ROOM_ENTANGLE))
		{
			act("You cannot move that fast in your surroundings.", ch, NULL, NULL, TO_CHAR);
			current = SPEED_HALF;
		}
		
		// creatures native to a terrain have no restriction for terrain
		if (!IS_SHIFT(race_table[get_race(ch)].sectors, room->sector_type))
		{
			if (!IS_FLYING(ch)) // terrain that only affects if on the ground
			{
				switch (room->sector_type)
				{
					case SECT_OCEAN:
					case SECT_RIVER:
					case SECT_LAKE:
						if (!rspec_req(mount, RSPEC_AQUATIC)
						&& !CAN_SWIM(mount))
						{
							act("You cannot move that fast in your surroundings.", ch, NULL, NULL, TO_CHAR);
							current = SPEED_HALF;
						}
						break;
					case SECT_SWAMP:
						if (!rspec_req(mount, RSPEC_AQUATIC)
						&& !CAN_SWIM(mount)
						&& !learned(mount, gsn_woodland_stride))
						{
							act("You cannot move that fast in your surroundings.", ch, NULL, NULL, TO_CHAR);
							current = SPEED_HALF;
						}
						break;
					case SECT_DESERT:
					case SECT_BEACH:
						if (!learned(mount, gsn_woodland_stride))
						{
							if (current > SPEED_NORMAL)
							{
								act("You cannot move that fast in your surroundings.", ch, NULL, NULL, TO_CHAR);
								current = SPEED_NORMAL;
							}
						}
						break;
					case SECT_UNDER_WATER:
						if (!rspec_req(mount, RSPEC_AQUATIC)
						&& !CAN_SWIM(mount))
						{
							act("You cannot move that fast in your surroundings.", ch, NULL, NULL, TO_CHAR);
							current = SPEED_HALF;
						}
						break;
					case SECT_LAVA:
						if (!IS_AFFECTED(mount, AFF_WATER_WALK))
						{
							act("You cannot move that fast in your surroundings.", ch, NULL, NULL, TO_CHAR);
							current = SPEED_HALF;
						}
						break;
					case SECT_TUNDRA:
						if (!learned(mount, gsn_woodland_stride)
						&& !IS_AFFECTED(mount, AFF_WATER_WALK))
						{
							if (current > SPEED_NORMAL)
							{
								act("You cannot move that fast in your surroundings.", ch, NULL, NULL, TO_CHAR);
								current = SPEED_NORMAL;
							}
						}
						break;
				}
			}
		}
	}
	ch->speed = current;
	if (mount != ch)
		mount->speed = current;
	
	pop_call();
	return;
}


void do_speed( CHAR_DATA *ch, char *argument )
{
	int value;

	push_call("do_speed(%p,%p)",ch,argument);

	if (argument[0]=='\0')
	{
		ch_printf_color(ch, "Usage:  speed <%s>\n\r", give_flags(move_speeds));
		send_to_char( "This allows changing of rate of movement. Rates effect the amount of MV lost.\n\r", ch);
		ch_printf_color( ch, "  {200}Current move speed: {178}%s{300}\n\r", capitalize(move_speeds[ch->speed]));

		pop_call();
		return;
	}

	if ((value = get_flag(argument, move_speeds)) == -1)
	{
		send_to_char( "That is not a valid speed.\n\r" ,ch);
		pop_call();
		return;
	}
	ch->speed = value;
	check_speed(ch, ch->in_room);
	
	ch_printf_color( ch, "{200}Speed set to: {178}%s{300}.\n\r", capitalize(move_speeds[ch->speed]));

	if (IS_AFFECTED(ch, AFF_SNEAK) && ch->speed > 0)
	{
		switch (ch->speed)
		{
			default:
				break;
			case 1:
				if ((!IS_OUTSIDE(ch) || !learned(ch, gsn_camouflage)) && !learned(ch, gsn_swift_stealth))
					ch_printf_color( ch, "You suffer a penalty of -5 to your stealth roll at this speed.\n\r" );
				break;
			case 2:
				ch_printf_color( ch, "You suffer a penalty of -20 to your stealth roll at this speed.\n\r" );
				break;
		}
	}
	
	pop_call();
	return;
}

void do_north( CHAR_DATA *ch, char *argument )
{
	push_call("do_north(%p,%p)",ch,argument);

	move_char( ch, DIR_NORTH, TRUE, argument );
	pop_call();
	return;
}

void do_east( CHAR_DATA *ch, char *argument )
{
	push_call("do_east(%p,%p)",ch,argument);

	move_char( ch, DIR_EAST, TRUE, argument );
	pop_call();
	return;
}

void do_south( CHAR_DATA *ch, char *argument )
{

	push_call("do_south(%p,%p)",ch,argument);

	move_char( ch, DIR_SOUTH, TRUE, argument );
	pop_call();
	return;
}

void do_west( CHAR_DATA *ch, char *argument )
{

	push_call("do_west(%p,%p)",ch,argument);

	move_char( ch, DIR_WEST, TRUE, argument );
	pop_call();
	return;
}

void do_up( CHAR_DATA *ch, char *argument )
{
	push_call("do_up(%p,%p)",ch,argument);

	move_char( ch, DIR_UP, TRUE, argument );
	pop_call();
	return;
}

void do_down( CHAR_DATA *ch, char *argument )
{
	push_call("do_down(%p,%p)",ch,argument);

	move_char( ch, DIR_DOWN, TRUE, argument );
	pop_call();
	return;
}

/*
 * The JUMP command, used to leap up, down or across
 * exits with the EX_JUMP flag - Kregor
 *
 * USING THE FLAG:
 * Ideally, a jump across should be to get over a pit, accomplished
 * by setting a downward exit with a fall_dist, can be flagged
 * EX_JUMP and/or EX_CLIMB. It is assumed you are jumping over this
 * exit to get the direction you wish to go. Failure makes you fall
 * into this pit room. The room across from the EX_JUMP exit should
 * also be flagged EX_JUMP in the opposing direction, with a like
 * downward pit room below it that adjoins with the pit room
 * beneath the other room.
 */
void do_jump( CHAR_DATA *ch, char *argument )
{
	int dir, diceroll, dc, reach, check;
	EXIT_DATA *pExit;
	ROOM_INDEX_DATA *to_room;
	char direction[6];

	push_call("do_jump(%p,%p)",ch,argument);

	if (argument[0] == '\0')
	{
		act( "Jump in what direction?", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return;
	}
	if ((dir = direction_door(argument)) == -1)
	{
		send_to_char("There is nowhere to jump in that direction.\n\r",ch);
		pop_call();
		return;
	}
	if ((pExit = get_exit(ch->in_room->vnum, dir)) == NULL)
	{
		send_to_char("There is nowhere to jump in that direction.\n\r",ch);
		pop_call();
		return;
	}
	
	if ((to_room = room_index[pExit->to_room]) == NULL)
	{
		log_build_printf(ch->in_room->vnum, "do_jump: NULL room");
		pop_call();
		return;
	}

	if (!IS_SET(pExit->exit_info, EX_JUMP))
	{
		send_to_char("You cannot jump to get there.\n\r",ch);
		pop_call();
		return;
	}
	if (!can_use_exit(ch, pExit))
	{
		if(IS_SET(ch->in_room->room_flags, ROOM_INDOORS))
		{
			act( "You slam yourself into the wall!", ch, NULL, NULL, TO_CHAR );
			act( "$n slams $mself into the wall!", ch, NULL, NULL, TO_ROOM );
		}
		else
		{
			send_to_char( "Alas, you cannot go that way.\n\r", ch );
		}
	}
	if (!can_move_char(ch, dir))
	{
		send_to_char("You cannot jump there.\n\r",ch);
		pop_call();
		return;
	}
	
	diceroll = jump_roll(ch);
	reach = (get_height(ch) + 12) / 12;
	
	switch (dir)
	{
		case DIR_UP:
			if (pExit->fall_dist < reach)
				dc = 0;
			else
				dc = (pExit->fall_dist - reach) * 4;
			break;
		case DIR_NORTH:
		case DIR_SOUTH:
		case DIR_EAST:
		case DIR_WEST:
			dc = pExit->fall_dist;
			break;
		case DIR_DOWN:
			if (pExit->fall_dist < reach)
				dc = 0;
			else
				dc = 15;
			break;
	}

	if ((check = jump_check(ch, NULL, diceroll, dc)) >= 0)
	{
		if (check <= -5 || !refl_save(ch, NULL, 20, -1))
		{
			/* use falling code in char_to_room to damage char if fails downward jump */	
			if (dir == DIR_DOWN)
			{
				act( "You try to jump $T, but fall.", ch, NULL, dir_name[dir], TO_CHAR);
				act( "$n tries to jump $T, but falls.", ch, NULL, dir_name[dir], TO_ROOM);
	
				set_falling(ch, pExit->fall_dist);
				char_from_room(ch);
				char_to_room(ch, pExit->to_room, TRUE);
				
				do_look(ch, "");
	
				if (!IS_NPC(ch))
				{
					mprog_greet_trigger(ch);
					oprog_greet_trigger(ch);
					rprog_greet_trigger(ch);
				}
				pop_call();
				return;
			}
			else if (dir == DIR_UP)
			{
				act( "You try to jump $T, but do not succeed.", ch, NULL, dir_name[dir], TO_CHAR);
				act( "$n tries to jump $T, but does not succeed.", ch, NULL, dir_name[dir], TO_ROOM);
				pop_call();
				return;
			}
			else
			{
				act( "You try to jump across to the $T, but falls short.", ch, NULL, dir_name[dir], TO_CHAR);
				act( "$n tries to jump across to the $T, but falls short.", ch, NULL, dir_name[dir], TO_ROOM);
	
				/* If there's a pit, fall into it! */
				if ((pExit = get_exit(ch->in_room->vnum, DIR_DOWN)) != NULL
				&& pExit->fall_dist && !can_move_char(ch, DIR_DOWN))
				{
					set_falling(ch, pExit->fall_dist);
					act( "$n falls downward!", ch, NULL, NULL, TO_ROOM);			
					char_from_room(ch);
					char_to_room(ch, pExit->to_room, TRUE);
	
					do_look(ch, "");
	
					if (!IS_NPC(ch))
					{
						mprog_greet_trigger(ch);
						oprog_greet_trigger(ch);
						rprog_greet_trigger(ch);
					}
				}
				pop_call();
				return;
			}
		}
	}
	
	if (dir >= 4)
		sprintf(direction, "high");
	else
	{
		sprintf(direction, "long");
		if ((pExit = to_room->exit[dir]) == NULL)
		{
			log_build_printf(to_room->vnum, "do_jump: no valid exit to jump to");
			pop_call();
			return;
		}
	}
	
	act( "You make an athletic $t jump $Tward.", ch, direction, dir_name[dir], TO_CHAR);
	act( "$n makes an athletic $t jump $Tward.", ch, direction, dir_name[dir], TO_ROOM);
	
	char_from_room(ch);
	char_to_room(ch, pExit->to_room, TRUE);
	
	do_look(ch, "");

	/* if not trained in Jump, char lands prone unless check made by 5 or more */
	if (check < 0 || (check < 5 && !learned(ch, gsn_jump)))
	{
		act( "$n leaps in from $T, landing flat on $s face.", ch, NULL, rev_dir_name[dir], TO_ROOM);
		act( "{178}You land flat on your face on the other side.", ch, NULL, NULL, TO_CHAR);
		ch->position = POS_RESTING;
	}
	else
	{
		act( "$n leaps in from $T.", ch, NULL, rev_dir_name[dir], TO_ROOM);
	}

	if (!IS_NPC(ch))
	{
		mprog_greet_trigger(ch);
		oprog_greet_trigger(ch);
		rprog_greet_trigger(ch);
	}
	
	pop_call();
	return;
}

	
int scan_door( CHAR_DATA *ch, char *arg )
{
	char door_name[MAX_INPUT_LENGTH];
	int count, number, door;
	EXIT_DATA *pExit;

	push_call("scan_door(%p,%p)",ch,arg);

	number = number_argument(arg, door_name);
	count  = 0;

	for (door = 0 ; door <= 5 ; door++)
	{
		if ((pExit = get_exit(ch->in_room->vnum, door)) == NULL)
		{
			continue;
		}
		if (!IS_SET(pExit->exit_info, EX_ISDOOR))
		{
			continue;
		}
		if (!pExit->keyword || !is_name(door_name, pExit->keyword))
		{
			continue;
		}
		if (!can_use_exit(ch, pExit))
		{
			continue;
		}
		if (IS_SET(pExit->exit_info, EX_HIDDEN) && !can_see_hidden(ch,door))
		{
			continue;
		}
		if (++count != number)
		{
			continue;
		}
		pop_call();
		return door;
	}
	pop_call();
	return( -1 );
}

int find_door( CHAR_DATA *ch, char *arg )
{
	EXIT_DATA *pExit;
	int door = -1;

	push_call("find_door(%p,%p)",ch,arg);

	if ((door = scan_door(ch, arg)) >= 0)
	{
		pop_call();
		return door;
	}

	if ((door = direction_door(arg)) == -1)
	{
		pop_call();
		return -1;
	}

	if ((pExit = get_exit(ch->in_room->vnum, door)) == NULL)
	{
		pop_call();
		return -1;
	}

	if (!IS_SET(pExit->exit_info, EX_ISDOOR))
	{
		pop_call();
		return -1;
	}

	if (!can_use_exit(ch, pExit))
	{
		pop_call();
		return -1;
	}

	if (IS_SET(pExit->exit_info, EX_HIDDEN) && !can_see_hidden(ch,door))
	{
		pop_call();
		return -1;
	}
	pop_call();
	return door;
}


void do_open( CHAR_DATA *ch, char *argument )
{
	char arg[MAX_INPUT_LENGTH];
	OBJ_DATA *obj;
	int door;

	push_call("do_open(%p,%p)",ch,argument);

	one_argument( argument, arg );

	if (!check_blind(ch))
	{
		pop_call();
		return;
	}

	if (arg[0] == '\0')
	{
		send_to_char( "Open what?\n\r", ch );
		pop_call();
		return;
	}

	if (IS_INCORPOREAL(ch) || !is_handy(ch))
	{
		act("You are unable to open anything.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return;
	}

	if ((door = find_door(ch, arg)) >= 0)
	{
		ROOM_INDEX_DATA *to_room;
		EXIT_DATA *pExit;
		EXIT_DATA *pExit_rev;

		pExit = ch->in_room->exit[door];

		if (!IS_SET(pExit->exit_info, EX_CLOSED))
		{
			send_to_char( "It's already open.\n\r", ch);
			pop_call();
			return;
		}

		/*
		 * Auto unlock - Scandum 02-07-2002
		 */
		if (IS_SET(pExit->exit_info, EX_LOCKED) && !IS_SET(pExit->exit_info, EX_KNOCKED))
		{
			OBJ_DATA *key = NULL;

			switch (pExit->key)
			{
				case -2:
					break;
				case -1:
				case 0:
					send_to_char("It's locked.\n\r", ch);
					pop_call();
					return;
				default:
					if ((key = find_key(ch, pExit->key)) == NULL)
					{
						send_to_char( "It's locked.\n\r", ch);
						pop_call();
						return;
					}
					break;
			}
			REMOVE_BIT(pExit->exit_info, EX_LOCKED);
			if (key)
			{
				act( "You unlock the $d with $p.",  ch, key, pExit->keyword, TO_CHAR);
				act( "$n unlocks the $d with $p.", ch, key, pExit->keyword, TO_ROOM);
			}
			else
			{
				act( "You unlock the $d.",  ch, NULL, pExit->keyword, TO_CHAR);
				act( "$n unlocks the $d.", ch, NULL, pExit->keyword, TO_ROOM);
			}

			if ((to_room = room_index[pExit->to_room]) != NULL
			&&  (pExit_rev = to_room->exit[rev_dir[door]]) != NULL
			&&  room_index[pExit_rev->to_room] == ch->in_room)
			{
				REMOVE_BIT(pExit_rev->exit_info, EX_LOCKED);
			}
		}

		REMOVE_BIT(pExit->exit_info, EX_CLOSED);
		act( "$n opens the $d.", ch, NULL, pExit->keyword, TO_ROOM );
		act( "You open the $d.", ch, NULL, pExit->keyword, TO_CHAR );

		if ((to_room = room_index[pExit->to_room]) != NULL
		&&  (pExit_rev = to_room->exit[rev_dir[door]]) != NULL
		&&  room_index[pExit_rev->to_room] == ch->in_room )
		{
			REMOVE_BIT(pExit_rev->exit_info, EX_CLOSED);

			ch->in_room = to_room;

			act( "The $d opens.", ch, NULL, pExit_rev->keyword, TO_ROOM);

			ch->in_room = room_index[pExit_rev->to_room];
		}
		trig_room_trap(ch, trap_door[door]);
	}
	else if ((obj = get_obj_here(ch, arg)) != NULL)
	{
		if (obj->item_type != ITEM_SPELLPOUCH && obj->item_type != ITEM_CONTAINER
		&& obj->item_type != ITEM_SHEATH && obj->item_type != ITEM_CART && obj->item_type != ITEM_QUIVER)
		{
			send_to_char( "That's not a container.\n\r", ch );
			pop_call();
			return;
		}
		if (!IS_SET(obj->value[1], CONT_CLOSED))
		{
			send_to_char( "It's already open.\n\r", ch );
			pop_call();
			return;
		}
		if (!IS_SET(obj->value[1], CONT_CLOSEABLE))
		{
			send_to_char( "It's not closeable.\n\r", ch );
			pop_call();
			return;
		}
		if (IS_SET(obj->value[1], CONT_LOCKED))
		{
			if (obj->value[2] < 1)
			{
				send_to_char( "It's locked.\n\r", ch );
				pop_call();
				return;
			}
			if (find_key(ch, obj->value[2]) == NULL)
			{
				send_to_char( "It's locked.\n\r", ch );
				pop_call();
				return;
			}
			REMOVE_BIT(obj->value[1], CONT_LOCKED);
			act( "You unlock $p.",  ch, obj, NULL, TO_CHAR );
			if (obj->carried_by == ch)
				act( "$n unlocks $p.", ch, obj, NULL, TO_CAN_SEE );
			else
				act( "$n unlocks $p.", ch, obj, NULL, TO_ROOM );
			trig_obj_trap(ch, obj, TRAP_TRIG_OPEN_OBJ);
		}
		REMOVE_BIT(obj->value[1], CONT_CLOSED);
		act( "You open $p.", ch, obj, NULL, TO_CHAR );
		if (obj->carried_by == ch)
			act( "$n opens $p.", ch, obj, NULL, TO_CAN_SEE );
		else
			act( "$n opens $p.", ch, obj, NULL, TO_ROOM );
		trig_obj_trap(ch, obj, TRAP_TRIG_OPEN_OBJ);
	}
	else
	{
		act( "You see no $T here.", ch, NULL, arg, TO_CHAR );
	}
	pop_call();
	return;
}

void do_close( CHAR_DATA *ch, char *argument )
{
	char arg[MAX_INPUT_LENGTH];
	OBJ_DATA *obj;
	int door;

	push_call("do_close(%p,%p)",ch,argument);

	one_argument( argument, arg );

	if (!check_blind(ch))
	{
		pop_call();
		return;
	}

	if (arg[0] == '\0')
	{
		send_to_char( "Close what?\n\r", ch );
		pop_call();
		return;
	}

	if ((door = find_door(ch, arg)) >= 0)
	{
		ROOM_INDEX_DATA *to_room;
		EXIT_DATA *pExit;
		EXIT_DATA *pExit_rev;

		pExit = ch->in_room->exit[door];
		if (IS_SET(pExit->exit_info, EX_CLOSED))
		{
			send_to_char( "It's already closed.\n\r",    ch );
			pop_call();
			return;
		}
		if (IS_SET(pExit->exit_info, EX_BASHED))
		{
			send_to_char( "It's been bashed off its hinges!\n\r",    ch );
			pop_call();
			return;
		}
		SET_BIT(pExit->exit_info, EX_CLOSED);
		act( "$n closes the $d.", ch, NULL, pExit->keyword, TO_ROOM );
		act( "You close the $d.", ch, NULL, pExit->keyword, TO_CHAR );

		if ((to_room = room_index[pExit->to_room]) != NULL
		&&  (pExit_rev = to_room->exit[rev_dir[door]]) != 0
		&&   room_index[pExit_rev->to_room] == ch->in_room)
		{
			CHAR_DATA *rch;

			SET_BIT( pExit_rev->exit_info, EX_CLOSED );
			for (rch = to_room->first_person ; rch ; rch = rch->next_in_room)
			{
				act( "The $d closes.", rch, NULL, pExit_rev->keyword, TO_CHAR );
			}
		}
	}
	else if ((obj = get_obj_here(ch, arg)) != NULL)
	{
		if (obj->item_type != ITEM_SPELLPOUCH && obj->item_type != ITEM_CONTAINER
		&& obj->item_type != ITEM_SHEATH && obj->item_type != ITEM_CART && obj->item_type != ITEM_QUIVER)
		{
			send_to_char( "That's not a container.\n\r", ch );
			pop_call();
			return;
		}
		if (IS_SET(obj->value[1], CONT_CLOSED))
		{
			send_to_char( "It's already closed.\n\r",    ch );
			pop_call();
			return;
		}
		if (!IS_SET(obj->value[1], CONT_CLOSEABLE))
		{
			send_to_char( "It's not closeable.\n\r",      ch );
			pop_call();
			return;
		}
		SET_BIT(obj->value[1], CONT_CLOSED);
		act( "You close $p.", ch, obj, NULL, TO_CHAR );
		if (obj->carried_by == ch)
			act( "$n closes $p.", ch, obj, NULL, TO_CAN_SEE );
		else
			act( "$n closes $p.", ch, obj, NULL, TO_ROOM );
	}
	else
	{
		act( "You see no $T here.", ch, NULL, arg, TO_CHAR );
	}
	pop_call();
	return;
}

OBJ_DATA *find_key( CHAR_DATA *ch, int key )
{
	OBJ_DATA *obj;

	push_call("find_key(%p,%p)",ch,key);

	for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
	{
		if ((obj->pIndexData->vnum == key) || ((obj->item_type == ITEM_KEY) && (key == obj->value[0])))
		{
			pop_call();
			return obj;
		}
	}
	pop_call();
	return NULL;
}

void do_knock (CHAR_DATA *ch, char *argument)
{
	int door;
	char arg[MAX_INPUT_LENGTH];
	ROOM_INDEX_DATA	*to_room;
	EXIT_DATA *pExit;
	EXIT_DATA *pExit_rev;

	push_call("do_knock(%p,%p)",ch,argument);

	argument = one_argument (argument, arg);

	if ((door = find_door(ch, arg)) == -1)
	{
		send_to_char ("That's not a door.\n\r", ch);
		pop_call();
		return;
	}

	pExit = ch->in_room->exit[door];

	if (!IS_SET(pExit->exit_info, EX_ISDOOR) ) 
	{
		send_to_char ("There's no door on it.\n\r", ch);
		pop_call();
		return;
	}

	else if (!IS_SET(pExit->exit_info, EX_CLOSED))
	{
		send_to_char ("It's already open!\n\r", ch);
		pop_call();
		return;
	}

	act( "$n knocks on the $d.", ch, NULL, pExit->keyword, TO_ROOM );
	act( "You knock on the $d.", ch, NULL, pExit->keyword, TO_CHAR );

	if ((to_room = room_index[pExit->to_room]) != NULL
	&&  (pExit_rev = to_room->exit[rev_dir[door]]) != 0
	&&   room_index[pExit_rev->to_room] == ch->in_room)
	{
		CHAR_DATA *rch;

		for (rch = to_room->first_person ; rch ; rch = rch->next_in_room)
		{
			act( "Someone knocks on the $d.", rch, NULL, pExit_rev->keyword, TO_CHAR );
		}
	}

	pop_call();
	return;
}

void do_lock( CHAR_DATA *ch, char *argument )
{
	char arg[MAX_INPUT_LENGTH];
	OBJ_DATA *obj;
	int door, dc;

	push_call("do_lock(%p,%p)",ch,argument);

	if (!check_blind(ch))
	{
		pop_call();
		return;
	}

	one_argument( argument, arg );

	if (arg[0] == '\0')
	{
		send_to_char( "Lock what?\n\r", ch );
		pop_call();
		return;
	}

	if ((door = find_door(ch, arg)) >= 0)
	{
		ROOM_INDEX_DATA *to_room;
		EXIT_DATA *pExit;
		EXIT_DATA *pExit_rev;

		pExit = ch->in_room->exit[door];

		if (!IS_SET(pExit->exit_info, EX_CLOSED))
		{
			if (IS_SET(pExit->exit_info, EX_BASHED))
			{
				send_to_char("It's been bashed off its hinges!\n\r", ch);
				pop_call();
				return;
			}
			send_to_char("The door is wide open.\n\r", ch);
			pop_call();
			return;			
		}
		if (IS_SET(pExit->exit_info, EX_LOCKED))
		{
			send_to_char( "It's already locked.\n\r", ch);
			pop_call();
			return;
		}
		if (pExit->key == -1)
		{
			send_to_char( "It can't be locked.\n\r", ch );
			pop_call();
			return;
		}
		if (IS_SET(pExit->exit_info, EX_KNOCKED))
		{
			send_to_char( "Magical energies prevent it from being locked.\n\r", ch);
			pop_call();
			return;
		}
		if (!IS_SET(pExit->exit_info, EX_CLOSED))
		{
			act( "You need to close the $d to lock it.", ch, NULL, pExit->keyword, TO_CHAR );
			pop_call();
			return;
		}

		if (pExit->key != -2 && find_key(ch, pExit->key) == NULL)
		{
			if (!learned(ch, gsn_pick_lock))
			{
				send_to_char( "You lack the key.\n\r", ch);
				pop_call();
				return;
			}

			dc = lock_dc(pExit, NULL);

			if (!pick_lock_check(ch, NULL, pick_lock_roll(ch), dc))
			{
				send_to_char( "You failed to rig the lock.\n\r", ch);
				pop_call();
				return;
			}
		}
		SET_BIT(pExit->exit_info, EX_LOCKED);
		act( "You lock the $d.", ch, NULL, pExit->keyword, TO_CHAR);
		act( "$n locks the $d.", ch, NULL, pExit->keyword, TO_ROOM);

		if ((to_room = room_index[pExit->to_room]) != NULL
		&&  (pExit_rev = to_room->exit[rev_dir[door]]) != 0
		&&  room_index[pExit_rev->to_room] == ch->in_room)
		{
			SET_BIT( pExit_rev->exit_info, EX_LOCKED );
		}
		pop_call();
		return;
	}

	if ((obj = get_obj_here(ch, arg)) == NULL)
	{
		act( "You see no $T here.", ch, NULL, arg, TO_CHAR );
		pop_call();
		return;
	}
	else
	{
		if (obj->item_type == ITEM_CONTAINER)
		{
			if (IS_SET(obj->value[1], CONT_LOCKED))
			{
				send_to_char( "It's already locked.\n\r",    ch );
				pop_call();
				return;
			}
			if (obj->value[2] < 1)
			{
				send_to_char( "It can't be locked.\n\r", ch);
				pop_call();
				return;
			}
			if (IS_SET(obj->value[1], CONT_KNOCKED))
			{
				send_to_char( "Magical energies prevent it from being locked.\n\r",    ch );
				pop_call();
				return;
			}
			if (!IS_SET(obj->value[1], CONT_CLOSED))
			{
				act( "You need to close $p to lock it.",  ch, obj, NULL, TO_CHAR );
				pop_call();
				return;
			}
	
			if (find_key(ch, obj->value[2]) == NULL)
			{
				if (!learned(ch, gsn_pick_lock))
				{
					send_to_char( "You lack the key.\n\r", ch);
					pop_call();
					return;
				}
	
				dc = lock_dc(NULL, obj);
		
				if (!pick_lock_check(ch, NULL, pick_lock_roll(ch), dc))
				{
					send_to_char( "You failed to rig the lock.\n\r", ch );
					pop_call();
					return;
				}
			}
			SET_BIT(obj->value[1], CONT_LOCKED);
			act( "You lock $p.", ch, obj, NULL, TO_CHAR );
			act( "$n locks $p.", ch, obj, NULL, TO_ROOM );
		}
		else if (obj->item_type == ITEM_PORTAL)
		{
			if (IS_SET(obj->value[2], PORTAL_LOCKED))
			{
				send_to_char( "It's already locked.\n\r",    ch );
				pop_call();
				return;
			}
			if (obj->value[1] == -1)
			{
				send_to_char( "It can't be locked.\n\r", ch);
				pop_call();
				return;
			}
			if (IS_SET(obj->value[2], PORTAL_KNOCKED))
			{
				send_to_char( "Magical energies prevent it from being locked.\n\r",    ch );
				pop_call();
				return;
			}
			if (!IS_SET(obj->value[2], PORTAL_CLOSED))
			{
				act( "You need to close $p to lock it.",  ch, obj, NULL, TO_CHAR );
				pop_call();
				return;
			}
	
			if (find_key(ch, obj->value[2]) == NULL)
			{
				if (!learned(ch, gsn_pick_lock))
				{
					send_to_char( "You lack the key.\n\r", ch);
					pop_call();
					return;
				}
	
				dc = lock_dc(NULL, obj);
		
				if (!pick_lock_check(ch, NULL, pick_lock_roll(ch), dc))
				{
					send_to_char( "You failed to rig the lock.\n\r", ch );
					pop_call();
					return;
				}
			}
			SET_BIT(obj->value[2], PORTAL_LOCKED);
			act( "You lock $p.", ch, obj, NULL, TO_CHAR );
			act( "$n locks $p.", ch, obj, NULL, TO_ROOM );
		}
		else
		{
			send_to_char( "That's not a container or portal.\n\r", ch );
			pop_call();
			return;
		}
	}
	pop_call();
	return;
}

void do_unlock( CHAR_DATA *ch, char *argument )
{
	char arg[MAX_INPUT_LENGTH];
	OBJ_DATA *obj, *key;
	int door;

	push_call("do_unlock(%p,%p)",ch,argument);

	obj=NULL;
	key=NULL;

	one_argument( argument, arg );

	if ( arg[0] == '\0' )
	{
		send_to_char( "Unlock what?\n\r", ch );
		pop_call();
		return;
	}

	if ((door = find_door(ch, arg)) >= 0)
	{
		ROOM_INDEX_DATA *to_room;
		EXIT_DATA *pExit;
		EXIT_DATA *pExit_rev;

		pExit = ch->in_room->exit[door];

		if (!IS_SET(pExit->exit_info, EX_CLOSED))
		{
			send_to_char( "It's not closed.\n\r", ch);
			pop_call();
			return;
		}
		if (!IS_SET(pExit->exit_info, EX_LOCKED) || IS_SET(pExit->exit_info, EX_KNOCKED))
		{
			send_to_char( "It's already unlocked.\n\r", ch );
			pop_call();
			return;
		}
		if (pExit->key == -1)
		{
			send_to_char( "It can't be unlocked.\n\r", ch);
			pop_call();
			return;
		}
		if (pExit->key != -2 && (key = find_key(ch, pExit->key)) == NULL)
		{
			if (learned(ch, gsn_pick_lock))
			{
				do_pick(ch, arg);
				pop_call();
				return;
			}
			send_to_char( "You lack the key.\n\r", ch);
			pop_call();
			return;
		}
		REMOVE_BIT(pExit->exit_info, EX_LOCKED);

		if (!key)
		{
			act( "You unlatch the $d.", ch, NULL, pExit->keyword, TO_CHAR);
			act( "$n latches the $d.", ch, NULL, pExit->keyword, TO_ROOM);
		}
		else
		{
			act( "You unlock the $d with $p.", ch, key, pExit->keyword, TO_CHAR);
			act( "$n unlocks the $d with $p.", ch, key, pExit->keyword, TO_ROOM);
		}

		if ((to_room = room_index[pExit->to_room]) != NULL
		&& (pExit_rev = to_room->exit[rev_dir[door]]) != NULL
		&& room_index[pExit_rev->to_room] == ch->in_room )
		{
			REMOVE_BIT(pExit_rev->exit_info, EX_LOCKED);
		}
	}
	else if ((obj = get_obj_here(ch, arg)) != NULL)
	{
		if (obj->item_type == ITEM_CONTAINER)
		{
			if (!IS_SET(obj->value[1], CONT_CLOSED))
			{
				send_to_char( "It's not closed.\n\r", ch );
				pop_call();
				return;
			}
			if (obj->value[2] == -1)
			{
				send_to_char( "It can't be unlocked.\n\r", ch );
				pop_call();
				return;
			}
			if ((key = find_key(ch, obj->value[2])) == NULL)
			{
				if (learned(ch, gsn_pick_lock))
				{
					do_pick(ch, arg);
					pop_call();
					return;
				}
				send_to_char( "You lack the key.\n\r", ch );
				pop_call();
				return;
			}
			if (!IS_SET(obj->value[1], CONT_LOCKED) || IS_SET(obj->value[1], CONT_KNOCKED))
			{
				send_to_char( "It's already unlocked.\n\r", ch );
				pop_call();
				return;
			}
	
			REMOVE_BIT(obj->value[1], CONT_LOCKED);
	
			act( "You unlock $p with $P.", ch, obj, key, TO_CHAR );
			act( "$n unlocks $p with $P.", ch, obj, key, TO_ROOM );
		}
		if (obj->item_type == ITEM_PORTAL)
		{
			if (!IS_SET(obj->value[2], PORTAL_CLOSED))
			{
				send_to_char( "It's not closed.\n\r", ch );
				pop_call();
				return;
			}
			if (obj->value[2] == -1)
			{
				send_to_char( "It can't be unlocked.\n\r", ch );
				pop_call();
				return;
			}
			if ((key = find_key(ch, obj->value[1])) == NULL)
			{
				if (learned(ch, gsn_pick_lock))
				{
					do_pick(ch, arg);
					pop_call();
					return;
				}
				send_to_char( "You lack the key.\n\r", ch );
				pop_call();
				return;
			}
			if (!IS_SET(obj->value[2], PORTAL_LOCKED) || IS_SET(obj->value[2], PORTAL_KNOCKED))
			{
				send_to_char( "It's already unlocked.\n\r", ch );
				pop_call();
				return;
			}
	
			REMOVE_BIT(obj->value[2], PORTAL_LOCKED);
	
			act( "You unlock $p with $P.", ch, obj, key, TO_CHAR );
			act( "$n unlocks $p with $P.", ch, obj, key, TO_ROOM );
		}
		else
		{
			send_to_char( "That's not a container or portal.\n\r", ch );
			pop_call();
			return;
		}
	}
	else
	{
		act( "You see no $T here.", ch, NULL, arg, TO_CHAR );
	}
	pop_call();
	return;
}

bool can_use_furniture( CHAR_DATA *ch, OBJ_DATA *obj, int position )
{
	int nocover;

	push_call("can_use_furniture(%p,%p,%p)",ch,obj,position);
	
	if (!obj || !ch)
	{
		pop_call();
		return FALSE;
	}
	
	if (ch->hitched)
	{
		act("You cannot use $p while you are hitched to $P.", ch, obj, ch->hitched, TO_CHAR);
		pop_call();
		return FALSE;
	}

	if (obj->item_type != ITEM_FURNITURE)
	{
		switch (position)
		{
			default:
				send_to_char("You can't do that on that.\n\r", ch);
				pop_call();
				return FALSE;
			case POS_CROUCHING:
				break;
			case POS_SITTING:
				send_to_char("You can't sit on or at that.\n\r", ch);
				pop_call();
				return FALSE;
			case POS_STANDING:
				send_to_char("You can't stand on that.\n\r", ch);
				pop_call();
				return FALSE;	
			case POS_RESTING:
				send_to_char("You can't rest on that.\n\r", ch);
				pop_call();
				return FALSE;	
			case POS_SLEEPING:
				send_to_char("You can't sleep on that.\n\r", ch);
				pop_call();
				return FALSE;
			case POS_KNEELING:
				send_to_char("You can't kneel at that.\n\r", ch);
				pop_call();
				return FALSE;	
		}
		switch (obj->item_type)
		{
			default:
				send_to_char("That would not grant you any cover.\n\r", ch);
				pop_call();
				return FALSE;
			case ITEM_FURNITURE:
			case ITEM_CONTAINER:
			case ITEM_FOUNTAIN:
			case ITEM_BOAT:
			case ITEM_TOTEM:
				break;
		}	
		if (get_size(ch) > obj->size + 1)
		{
			send_to_char("That is too small to grant you any cover!\n\r", ch);
			pop_call();
			return FALSE;
		}
	}

	switch (position)
	{
		default:
			send_to_char("You can't do that to that.\n\r", ch);
			pop_call();
			return FALSE;
		case POS_CROUCHING:
			break;
		case POS_SITTING:
			if (!IS_SET(obj->value[2], FURN_SIT_AT)
			&& !IS_SET(obj->value[2], FURN_SIT_ON)
			&& !IS_SET(obj->value[2], FURN_SIT_IN))
			{
				send_to_char("You can't sit on that.\n\r", ch);
				pop_call();
				return FALSE;
			}
			break;
		case POS_STANDING:
			if (!IS_SET(obj->value[2], FURN_STAND_ON)
			&& !IS_SET(obj->value[2], FURN_STAND_IN))
			{
				send_to_char("You can't stand on that.\n\r", ch);
				pop_call();
				return FALSE;	
			}
			break;
		case POS_RESTING:
			if (!IS_SET(obj->value[2], FURN_REST_ON)
			&& !IS_SET(obj->value[2], FURN_REST_IN))
			{
				send_to_char("You can't rest on that.\n\r", ch);
				pop_call();
				return FALSE;	
			}
			break;
		case POS_SLEEPING:
			if (!IS_SET(obj->value[2], FURN_SLEEP_ON)
			&& !IS_SET(obj->value[2], FURN_SLEEP_IN))
			{
				send_to_char("You can't sleep on that.\n\r", ch);
				pop_call();
				return FALSE;
			}
			break;
		case POS_KNEELING:
			if (!IS_SET(obj->value[2], FURN_KNEEL_AT))
			{
				send_to_char("You can't kneel at that.\n\r", ch);
				pop_call();
				return FALSE;	
			}
			break;
	}
	
	if ((nocover = obj->size - get_size(ch)) <= 0)
		nocover = 1;
	else
		nocover *= 2;

	if (ch->furniture != obj)
	{
		if (position == POS_CROUCHING && count_users(obj) >= nocover)
		{
			act( "There's no more room to hide behind $p.", ch, obj, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
		else if (obj->value[0] && count_users(obj) >= obj->value[0]) 
		{
			if (position == POS_KNEELING || (position == POS_SITTING && IS_SET(obj->value[2], FURN_SIT_AT)))
				act( "There's no more room at $p.", ch, obj, NULL, TO_CHAR);
			else
				act( "There's no more room on $p.", ch, obj, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
	}
	pop_call();
	return TRUE;
}

void do_stand( CHAR_DATA *ch, char *argument )
{
	OBJ_DATA *obj = NULL;
	CHAR_DATA *rch;

	push_call("do_stand(%p,%p)",ch,argument);

	if (in_combat(ch) && ch->position > POS_CROUCHING)
	{
		send_to_char("You are already standing... in combat!\n\r", ch);
		pop_call();
		return;
	}

	if (IS_AFFECTED(ch, AFF_SLEEP))
	{
		send_to_char( "You can't wake up!\n\r", ch );
		pop_call();
		return;
	}

	if (argument[0] != '\0')
	{
		if ((obj = get_obj_list(ch, argument, ch->in_room->first_content)) == NULL)
		{
			send_to_char("You do not see that here.\n\r", ch);
			pop_call();
			return;
		}
	}

	if (!IS_NPC(ch) && is_string(ch->pcdata->pose))
	{
		STRFREE (ch->pcdata->pose);
		ch->pcdata->pose = STRALLOC("");
		send_to_char( "Your pose has been removed.\n\r", ch );
	}
	
	if (obj) 
	{
		if (!can_use_furniture(ch, obj, POS_STANDING))
		{
			pop_call();
			return;
		}
	}

	if (ch->position <= POS_SITTING)
	{
		TAKE_ACTION(ch, ACTION_MOVE);
	}
	
	if (in_combat(ch) && ch->position <= POS_SITTING && !learned(ch, gsn_spring_attack))
	{
		for (rch = ch->in_room->first_person ; rch ; rch = rch->next_in_room)
		{
			if (who_fighting(rch) == ch)
				attack_of_opportunity(rch, ch);
		}
	}

	switch (ch->position)
	{
		case POS_SLEEPING:
			update_pos(ch, POS_STANDING);
			if (obj == NULL)
			{
				act( "You wake and stand up.",  ch, NULL, NULL, TO_CHAR);
				act( "$n wakes and stands up.", ch, NULL, NULL, TO_ROOM);
			}
			else if (IS_SET(obj->value[2], FURN_STAND_ON))
			{
				act( "You wake and jump onto $p.",  ch, obj, NULL, TO_CHAR);
				act( "$n wakes and jumps onto $p.", ch, obj, NULL, TO_ROOM);
			}
			else
			{
				act( "You wake and step into $p.",  ch, obj, NULL, TO_CHAR);
				act( "$n wakes and steps into $p.", ch, obj, NULL, TO_ROOM);
			}
			break;

		case POS_STANDING:
		case POS_KNEELING:
		case POS_CROUCHING:
		case POS_SITTING:
		case POS_RESTING:
			if (ch->position == POS_STANDING
			&& ((obj == NULL && ch->furniture == NULL) || obj == ch->furniture))
			{
				send_to_char("You are already standing.\n\r", ch);
				break;
			}
			if (ch->position == POS_CROUCHING && ch->furniture
			&& IS_AFFECTED(ch, AFF_HIDE))
			{
				do_visible(ch, "");
				update_pos(ch, POS_STANDING);
				break;
			}
			if (obj == NULL)
			{
				if (ch->position == POS_KNEELING)
				{
					act( "You rise up from your knee.",  ch, NULL, NULL, TO_CHAR);
					act( "$n rises from $s knee.", ch, NULL, NULL, TO_ROOM);
				}
				else
				{
					act( "You stand up.",  ch, NULL, NULL, TO_CHAR);
					act( "$n stands up.", ch, NULL, NULL, TO_ROOM);
				}
			}
			else if (IS_SET(obj->value[2], FURN_STAND_ON))
			{
				act( "You jump onto $p.", ch, obj, NULL, TO_CHAR);
				act( "$n jumps onto $p.", ch, obj, NULL, TO_ROOM);
			}
			else
			{
				act( "You step into $p.", ch, obj, NULL, TO_CHAR);
				act( "$n steps into $p.", ch, obj, NULL, TO_ROOM);
			}
			if (in_combat(ch))
			{
				update_pos(ch, POS_FIGHTING);
			}
			else
			{
				update_pos(ch, POS_STANDING);
			}
			break;
	}
	if (ch->furniture)
	{
		user_from_furniture(ch);
	}
	user_to_furniture(ch, obj);
	pop_call();
	return;
}

void do_sit (CHAR_DATA *ch, char *argument)
{
	OBJ_DATA *obj = NULL;

	push_call("do_sit(%p,%p)",ch,argument);

	if (in_combat(ch))
	{
		send_to_char_color("{118}Your adrenaline is pumping too hard to rest!\n\r", ch);
		pop_call();
		return;
	}

	if (ch->fear_level)
	{
		ch_printf_color(ch, "{018}Mind is racing too fast with fear to rest!\n\r");
		pop_call();
		return;
	}

	if (IS_AFFECTED(ch, AFF_SLEEP))
	{
		send_to_char( "You can't wake up!\n\r", ch );
		pop_call();
		return;
	}

	if (is_mounting(ch))
	{
		send_to_char("You are already sitting, on your mount.\n\r", ch);
		pop_call();
		return;
	}

	if (argument[0] != '\0')
	{
		if ((obj = get_obj_list(ch, argument, ch->in_room->first_content)) == NULL)
		{
			send_to_char("You do not see that here.\n\r", ch);
			pop_call();
			return;
		}

		if (!can_use_furniture(ch, obj, POS_SITTING))
		{
			pop_call();
			return;
		}
	}

	if (!IS_NPC(ch) && is_string(ch->pcdata->pose) && ch->position > POS_SITTING)
	{
		STRFREE (ch->pcdata->pose);
		ch->pcdata->pose = STRALLOC("");
		send_to_char( "Your pose has been removed.\n\r", ch );
	}
	
	switch (ch->position)
	{
		case POS_SLEEPING:
			update_pos(ch, POS_SITTING);
			if (obj == NULL)
			{
				act( "You wake and sit up.",  ch, NULL, NULL, TO_CHAR);
				act( "$n wakes and sits up.", ch, NULL, NULL, TO_ROOM);
			}
			else if (IS_SET(obj->value[2], FURN_SIT_ON))
			{
				act( "You wake and sit on $p.",  ch, obj, NULL, TO_CHAR);
				act( "$n wakes and sits on $p.", ch, obj, NULL, TO_ROOM);
			}
			else if (IS_SET(obj->value[2], FURN_SIT_AT))
			{
				act( "You wake and sit at $p.",  ch, obj, NULL, TO_CHAR);
				act( "$n wakes and sits at $p.", ch, obj, NULL, TO_ROOM);
			}
			else
			{
				act( "You wake and sit in $p.",  ch, obj, NULL, TO_CHAR);
				act( "$n wakes and sits in $p.", ch, obj, NULL, TO_ROOM);
			}
			break;

		case POS_RESTING:
			if (obj == NULL)
			{
				act( "You sit up.", ch, NULL, NULL, TO_CHAR);
				act( "$n sits up.", ch, NULL, NULL, TO_ROOM);
			}
			else if (IS_SET(obj->value[2], FURN_SIT_ON))
			{
				act( "You sit up on $p.", ch, obj, NULL, TO_CHAR);
				act( "$n sits up on $p.", ch, obj, NULL, TO_ROOM);
			}
			else if (IS_SET(obj->value[2], FURN_SIT_AT))
			{
				act( "You sit up at $p.", ch, obj, NULL, TO_CHAR);
				act( "$n sits up at $p.", ch, obj, NULL, TO_ROOM);
			}
			else
			{
				act( "You sit up in $p.", ch, obj, NULL, TO_CHAR);
				act( "$n sits up in $p.", ch, obj, NULL, TO_ROOM);
			}
			break;
		case POS_KNEELING:
		case POS_CROUCHING:
		case POS_SITTING:
		case POS_STANDING:
			if (ch->position == POS_SITTING
			&& ((obj == NULL && ch->furniture == NULL) || obj == ch->furniture))
			{
				send_to_char("You are already sitting down.\n\r", ch);
				break;
			}
			update_pos(ch, POS_SITTING);
			if (obj == NULL)
			{
				act( "You sit down on the ground.", ch, NULL, NULL, TO_CHAR);
				act( "$n sits down on the ground.", ch, NULL, NULL, TO_ROOM);
			}
			else if (IS_SET(obj->value[2], FURN_SIT_ON))
			{
				act( "You sit on $p.", ch, obj, NULL, TO_CHAR);
				act( "$n sits on $p.", ch, obj, NULL, TO_ROOM);
			}
			else if (IS_SET(obj->value[2], FURN_SIT_AT))
			{
				act( "You sit at $p.", ch, obj, NULL, TO_CHAR);
				act( "$n sits at $p.", ch, obj, NULL, TO_ROOM);
			}
			else
			{
				act( "You sit down in $p.", ch, obj, NULL, TO_CHAR);
				act( "$n sits down in $p.", ch, obj, NULL, TO_ROOM);
			}
			break;
	}
	if (ch->furniture)
		user_from_furniture(ch);
	user_to_furniture(ch, obj);
	pop_call();
	return;
}

void do_kneel (CHAR_DATA *ch, char *argument)
{
	OBJ_DATA *obj = NULL;
	CHAR_DATA *victim = NULL;

	push_call("do_kneel(%p,%p)",ch,argument);

	if (in_combat(ch))
	{
		send_to_char_color("{118}Your adrenaline is pumping too hard to rest!\n\r", ch);
		pop_call();
		return;
	}

	if (ch->fear_level)
	{
		ch_printf_color(ch, "{018}Mind is racing too fast with fear to rest!\n\r");
		pop_call();
		return;
	}

	if (IS_AFFECTED(ch, AFF_SLEEP))
	{
		send_to_char( "You can't wake up!\n\r", ch );
		pop_call();
		return;
	}

	if (is_mounting(ch))
	{
		send_to_char("You should dismount first.\n\r", ch);
		pop_call();
		return;
	}

	if (argument[0] != '\0')
	{
		if ((obj = get_obj_list(ch, argument, ch->in_room->first_content)) == NULL)
		{
			if ((victim = get_char_room(ch, argument)) == NULL)
			{
				send_to_char("You do not see that here.\n\r", ch);
				pop_call();
				return;
			}
		}

		if (obj && !can_use_furniture(ch, obj, POS_KNEELING))
		{
			pop_call();
			return;
		}
	}

	if (!IS_NPC(ch) && is_string(ch->pcdata->pose) && ch->position != POS_KNEELING)
	{
		STRFREE (ch->pcdata->pose);
		ch->pcdata->pose = STRALLOC("");
		send_to_char( "Your pose has been removed.\n\r", ch );
	}
	
	switch (ch->position)
	{
		case POS_KNEELING:
		case POS_CROUCHING:
		case POS_SITTING:
		case POS_STANDING:
		case POS_RESTING:
			if (ch->position == POS_KNEELING
			&& ((obj == NULL && ch->furniture == NULL) || obj == ch->furniture))
			{
				send_to_char("You are already kneeling.\n\r", ch);
				break;
			}
			update_pos(ch, POS_KNEELING);
			if (obj != NULL)
			{
				act( "You kneel at $p.", ch, obj, NULL, TO_CHAR);
				act( "$n kneel at $p.", ch, obj, NULL, TO_ROOM);
			}
			else if (victim != NULL)
			{
				act( "You kneel before $N.", ch, NULL, victim, TO_CHAR);
				act( "$n kneels before $N.", ch, NULL, victim, TO_ROOM);
			}
			else
			{
				act( "You kneel down.", ch, NULL, NULL, TO_CHAR);
				act( "$n kneels down.", ch, NULL, NULL, TO_ROOM);
			}
			break;
	}
	if (ch->furniture)
		user_from_furniture(ch);
	user_to_furniture(ch, obj);
	pop_call();
	return;
}

void do_rest (CHAR_DATA *ch, char *argument)
{
	OBJ_DATA *obj = NULL;

	push_call("do_rest(%p,%p)",ch,argument);

	if (in_combat(ch))
	{
		send_to_char_color("{118}Your adrenaline is pumping too hard to rest!\n\r", ch);
		pop_call();
		return;
	}

	if (ch->fear_level)
	{
		ch_printf_color(ch, "{018}Mind is racing too fast with fear to rest!\n\r");
		pop_call();
		return;
	}

	if (IS_AFFECTED(ch, AFF_SLEEP))
	{
		send_to_char( "You can't wake up!\n\r", ch );
		pop_call();
		return;
	}

	if (is_mounting(ch))
	{
		send_to_char("You should dismount first.\n\r", ch);
		pop_call();
		return;
	}

	if (argument[0] != '\0')
	{
		if ((obj = get_obj_list(ch, argument, ch->in_room->first_content)) == NULL)
		{
			send_to_char("You do not see that here.\n\r", ch);
			pop_call();
			return;
		}

		if (!can_use_furniture(ch, obj, POS_RESTING))
		{
			pop_call();
			return;
		}
	}

	if (!IS_NPC(ch) && is_string(ch->pcdata->pose) && ch->position > POS_SITTING)
	{
		STRFREE (ch->pcdata->pose);
		ch->pcdata->pose = STRALLOC("");
		send_to_char( "Your pose has been removed.\n\r", ch );
	}
	
	switch (ch->position)
	{
		case POS_SLEEPING:
			update_pos(ch, POS_RESTING);
			if (obj == NULL)
			{
				act( "You wake and rest easily.",  ch, NULL, NULL, TO_CHAR);
				act( "$n wakes and rests easily.", ch, NULL, NULL, TO_ROOM);
			}
			else if (IS_SET(obj->value[2], FURN_REST_ON))
			{
				act( "You wake and rest on $p.",  ch, obj, NULL, TO_CHAR);
				act( "$n wakes and rests on $p.", ch, obj, NULL, TO_ROOM);
			}
			else
			{
				act( "You wake and rest in $p.",  ch, obj, NULL, TO_CHAR);
				act( "$n wakes and rests in $p.", ch, obj, NULL, TO_ROOM);
			}
			if (in_camp(ch))
			{
				send_to_char("You camp and begin recuperating.\n\r", ch);
				act( "$n joins the camp.\n\r",ch,NULL,NULL,TO_ROOM);
				SET_AFFECT(ch, AFF2_CAMPING);
			}
			break;

		case POS_KNEELING:
		case POS_CROUCHING:
		case POS_SITTING:
		case POS_STANDING:
		case POS_RESTING:
			if (ch->position == POS_RESTING
			&& ((obj == NULL && ch->furniture == NULL) || obj == ch->furniture))
			{
				send_to_char("You are already resting.\n\r", ch);
				break;
			}
			update_pos(ch, POS_RESTING);
			if (obj == NULL)
			{
				act( "You rest easily.",  ch, NULL, NULL, TO_CHAR);
				act( "$n rests easily.", ch, NULL, NULL, TO_ROOM);
			}
			else if (IS_SET(obj->value[2], FURN_REST_ON))
			{
				act( "You rest on $p.", ch, obj, NULL, TO_CHAR);
				act( "$n rests on $p.", ch, obj, NULL, TO_ROOM);
			}
			else
			{
				act( "You rest in $p.", ch, obj, NULL, TO_CHAR);
				act( "$n rests in $p.", ch, obj, NULL, TO_ROOM);
			}
			if (in_camp(ch))
			{
				send_to_char("You camp and begin recuperating.\n\r", ch);
				act( "$n joins the camp.\n\r",ch,NULL,NULL,TO_ROOM);
				SET_AFFECT(ch, AFF2_CAMPING);
			}
			break;
	}
	if (ch->furniture)
		user_from_furniture(ch);
	user_to_furniture(ch, obj);
	pop_call();
	return;
}

void do_crouch (CHAR_DATA *ch, char *argument)
{
	OBJ_DATA *obj = NULL;

	push_call("do_crouch(%p,%p)",ch,argument);

	if (ch->position <= POS_SLEEPING)
	{
		send_to_char( "In your dreams, or what?\n\r", ch );
		pop_call();
		return;
	}

	if (IS_AFFECTED(ch, AFF_SLEEP))
	{
		send_to_char( "You can't wake up!\n\r", ch );
		pop_call();
		return;
	}

	if (is_mounting(ch))
	{
		send_to_char("You should dismount first.\n\r", ch);
		pop_call();
		return;
	}

	if (argument[0] != '\0')
	{
		if ((obj = get_obj_list(ch, argument, ch->in_room->first_content)) == NULL)
		{
			send_to_char("You do not see that here.\n\r", ch);
			pop_call();
			return;
		}

		if (!can_use_furniture(ch, obj, POS_CROUCHING))
		{
			pop_call();
			return;
		}
	}

	if (!IS_NPC(ch) && is_string(ch->pcdata->pose) && ch->position != POS_CROUCHING)
	{
		STRFREE (ch->pcdata->pose);
		ch->pcdata->pose = STRALLOC("");
		send_to_char( "Your pose has been removed.\n\r", ch );
	}
	
	switch (ch->position)
	{
		case POS_SLEEPING:
			send_to_char("In your dreams, or what?", ch);
			pop_call();
			return;

		case POS_KNEELING:
		case POS_CROUCHING:
		case POS_SITTING:
		case POS_STANDING:
		case POS_RESTING:
			if (ch->position == POS_CROUCHING
			&& ((obj == NULL && ch->furniture == NULL) || obj == ch->furniture))
			{
				send_to_char("You are already crouching down.\n\r", ch);
				break;
			}
			update_pos(ch, POS_CROUCHING);
			if (obj == NULL)
			{
				act( "You crouch down.", ch, NULL, NULL, TO_CHAR);
				act( "$n crouches down.", ch, NULL, NULL, TO_CAN_SEE);
			}
			else
			{
				act( "You crouch behind $p for cover.", ch, obj, NULL, TO_CHAR);
				act( "$n crouches behind $p for cover.", ch, obj, NULL, TO_CAN_SEE);
			}
			break;
	}
	if (ch->furniture)
		user_from_furniture(ch);
	user_to_furniture(ch, obj);
	pop_call();
	return;
}

void do_sleep( CHAR_DATA *ch, char *argument )
{
	OBJ_DATA *obj = NULL;

	push_call("do_sleep(%p,%p)",ch,argument);

	if (in_combat(ch))
	{
		send_to_char("No way! You are in combat!\n\r", ch);
		pop_call();
		return;
	}

	if (argument && ch->pcdata->last_combat + 60 > mud->current_time)
	{
		ch_printf_color(ch, "{018}Your adrenaline is pumping too hard to sleep now!\n\r");
		pop_call();
		return;
	}

	if (ch->fear_level)
	{
		ch_printf_color(ch, "{018}Mind is racing too fast with fear to sleep now!\n\r");
		pop_call();
		return;
	}

	if (is_mounting(ch))
	{
		send_to_char("You should dismount first.\n\r", ch);
		pop_call();
		return;
	}

	if (argument[0] != '\0')
	{
		if ((obj = get_obj_list(ch, argument, ch->in_room->first_content)) == NULL)
		{
			send_to_char("You do not see that here.\n\r", ch);
			pop_call();
			return;
		}

		if (!can_use_furniture(ch, obj, POS_SLEEPING))
		{
			pop_call();
			return;
		}
	}

	if (!IS_NPC(ch) && is_string(ch->pcdata->pose))
	{
		STRFREE (ch->pcdata->pose);
		ch->pcdata->pose = STRALLOC("");
		send_to_char( "Your pose has been removed.\n\r", ch );
	}
	
	switch (ch->position)
	{
		case POS_SLEEPING:
			if (obj == NULL || obj == ch->furniture)
			{
				send_to_char("You are already sleeping.\n\r", ch);
				break;
			}
			update_pos(ch, POS_RESTING);
			if (IS_SET(obj->value[2], FURN_SLEEP_ON))
			{
				act( "You wake up and go to sleep on $p.",   ch, obj, NULL, TO_CHAR);
				act( "$n wakes up and goes to sleep on $p.", ch, obj, NULL, TO_ROOM);
			}
			else
			{
				act( "You wake up and go to sleep in $p.",   ch, obj, NULL, TO_CHAR);
				act( "$n wakes up and goes to sleep in $p.", ch, obj, NULL, TO_ROOM);
			}
			update_pos(ch, POS_SLEEPING);
			break;

		case POS_KNEELING:
		case POS_CROUCHING:
		case POS_SITTING:
		case POS_STANDING:
		case POS_RESTING:
			if (obj == NULL)
			{
				act( "You go to sleep.",  ch, NULL, NULL, TO_CHAR);
				act( "$n goes to sleep.", ch, NULL, NULL, TO_ROOM);
			}
			else if (IS_SET(obj->value[2], FURN_SLEEP_ON))
			{
				act( "You go to sleep on $p.",  ch, obj, NULL, TO_CHAR);
				act( "$n goes to sleep on $p.", ch, obj, NULL, TO_ROOM);
			}
			else
			{
				act( "You go to sleep in $p.",  ch, obj, NULL, TO_CHAR);
				act( "$n goes to sleep in $p.", ch, obj, NULL, TO_ROOM);
			}
			if (ch->position != POS_RESTING && in_camp(ch))
			{
				send_to_char("You camp and begin recuperating.\n\r", ch);
				act( "$n joins the camp.\n\r", ch, NULL, NULL, TO_ROOM);
				SET_AFFECT(ch, AFF2_CAMPING);
			}
			update_pos(ch, POS_SLEEPING);
			break;
	}
	if (ch->furniture)
		user_from_furniture(ch);
	user_to_furniture(ch, obj);
	pop_call();
	return;
}

/*
 * Revamped this a good bit because it made more
 * sense to awaken in a resting position, and to
 * not break camp or remove furniture simply
 * because you woke up - Kregor
 */
void do_wake( CHAR_DATA *ch, char *argument )
{
	char arg[MAX_INPUT_LENGTH];
	CHAR_DATA *victim;

	push_call("do_wake(%p,%p)",ch,argument);

	one_argument( argument, arg );

	if (arg[0] == '\0')
		victim = ch;
	else if (!IS_AWAKE(ch))
	{
		send_to_char( "You are asleep yourself!\n\r", ch);
		pop_call();
		return;
	}
	else if ((victim = get_char_room(ch, arg)) == NULL)
	{
		send_to_char( "They aren't here.\n\r", ch);
		pop_call();
		return;
	}

	if (victim != ch)
	{
		if (IS_AFFECTED(victim, AFF2_FASCINATED))
		{
			act("You shake $N to $S senses.", ch, NULL, victim, TO_CHAR);
			act("$n shakes you to your senses!", ch, NULL, victim, TO_VICT);
			act("$n shakes $N to $S senses!", ch, NULL, victim, TO_NOTVICT);
			AFFECT_STRIP(victim, AFF2_FASCINATED);
		}
	}
	
	if (IS_AWAKE(victim))
	{
		if (victim == ch)
		{
			act( "You shake your head trying to stay awake.", ch, NULL, NULL, TO_CHAR );
			act( "$n shakes $s head trying to stay awake.", ch, NULL, NULL, TO_ROOM );
		}
		else
			act( "$N is already awake.", ch, NULL, victim, TO_CHAR );
		pop_call();
		return;
	}

	if (IS_AFFECTED(victim, AFF_SLEEP) || ch->position < POS_SLEEPING)
	{
		act( "You can't wake $N!", ch, NULL, victim, TO_CHAR );
		pop_call();
		return;
	}
	
	update_pos(victim, POS_RESTING);

	if (ch != victim)
	{
		act( "You wake $N.", ch, NULL, victim, TO_CHAR );
		act( "$n wakes $N.", ch, NULL, victim, TO_NOTVICT );
		act( "$n wakes you.", ch, NULL, victim, TO_VICT );
	}
	else
	{
		act( "You wake up.", ch, NULL, NULL, TO_CHAR );
		act( "$n wakes up.", ch, NULL, NULL, TO_ROOM );
	}

	pop_call();
	return;
}

void do_pray (CHAR_DATA *ch, char *argument)
{
	OBJ_DATA *obj = NULL;

	push_call("do_pray(%p,%p)",ch,argument);

	if (in_combat(ch))
	{
		send_to_char_color("{118}Your adrenaline is pumping too hard to pray!\n\r", ch);
		pop_call();
		return;
	}

	if (IS_AFFECTED(ch, AFF_SLEEP))
	{
		send_to_char( "You can't wake up!\n\r", ch );
		pop_call();
		return;
	}

	if (is_mounting(ch))
	{
		send_to_char("You should dismount first.\n\r", ch);
		pop_call();
		return;
	}

	if (argument[0] != '\0')
	{
		if ((obj = get_obj_list(ch, argument, ch->in_room->first_content)) == NULL)
		{
			send_to_char("You do not see that here.\n\r", ch);
			pop_call();
			return;
		}

		if (obj && !can_use_furniture(ch, obj, POS_KNEELING))
		{
			pop_call();
			return;
		}
	}
	else
	{
		for (obj = ch->in_room->first_content ; obj ; obj = obj->next_content)
		{
			if (can_use_furniture(ch, obj, POS_KNEELING))
				break;
		}
		if (!obj)
		{
			send_to_char("There is no altar here to pray at.\n\r", ch);
			pop_call();
			return;
		}
	}

	if (!IS_NPC(ch) && is_string(ch->pcdata->pose) && ch->position != POS_KNEELING)
	{
		STRFREE (ch->pcdata->pose);
		ch->pcdata->pose = STRALLOC("");
		send_to_char( "Your pose has been removed.\n\r", ch );
	}
	
	switch (ch->position)
	{
		case POS_KNEELING:
		case POS_CROUCHING:
		case POS_SITTING:
		case POS_STANDING:
		case POS_RESTING:
			if (ch->position == POS_KNEELING
			&& ((obj == NULL && ch->furniture == NULL) || obj == ch->furniture))
			{
				send_to_char("You are already kneeling.\n\r", ch);
				break;
			}
			update_pos(ch, POS_KNEELING);
			if (obj != NULL)
			{
				act( "You kneel at $p.", ch, obj, NULL, TO_CHAR);
				act( "$n kneels at $p.", ch, obj, NULL, TO_ROOM);
			}
			else
			{
				act( "You kneel down.", ch, NULL, NULL, TO_CHAR);
				act( "$n kneels down.", ch, NULL, NULL, TO_ROOM);
			}
			break;
	}
	if (ch->furniture)
		user_from_furniture(ch);
	user_to_furniture(ch, obj);
	pop_call();
	return;
}

void do_recall( CHAR_DATA *ch, char *argument )
{
	char arg[MAX_STRING_LENGTH];
	
	push_call("do_recall(%p,%p)",ch,argument);

	if (IS_NPC(ch))
	{
		pop_call();
		return;
	}

	argument = one_argument(argument, arg);
	
	if (arg[0] == '\0')
	{
		send_to_char_color("{068}Current Recall Room:\n\r", ch);
		ch_printf_color(ch, "  {178}%s\n\r", room_index[ch->pcdata->recall]->name);
		send_to_char_color("{078}Syntax: recall <set|reset>\n\r", ch);
		pop_call();
		return;
	}
	
	if (!strcasecmp(arg, "set"))
	{
		if (ch->in_room->vnum == ch->pcdata->recall)
		{
			send_to_char("You are already in your recall room.\n\r", ch);
			pop_call();
			return;
		}

		if (IS_SET(ch->in_room->room_flags, ROOM_NO_RECALL))
		{
			send_to_char( "You cannot do that in this room.\n\r", ch);
			pop_call();
			return;
		}
		if (IS_SET(ch->in_room->area->flags, AFLAG_NORECALL))
		{
			send_to_char( "You cannot do that in this area.\n\r", ch);
			pop_call();
			return;
		}
		ch->pcdata->recall = ch->in_room->vnum;
		send_to_char( "Your recall room has been set.\n\r", ch);
		pop_call();
		return;
	}

	if (!strcasecmp(arg, "reset"))
	{
		ch->pcdata->recall = ch->pcdata->death_room;
		send_to_char( "Your recall room has been reset.\n\r", ch);
		pop_call();
		return;
	}

	pop_call();
	return;
}

void do_waypoint( CHAR_DATA *ch, char *argument )
{
	char arg[MAX_STRING_LENGTH];
	char buf[MAX_STRING_LENGTH];
	char colw[10], colW[10], colc[10], colC[10];
	int cnt, value;
	
	push_call("do_recall(%p,%p)",ch,argument);
	
	if (IS_NPC(ch))
	{
		pop_call();
		return;
	}
	
	if (!learned(ch, gsn_teleport)
	&& !learned(ch, gsn_greater_teleport)
	&& !learned(ch, gsn_shadow_walk)
	&& !learned(ch, gsn_pass_plant)
	&& !learned(ch, gsn_dimension_door)
	&& !learned(ch, gsn_teleportation_circle))
	{
		send_to_char("You do not know how to teleport.", ch);
		pop_call();
		return;
	}

	sprintf(colw, "{078}");
	sprintf(colW, "{178}");
	sprintf(colc, "{068}");
	sprintf(colC, "{168}");

	if (ch->in_room->sector_type == SECT_ETHEREAL)
	{
		send_to_char( "You don't know the path back to the real universe.\n\r", ch);
		pop_call();
		return;
	}

	if (ch->in_room->sector_type == SECT_ASTRAL)
	{
		send_to_char( "You are too far out of your mind to recall anything.\n\r", ch);
		pop_call();
		return;
	}

	if (argument[0] == '\0')
	{
		sprintf(buf, "%sCurrent Teleport Waypoints:\n\r", colc);
		for (cnt = 1 ; cnt <= UMIN(ch->level, MAX_WAYPOINT) ; cnt++)
		{
			if (ch->pcdata->waypoint[cnt] <= 0 || room_index[ch->pcdata->waypoint[cnt]] == NULL)
			{
				cat_sprintf(buf, "  %s%2d%s] %sNone\n\r", colW, cnt, colc, colw);
				continue;
			}
	
			cat_sprintf(buf, "  %s%2d%s] %s%s\n\r", colW, cnt, colc, colw, room_index[ch->pcdata->waypoint[cnt]]->name);
		}
		cat_sprintf(buf, "%sSyntax: waypoint <set|clear> <number>\n\r", colw);
		send_to_char_color(buf, ch);
		pop_call();
		return;
	}
	
	argument = one_argument(argument, arg);
	
	if (!strcasecmp(arg, "set"))
	{
		if (IS_SET(ch->in_room->room_flags, ROOM_NO_ASTRAL)
		|| IS_SET(ch->in_room->room_flags, ROOM_PRIVATE))
		{
			send_to_char( "You are unable to do that in this room.\n\r", ch);
			pop_call();
			return;
		}
		if (IS_SET(ch->in_room->area->flags, AFLAG_NOTELEPORT))
		{
			send_to_char( "You cannot do that in this area.\n\r", ch);
			pop_call();
			return;
		}
		if (!is_number(argument) || argument[0] == '\0')
		{
			send_to_char( "Set what waypoint number?\n\r", ch);
			pop_call();
			return;
		}
		if ((value = atol(argument)) > UMIN(ch->level, MAX_WAYPOINT))
		{
			send_to_char( "You cannot set that many waypoints. Clear one if you want another.\n\r", ch);
			pop_call();
			return;
		}
		if (ch->pcdata->waypoint[value] > 0)
		{
			send_to_char( "That waypoint is already set. Clear it if you wish to reset it.\n\r", ch);
			pop_call();
			return;
		}
		
		ch->pcdata->waypoint[value] = ch->in_room->vnum;
		act( "You study the room intently and keep it in your memory.", ch, NULL, NULL, TO_CHAR);
		act( "$n studies the room intently.", ch, NULL, NULL, TO_ROOM);
		pop_call();
		return;
	}

	if (!strcasecmp(arg, "clear"))
	{
		if (!is_number(argument) || argument[0] == '\0')
		{
			send_to_char( "Clear what waypoint number?\n\r", ch);
			pop_call();
			return;
		}
		if ((value = atol(argument)) > ch->level / 2)
		{
			send_to_char( "You cannot set that many waypoints. Clear one if you want another\n\r", ch);
			pop_call();
			return;
		}
		if (ch->pcdata->waypoint[value] <= 0)
		{
			send_to_char( "That waypoint is already clear.\n\r", ch);
			pop_call();
			return;
		}
		ch_printf_color(ch, "Your dismiss the image of %s from your memory.\n\r", room_index[ch->pcdata->waypoint[value]]->name);
		ch->pcdata->waypoint[value] = 0;
		pop_call();
		return;
	}
	pop_call();
	return;
}


/*
 * Commmand to enter a portal
 */
void do_enter( CHAR_DATA *ch, char *argument)
{
	OBJ_DATA *portal;
	int destination, old_room;
	CHAR_DATA *fch, *fch_next;

	push_call("do_enter(%p,%p)",ch,argument);

	if (argument[0] == '\0')
	{
		send_to_char("Enter what?\n\r", ch);
		pop_call();
		return;
	}

	old_room = ch->in_room->vnum;
	portal   = get_obj_list(ch, argument, ch->in_room->first_content);

	if (portal == NULL)
	{
		send_to_char("You cannot find that to enter.\n\r", ch);
		pop_call();
		return;
	}

	if (portal->item_type != ITEM_PORTAL)
	{
		send_to_char("You cannot seem to find a way in.\n\r", ch);
		pop_call();
		return;
	}
	
	if (!class_allowed(portal, ch, FALSE)
	|| is_affected(ch, gsn_dimensional_anchor)
	|| IS_SET(ch->in_room->room_flags, ROOM_NO_ASTRAL))
	{
		act("Mystic wards prevent you from entering $p.", ch, portal, NULL, TO_CHAR);
		pop_call();
		return;
	}
	
	if (IS_SET(portal->value[2], PORTAL_CLOSED))
	{
		act("$p is closed.", ch, portal, NULL, TO_CHAR);
		pop_call();
		return;
	}	

	if (IS_SET(portal->value[2], PORTAL_NO_ENTER))
	{
		act("You cannot find a way to enter $p.", ch, portal, NULL, TO_CHAR);
		pop_call();
		return;
	}	

	if (IS_SET(portal->value[2], PORTAL_RANDOM))
	{
		destination = get_random_room_index(ch, 1, MAX_VNUM-1)->vnum;
	}
	else if (IS_SET(portal->value[2], PORTAL_RANDOM_AREA))
	{
		destination = get_random_room_index(ch, portal->pIndexData->area->low_r_vnum, portal->pIndexData->area->hi_r_vnum)->vnum;
	}
	else
	{
		destination = portal->value[3];
	}

	if (get_room_index(destination) == NULL || destination == ch->in_room->vnum)
	{
		act( "$p doesn't seem to go anywhere.", ch, portal, NULL, TO_CHAR);
		pop_call();
		return;
	}

	if (!move_loss(ch, NULL, 1))
	{
		pop_call();
		return;
	}
	
	if (IS_SET(portal->value[2], PORTAL_STEP_THROUGH))
	{
		act( "You step through $p.", ch, portal, NULL, TO_CHAR);
		act( "$n steps through $p.", ch, portal, NULL, TO_SEEHEAR);
	}
	else if (IS_SET(portal->value[2], PORTAL_STEP_INTO))
	{
		act( "You step into $p.", ch, portal, NULL, TO_CHAR);
		act( "$n steps into $p.", ch, portal, NULL, TO_SEEHEAR);
	}
	else
	{
		act( "You enter $p.", ch, portal, NULL, TO_CHAR);
		act( "$n enters $p.", ch, portal, NULL, TO_SEEHEAR);
	}

	char_to_room(ch, destination, TRUE);

	act( "$n emerges out of a rift in space.", ch, NULL, NULL, TO_SEEHEAR);
	do_look(ch, "auto");

	for (fch = room_index[old_room]->first_person ; fch ; fch = fch_next)
	{
		fch_next = fch->next_in_room;

		if (fch->master == ch && IS_PET(fch, ch) && fch->position == POS_STANDING)
		{
			act( "You follow $N.", fch, NULL, ch, TO_CHAR);
			act( "$n follows $N.", fch, NULL, ch, TO_SEEHEAR);

			char_to_room(fch, destination, TRUE);
		
			act( "$n emerges out of a rift in space.", fch, NULL, NULL, TO_SEEHEAR);
			do_look(fch, "auto");
			
			TAKE_ACTION(fch, ACTION_MOVE);
		}
	}

	mprog_greet_trigger(ch);
	oprog_greet_trigger(ch);
	rprog_greet_trigger(ch);
	
	TAKE_ACTION(ch, ACTION_MOVE);

	if (portal->value[0] > 0)
	{
		portal->value[0]--;

		if (portal->value[0] == 0)
		{
			act( "$p fades out of existence.", fch, portal, NULL, TO_CHAR);
			act( "$p fades out of existence.", fch, portal, NULL, TO_ROOM);
			junk_obj(portal);
			pop_call();
			return;
		}
	}

	if (IS_SET(portal->value[2], PORTAL_GOWITH))
	{
		send_to_room(format("%s fades from existence.\n\r", portal->short_descr), room_index[old_room]);
		obj_from_room(portal);
		obj_to_room(portal, ch->in_room->vnum);
		act( "$p fades into reality.", ch, portal, NULL, TO_ROOM);
		pop_call();
		return;
	}
	pop_call();
	return;
}


void do_follow (CHAR_DATA * ch, char *argument)
{
	char arg[MAX_INPUT_LENGTH];
	CHAR_DATA *victim;

	push_call("do_follow(%p,%p)",ch,argument);

	one_argument (argument, arg);

	if (arg[0] == '\0')
	{
		send_to_char ("Follow whom?\n\r", ch);
		pop_call();
		return;
	}

	if (in_combat(ch))
	{
		send_to_char("No way! You are still fighting!\n",ch);
		pop_call();
		return;
	}

	if ((victim = get_char_room (ch, arg)) == NULL)
	{
		send_to_char ("They aren't here.\n\r", ch);
		pop_call();
		return;
	}

	if (victim == ch)
	{
		if (ch->master == NULL)
		{
			send_to_char ("You already follow yourself.\n\r", ch);
			pop_call();
			return;
		}
		stop_follower(ch);
		pop_call();
		return;
	}

	if (!IS_NPC(ch))
	{
		if (is_master(victim, ch) && IS_NPC (victim))
		{
			send_to_char ("You may not follow a slave or pet.\n\r", ch);
			pop_call();
			return;
		}
	}
	
	if (IS_AFFECTED(ch, AFF_DOMINATE))
	{
		send_to_char ("You only feel like following your master.\n\r", ch);
		pop_call();
		return;
	}

	/* poll the block list of the victim */

	if (blocking(victim, ch))
	{
		act ("$E eludes you at every step.", ch, 0, victim, TO_CHAR);
		pop_call();
		return;
	}

	if (ch->master != NULL)
	{
		stop_follower (ch);
	}
	add_follower (ch, victim);
	pop_call();
	return;
}

void add_follower (CHAR_DATA * ch, CHAR_DATA * master)
{
	PET_DATA *pet;

	push_call("add_follower(%p,%p)",ch,master);

	if (ch->master != NULL)
	{
		bug ("Add_follower: non-null master.", 0);
		pop_call();
		return;
	}

	ch->master = master;
	ch->leader = NULL;
	if (can_see(master, ch))
	{
		act ("$n now follows you.", ch, NULL, master, TO_VICT);
	}
	act ("You now follow $N.", ch, NULL, master, TO_CHAR);

	ALLOCMEM(pet, PET_DATA, 1);
	pet->ch = ch;
	LINK(pet, mud->f_pet, mud->l_pet, next, prev);
	
	if (IS_ACT(ch, ACT_FAMILIAR) && !IS_NPC(master))
	{
		master->pcdata->familiar = ch->race;
	}

	pop_call();
	return;
}


void stop_follower (CHAR_DATA * ch)
{
	PET_DATA *pet;

	push_call("stop_follower(%p)",ch);

	if (ch == NULL)
	{
		bug( "stop_follower : ch == NULL.",0);
		pop_call();
		return;
	}

	if (ch->master == NULL)
	{
		bug( "Stop_follower: null master.", 0 );
		pop_call();
		return;
	}

	if (can_see(ch->master, ch))
	{
		act ("$n stops following you.", ch, NULL, ch->master, TO_VICT);
	}
	act ("You stop following $N.", ch, NULL, ch->master, TO_CHAR);

	if (ch->master->mounting == ch)
	{
		ch->master->mounting = NULL;
	}
	if (IS_ACT(ch, ACT_FAMILIAR) && !IS_NPC(ch->master))
	{
		ch->master->pcdata->familiar = RACE_NONE;
	}
	
	ch->master = NULL;
	ch->leader = NULL;

	for (pet = mud->f_pet ; pet ; pet = pet->next)
	{
		if (pet->ch == ch)
		{
			UNLINK(pet, mud->f_pet, mud->l_pet, next, prev);
			FREEMEM(pet);
			break;
		}
	}
	if (IS_ACT(ch, ACT_PET))
	{
		if (ch->in_room)
			act( "$n has left the game.", ch, NULL, NULL, TO_ROOM);
		junk_mob(ch);
	}

	pop_call();
	return;
}


void die_follower (CHAR_DATA * ch)
{
	PET_DATA *pch, *pch_next;

	push_call("die_follower(%p)",ch);

	ch->position = POS_DEAD;

	if (ch->master != NULL)
	{
		stop_follower(ch);
	}
	ch->leader = NULL;

	for (pch = mud->f_pet ; pch ; pch = pch_next)
	{
		pch_next = pch->next;

		if (pch->ch->master == ch)
		{
			stop_follower(pch->ch);
		}
	}
	pop_call();
	return;
}


/*
 * Is cart hitched to someone - Kregor
 */
CHAR_DATA *is_hitched( OBJ_DATA * obj )
{
	CHAR_DATA *fch;

	push_call("is_hitched(%p)",obj);

	if (obj->in_room == NULL)
	{
		log_printf("is_hitched: obj->in_room == NULL");
		pop_call();
		return FALSE;
	}

	for (fch = obj->in_room->first_person ; fch ; fch = fch->next_in_room)
	{
		if (fch->hitched == obj)
		{
			pop_call();
			return fch;
		}
	}
	pop_call();
	return NULL;
}

/*
 * remove cart from char.
 */
void user_from_cart( CHAR_DATA *ch )
{
	push_call("user_from_cart(%p)",ch);

	if (ch->hitched == NULL)
	{
		pop_call();
		return;
	}

	ch->hitched = NULL;

	pop_call();
	return;
}


/*
 * Hitch a cart to yourself, or a target pet - Kregor
 */
void do_hitch( CHAR_DATA *ch, char *argument )
{
	CHAR_DATA *victim;
	OBJ_DATA *obj;
	char arg[MAX_INPUT_LENGTH];

	push_call("do_hitch()");
	
	if (*argument == '\0')
	{
		send_to_char("Hitch what to whom?\n\r", ch);
		pop_call();
		return;
	}
	argument = one_argument(argument, arg);
	
	if ((obj = get_obj_room(ch, arg)) == NULL)
	{
		send_to_char("There's no cart by that name here.\n\r", ch);
		pop_call();
		return;
	}
	if (!IS_OBJ_TYPE(obj, ITEM_CART))
	{
		act("$p is not a cart.", ch, obj, NULL, TO_CHAR);
		pop_call();
		return;
	}
	
	if (*argument == '\0')
	{
		victim = ch;
	}
	else if ((victim = get_char_room(ch, argument)) == NULL)
	{
		send_to_char("There's no one by that name here.\n\r", ch);
		pop_call();
		return;
	}
	else if (ch != victim && !is_master(victim, ch))
	{
		act("$N is not yours to hitch $p onto.", ch, obj, victim, TO_CHAR);
		pop_call();
		return;
	}
	
	if (is_hitched(obj))
	{
		act("$p is already hitched to someone.", ch, obj, NULL, TO_CHAR);
		pop_call();
		return;
	}
	
	if (victim->hitched)
	{
		if (victim == ch)
			act("You are already hitched to $p.", ch, victim->hitched, NULL, TO_CHAR);
		else
			act("$N is already hitched to $p.", ch, victim->hitched, victim, TO_CHAR);
		pop_call();
		return;
	}
	
	victim->hitched = obj;
	
	act("$p is hitched to $N.", ch, obj, victim, TO_NOTVICT);
	act("$p is hitched to you.", ch, obj, victim, TO_VICT);
	if (ch == victim)
		act("$p is hitched to you.", ch, obj, victim, TO_CHAR);
	else
		act("$p is hitched to $N.", ch, obj, victim, TO_CHAR);
	
	pop_call();
	return;
}


/*
 * Unhitch a cart - Kregor
 */
void do_unhitch( CHAR_DATA *ch, char *argument )
{
	CHAR_DATA *victim;
	OBJ_DATA *obj;
	char arg[MAX_INPUT_LENGTH];

	push_call("do_unhitch()");
	
	if (*argument == '\0')
	{
		send_to_char("Unhitch what?\n\r", ch);
		pop_call();
		return;
	}
	argument = one_argument(argument, arg);
	
	if ((obj = get_obj_room(ch, arg)) == NULL)
	{
		send_to_char("There's no cart by that name here.\n\r", ch);
		pop_call();
		return;
	}
	if (!IS_OBJ_TYPE(obj, ITEM_CART))
	{
		act("$p is not a cart.", ch, obj, NULL, TO_CHAR);
		pop_call();
		return;
	}
	if ((victim = is_hitched(obj)) == NULL)
	{
		act("$p is not hitched to anyone.", ch, obj, NULL, TO_CHAR);
		pop_call();
		return;
	}
	
	if (ch != victim && !is_master(victim, ch))
	{
		act("$N is not yours to unhitch.", ch, NULL, victim, TO_CHAR);
		pop_call();
		return;
	}
	
	user_from_cart(victim);
	
	act("$p is unhitched from $N.", ch, obj, victim, TO_NOTVICT);
	act("$p is unhitched from you.", ch, obj, victim, TO_VICT);
	if (ch == victim)
		act("$p is hitched from you.", ch, obj, victim, TO_CHAR);
	else
		act("$p is hitched from $N.", ch, obj, victim, TO_CHAR);
	
	pop_call();
	return;
}