/* ************************************************************************
*  file: act.movement.c , Implementation of commands      Part of DIKUMUD *
*  Usage : Movement commands, close/open & lock/unlock doors.             *
*  Copyright (C) 1990, 1991 - see 'license.doc' for complete information. *
************************************************************************* */




#include CONFIG

#if HAVE_STRINGS_H

#include <strings.h>
#endif

#if HAVE_STRING_H
#include <string.h>
#endif


#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "interp.h"
#include "db.h"
#include "event.h"
#include "bio.h"
#include "skills.h"
#include "error.h"
#include "proto.h"

/*   external vars  */

extern struct room_data *world;
extern struct zone_data *zone_table;
extern struct obj_index_data *obj_index;
extern int rev_dir[];
extern int max_dir[];
extern char *dirs[]; 
extern int movement_loss[];
extern struct command_info commands[];
/* external functs */

int special(struct char_data *ch, int cmd, char *arg);
void death_cry(struct char_data *ch);
struct obj_data *get_obj_in_list_vis(struct char_data *ch, char *name,
    struct obj_data *list);

/* I think we'll need a way to determine exit-objects from a direction */

struct obj_info_exit *get_exit(int room,int dir)
{
    struct obj_data *o;
    struct obj_info_exit *exit;

    for(o=world[room].contents;o;o=o->next_content)
        if((exit= (struct obj_info_exit *) get_obj_info(o,ITEM_EXIT)))
            if(exit->dir==dir)
                return exit;
    return NULL;
}

/* This is a routine to check for problems with particular exits which */
/* don't apply room-wide    -Sman */

int leave_by_exit(struct char_data *ch,int cmd)
{
    char firstdir=cmd,found=0;
    struct char_data *k,*in_way=0;
    struct weather_data *cond;
    struct obj_info_exit *exit_info;
    int i;

    if(!(exit_info = get_exit(ch->in_room,cmd))) {
        return ERROR_LOCATION;
    }

    /* First check if we're crowded in a SINGLE_FILE room */
    if(world[ch->in_room].room_flags & SINGLE_FILE) {
	for(i=0;i<max_dir[zone_table[world[ch->in_room].zone].dir_system] &&
                   !found;i++)
	    if(get_exit(ch->in_room,i)) {
		firstdir=i;
		found=1;
	    }
	if(!found) {
	    log("Invalid direction in leave_by_exit");
	    return ERROR_LOCATION;
	}
	if(firstdir==cmd) {
	    if(world[ch->in_room].people!=ch) {
		for(k=world[ch->in_room].people;!in_way && k;
			k=k->next_in_room) {
		    in_way=k;
		    if(k->next_in_room==ch)
			break;
		}
	    }
	} else if(ch->next_in_room) {
	    in_way=ch->next_in_room;
	}
	if(in_way) {
	    act("Oof! It seems that $N is in your way.",FALSE,ch,0,in_way,TO_CHAR);
	    act("Oof! $n runs into you.",FALSE,ch,0,in_way,TO_VICT);
	    act("$n bumps into $N.",TRUE,ch,0,in_way,TO_NOTVICT);
	    return ERROR_PHYS_PREVENTS;
	}
    }

    /* This is for the wind direction, so the wind can */
    /* be too strong to walk against it... */
    if(!(world[ch->in_room].room_flags & INDOORS)) {
	cond=&zone_table[world[ch->in_room].zone].conditions;
	if(cond->wind_dir == cmd && cond->windspeed > 70) {
	    send_to_char("The wind's too strong.\n\r",ch);
	    return ERROR_PHYS_PREVENTS;
	}
    }

    if(world[exit_info->to_room].sector_type == SECT_NO_GROUND) {
	if(cmd==4 && GET_POS(ch) < POS_FLY) {
	    send_to_char("You'd have to fly there!\n\r\n\r",ch);
	    return ERROR_PHYS_PREVENTS;
	}
    }
    return OKAY;
}

/* Needed this for teleports and such */
int can_enter_room(struct char_data *ch,int room, bool show_msg)
{
    bool has_boat;
    struct obj_data *obj;

    if (world[room].sector_type == SECT_WATER_NOSWIM &&
		GET_POS(ch) < POS_LEVITATE) {
	has_boat = FALSE;
	/* See if char is carrying a boat */
	for (obj=ch->carrying; obj; obj=obj->next_content)
	    if (get_obj_info(obj,ITEM_BOAT))
		has_boat = TRUE;
	if (!has_boat) {
	    if(show_msg)
		send_to_char("You need a boat to go there.\n\r\n\r", ch);
	    return ERROR_PHYS_PREVENTS;
	}
    }

    if(world[room].sector_type == SECT_NO_GROUND) {
	if(GET_POS(ch) < POS_LEVITATE) {
	    if(show_msg)
		send_to_char("You'd have to walk on air!",ch);
	    return ERROR_PHYS_PREVENTS;
	}
    }
    /* If we get this far, it's okay to go this way */
    return OKAY;
}

int can_go(struct char_data *ch, int dir)
{
    return NULL;
}

int do_simple_move(struct char_data *ch, int cmd, int following)
/* Assumes, 
    1. That there is no master and no followers.
    2. That the direction exists. 

*/
{
    char tmp[80],buf[MAX_STRING_LENGTH];
    int was_in;
    int need_movement;
    int to_room;
    int retval;
    struct obj_info_exit *exit_info;
    struct bio_attr *ba;

    ba=find_bio_attr(ch->physical->species,BIO_MOBILE);
    if(!ba || !ba->value1) {
        send_to_char("Your body cannot move!\n\r",ch);
        return ERROR_BODY;
    }

    /* The order for this is debateable -Sman */
    retval = leave_by_exit(ch,cmd);
    if (retval != OKAY) {
	return retval;
    }

    exit_info = get_exit(ch->in_room,cmd);

    to_room = exit_info->to_room;

    retval=can_enter_room(ch,to_room,TRUE);
    if(retval != OKAY)
	return retval;

    need_movement = (movement_loss[world[ch->in_room].sector_type]+
    movement_loss[world[to_room].sector_type]) / 2;


    if(GET_MOVE(ch)<need_movement)
    {
	if(!following)
	    send_to_char("You are too exhausted.\n\r",ch);
	else
	    send_to_char("You are too exhausted to follow.\n\r",ch);

	return ERROR_NO_MOVES;
    }

    GET_MOVE(ch) -= need_movement;

    /* You may notice th order of event queueing is odd - this is
       so the moving person doesn't learn of its own movements */

/*    if (!IS_AFFECTED(ch, AFF_SNEAK)) {*/
	sprintf(tmp, "$n leaves %s.", dirs[cmd]);
	act(tmp, TRUE, ch, 0,0,TO_ROOM);
        add_event_room(to_room,EVENT_ARRIVE,0,ch->id,ID_NOBODY);
/*    }*/

    was_in = ch->in_room;

    char_from_room(ch);

    char_to_room(ch, to_room,cmd);

    buf[0]=0;
/*    if (!IS_AFFECTED(ch, AFF_SNEAK)) {*/
	strcat(buf,"$n has arrived.");
	act(buf, TRUE, ch, 0,0, TO_ROOM);
        add_event_room(was_in,EVENT_DEPART,0,ch->id,ID_NOBODY);
/*    }*/

    do_look(ch, "\0",15);

    return OKAY;
}

int do_move(struct char_data *ch, char *argument, int cmd)
{
    char tmp[MAX_INPUT_LENGTH+20];
    int was_in;
    struct follow_type *k, *next_dude;
    struct obj_data *o;
    struct obj_info_exit *exit_info;
    bool found=FALSE;

    if(ch->specials.fighting) {
        send_to_char("You seem to be engaged in something at the moment.\n\r",ch);
        return ERROR_FAILED;
    }

    for(o=world[ch->in_room].contents;o;o=o->next_content)
        if((exit_info= (struct obj_info_exit *) get_obj_info(o,ITEM_EXIT)))
            if(exit_info->dir==cmd) {
                found++; /* Test if more than one way out?? */
                break;
            }
    if (!found) {
	send_to_char("Alas, you cannot go that way...\n\r", ch);
        return ERROR_FAILED;
    }

    if (IS_SET(exit_info->exit_info, EX_CLOSED)) {
        if (o->name) {
	    sprintf(tmp, "The %s seems to be closed.\n\r",
		    fname(o->name));
            send_to_char(tmp, ch);
        } else {
            send_to_char("It seems to be closed.\n\r", ch);
	}
        return ERROR_PHYS_PREVENTS;
    }
    if (exit_info->to_room == NOWHERE) {
        send_to_char("Alas, that way leads nowhere.\n\r", ch);
        return ERROR_FAILED;
    }
    if (!ch->followers && !ch->master)
        return(do_simple_move(ch,cmd,FALSE));
/*
    if (IS_AFFECTED(ch, AFF_CHARM) && (ch->master) && 
	     (ch->in_room == ch->master->in_room)) {
        send_to_char("The thought of leaving your master makes you weep.\n\r",
                ch);
        act("$n bursts into tears.", FALSE, ch, 0, 0, TO_ROOM);
        return ERROR_MAGIC_PREVENTS;
    }*/

    was_in = ch->in_room;
    if (do_simple_move(ch, cmd, TRUE) == OKAY) { /* Move the character */
        if (ch->followers) { /* If success move followers */
            for(k = ch->followers; k; k = next_dude) {
                next_dude = k->next;
                if ((was_in == k->follower->in_room) &&
			      (GET_POS(k->follower) >= POS_STAND)) {
                    act("You follow $N.", FALSE, k->follower, 0, ch, TO_CHAR);
                    send_to_char("\n\r", k->follower);
                    /*do_move(k->follower, argument, cmd);*/
                    sprintf(tmp,"%s %s",commands[cmd+1].command_name,argument);
                    command_interpreter(k->follower,tmp);
		}
            }
	}
        return OKAY;
    }
    return ERROR_FAILED;
}


struct obj_data *match_door(struct obj_info_exit *door,int from, int to)
{
    struct obj_data *o,*best=NULL;
    struct obj_info_exit *exit;

    for(o=world[to].contents;o;o=o->next) 
        if((exit = (struct obj_info_exit *) get_obj_info(o,ITEM_EXIT))) {
            if(exit->to_room == from) {
                best = o;
                if(exit->dir == rev_dir[door->dir]) /* Optimal answer */
                    return o;
            }
        }
    return best;
}

int do_gen_opening(struct char_data *ch, char *argument, int cmd)
{
    int other_room,ret;
    char type[MAX_INPUT_LENGTH], dir[MAX_INPUT_LENGTH], buf[MAX_STRING_LENGTH];
    struct obj_data *obj,*back;
    struct obj_info_exit *exit,*back_e;
    struct obj_info_container *container;

    char *openstr[] = {"open","close","lock","unlock","pick"};

    argument_interpreter(argument, type, dir);

    if (!*type) {
        sprintf(buf,"%s what?\n\r",openstr[cmd-1]);
        (void)CAP(buf);
	send_to_char(buf, ch);
	return ERROR_SYNTAX;
    }

    ret=generic_find(argument, FIND_OBJ_INV|FIND_OBJ_ROOM, ch, (void *)&obj);
    if(!ret) {
        send_to_char("Can't find it.\n\r",ch);
        return ERROR_MISSING_TARGET;
    }

    /*** Containers ***/
    if((container=(struct obj_info_container *)get_obj_info(obj,ITEM_CONTAINER)))
    {
        switch(cmd) {
            case OPENING_OPEN:
                if (!IS_SET(container->lock_state, CONT_CLOSED)) {
                    send_to_char("But it's already open!\n\r", ch);
                    return ERROR_FAILED;
                }
                if (!IS_SET(container->lock_state, CONT_CLOSEABLE)) {
                    send_to_char("You can't do that.\n\r", ch);
                    return ERROR_FAILED;
                }
                if (IS_SET(container->lock_state, CONT_LOCKED)) {
                    send_to_char("It seems to be locked.\n\r", ch);
                    return ERROR_FAILED;
                }

                REMOVE_BIT(container->lock_state, CONT_CLOSED);
                act("$n opens $p.", FALSE, ch, obj, 0, TO_ROOM);
                return OKAY;
                break;
            case OPENING_CLOSE:
                if (IS_SET(container->lock_state, CONT_CLOSED)) {
                    send_to_char("But it's already closed!\n\r", ch);
                    return ERROR_FAILED;
                }
                if (!IS_SET(container->lock_state, CONT_CLOSEABLE)) {
                    send_to_char("You can't do that.\n\r", ch);
                    return ERROR_FAILED;
                }

                SET_BIT(container->lock_state, CONT_CLOSED);
                act("$n closes $p.", FALSE, ch, obj, 0, TO_ROOM);
                return OKAY;
                break;
            case OPENING_LOCK:
                if (!IS_SET(container->lock_state, CONT_CLOSED)) {
                    send_to_char("Maybe you should close it first...\n\r", ch);
                    return ERROR_FAILED;
                }
                if (container->lock_number < 0) {
                    send_to_char("That thing can't be locked.\n\r", ch);
                    return ERROR_FAILED;
                }
                if (!has_key(ch, container->lock_number)){
                    send_to_char("You don't seem to have the right key.\n\r",ch);	
                    return ERROR_FAILED;
                }
                if (IS_SET(container->lock_state, CONT_LOCKED)) {
                    send_to_char("It is locked already.\n\r", ch);
                    return ERROR_FAILED;
                }
                SET_BIT(container->lock_state, CONT_LOCKED);
                send_to_char("*Click*\n\r",ch);
                act("$n locks $p - 'click', it says.",FALSE,ch,obj,0,TO_ROOM);
                return OKAY;

                break;
            case OPENING_UNLOCK:
                if (container->lock_number < 0) {
                    send_to_char("That can't be locked in the first place.\n\r", ch);
                    return ERROR_FAILED;
                }
                if (!has_key(ch, container->lock_number)){
                    send_to_char("You don't seem to have the right key.\n\r",ch);	
                    return ERROR_FAILED;
                }
                if (!IS_SET(container->lock_state, CONT_LOCKED)) {
                    send_to_char("It is unlocked already.\n\r", ch);
                    return ERROR_FAILED;
                }
                REMOVE_BIT(container->lock_state, CONT_LOCKED);
                send_to_char("*Click*\n\r", ch);
                act("$n unlocks $p - 'click', it says.", FALSE, ch, obj, 0,TO_ROOM);
                return OKAY;

                break;
            case OPENING_PICK:
        }

    /*** exits ***/
    } else if ((exit = (struct obj_info_exit *) get_obj_info(obj, ITEM_EXIT))) {
        switch(cmd) {
            case OPENING_OPEN:
                if (!IS_SET(exit->exit_info, EX_ISDOOR)) {
                    send_to_char("That's impossible, I'm afraid.\n\r", ch);
                    return ERROR_FAILED;
                }
                if (!IS_SET(exit->exit_info, EX_CLOSED)) {
                    send_to_char("It's already open!\n\r", ch);
                    return ERROR_FAILED;
                }
                if (IS_SET(exit->exit_info, EX_LOCKED)) {
                    send_to_char("It seems to be locked.\n\r", ch);
                    return ERROR_FAILED;
                }

                REMOVE_BIT(exit->exit_info, EX_CLOSED);
                if(obj->name)
                    act("$n opens the $F.",FALSE,ch,0,obj->name,TO_ROOM);
                else
                    act("$n opens the door.", FALSE, ch, 0, 0, TO_ROOM);

                /* now for opening the OTHER side of the door! */
                if ((other_room = exit->to_room) != NOWHERE) {
                    if ((back = match_door(exit,ch->in_room,other_room))) {
                        back_e = (struct obj_info_exit *)get_obj_info(back,ITEM_EXIT);
        	        if (back_e->to_room == ch->in_room) {
                             REMOVE_BIT(back_e->exit_info, EX_CLOSED);
                             if (back->name) {
	                         sprintf(buf,"The %s is opened from the other side.\n\r", fname(back->name));
	        		    send_to_room(buf, exit->to_room);
                            } else {
    		        	send_to_room("The door is opened from the other side.\n\r",
    		        	exit->to_room);
		            }
                        }
                    }
	        }
                return OKAY;
                break;
            case OPENING_CLOSE:
                if (!IS_SET(exit->exit_info, EX_ISDOOR)) {
                    send_to_char("That's impossible, I'm afraid.\n\r", ch);
                    return ERROR_FAILED;
                }
                if (IS_SET(exit->exit_info, EX_CLOSED)) {
                    send_to_char("It's already closed!\n\r", ch);
                    return ERROR_FAILED;
                }
    
                SET_BIT(exit->exit_info, EX_CLOSED);
                if(obj->name)
                    act("$n closes the $F.",FALSE,ch,0,obj->name,TO_ROOM);
                else
                    act("$n closes the door.", FALSE, ch, 0, 0, TO_ROOM);
    
                /* now for closing the OTHER side of the door! */
                if ((other_room = exit->to_room) != NOWHERE) {
                	if((back = match_door(exit,ch->in_room,other_room))) {
                            back_e = (struct obj_info_exit *)get_obj_info(back,ITEM_EXIT);
        	            if (back_e->to_room == ch->in_room) {
                            SET_BIT(back_e->exit_info, EX_CLOSED);
                            if (back->name) {
	        	            sprintf(buf,"The %s is closed from the other side.\n\r", fname(back->name));
	        		    send_to_room(buf, exit->to_room);
                            } else {
        			send_to_room("The door is closed from the other side.\n\r",
        			exit->to_room);
    		        }
                        }
                    }
	        }
                return OKAY;
                break;
            case OPENING_LOCK:
                if (!IS_SET(exit->exit_info, EX_ISDOOR)) {
                    send_to_char("That's absurd.\n\r", ch);
                    return ERROR_FAILED;
                }
                if (!IS_SET(exit->exit_info, EX_CLOSED)) {
                    send_to_char("You have to close it first, I'm afraid.\n\r", ch);
                    return ERROR_FAILED;
                }
                if (exit->key < 0) {
                    send_to_char("There does not seem to be any keyholes.\n\r", ch);
                    return ERROR_FAILED;
                }
                if (!has_key(ch, exit->key)) {
                    send_to_char("You don't have the proper key.\n\r", ch);
                    return ERROR_FAILED;
                }
                if (IS_SET(exit->exit_info, EX_LOCKED)) {
                    send_to_char("It's already locked!\n\r", ch);
                    return ERROR_FAILED;
                }
                SET_BIT(exit->exit_info, EX_LOCKED);

                if (obj->name)
                    act("$n locks the $F.",0,ch,0,obj->name, TO_ROOM);
                else
                    act("$n locks the door.", FALSE, ch, 0, 0, TO_ROOM);

                send_to_char("*Click*\n\r", ch);
                /* now for locking the other side, too */
                if ((other_room = exit->to_room) != NOWHERE)
                	if ((back = match_door(exit,ch->in_room,other_room))){
                            back_e = (struct obj_info_exit *)get_obj_info(back,ITEM_EXIT);
                            if (back_e->to_room == ch->in_room)
                                SET_BIT(back_e->exit_info, EX_LOCKED);
                        }
    
                return OKAY;
                break;
            case OPENING_UNLOCK:
                if (!IS_SET(exit->exit_info, EX_ISDOOR)) {
                    send_to_char("That's absurd.\n\r", ch);
                    return ERROR_FAILED;
                }
                if (!IS_SET(exit->exit_info, EX_CLOSED)) {
                    send_to_char("You have to close it first, I'm afraid.\n\r", ch);
                    return ERROR_FAILED;
                }
                if (exit->key < 0) {
                    send_to_char("There does not seem to be any keyholes.\n\r", ch);
                    return ERROR_FAILED;
                }
                if (!has_key(ch, exit->key)) {
                    send_to_char("You don't have the proper key.\n\r", ch);
                    return ERROR_FAILED;
                }
                if (IS_SET(exit->exit_info, EX_LOCKED)) {
                    send_to_char("It's already locked!\n\r", ch);
                    return ERROR_FAILED;
                }
                SET_BIT(exit->exit_info, EX_LOCKED);
    
                if (obj->name)
                    act("$n locks the $F.",0,ch,0,obj->name, TO_ROOM);
                else
                    act("$n locks the door.", FALSE, ch, 0, 0, TO_ROOM);

                send_to_char("*Click*\n\r", ch);
                /* now for locking the other side, too */
                if ((other_room = exit->to_room) != NOWHERE)
                    if((back = match_door(exit,ch->in_room,other_room))) {
                        back_e = (struct obj_info_exit *)get_obj_info(back,ITEM_EXIT);
                        if (back_e->to_room == ch->in_room)
                            SET_BIT(back_e->exit_info, EX_LOCKED);
                    }
    
                return OKAY;
                break;
            case OPENING_PICK:
        }
    }
    /*** huh? ***/
    send_to_char("That's not possible.\n\r",ch);
    return ERROR_FAILED;
}

int has_key(struct char_data *ch, int key)
{
    return 0;
}
#if 0

int do_lock(struct char_data *ch, char *argument, int cmd)
{
    int other_room;
    char type[MAX_INPUT_LENGTH], dir[MAX_INPUT_LENGTH];
    struct room_direction_data *door,*back;
    struct obj_data *obj;


    }
    return ERROR_SYNTAX;
}

int do_unlock(struct char_data *ch, char *argument, int cmd)
{
    int other_room;
    char type[MAX_INPUT_LENGTH], dir[MAX_INPUT_LENGTH];
    struct room_direction_data *door,*back;
    struct obj_data *obj;



int do_pick(struct char_data *ch, char *argument, int cmd)
{
    byte percent;
    int other_room;
    char type[MAX_INPUT_LENGTH], dir[MAX_INPUT_LENGTH];
    struct room_direction_data *door, *back;
    struct obj_data *obj;
    struct obj_info_container *cont;
    void *target;

    if(1 /*GET_CLASS(ch)!=CLASS_THIEF &&*/) {
	send_to_char("You're no thief!\n\r", ch);
	return ERROR_NO_KNOWLEDGE;
    }
    argument_interpreter(argument, type, dir);

    percent=number(1,101); /* 101% is a complete failure */

/*
    if (percent > (ch->skills[SKILL_PICK_LOCK].learned)) {
        send_to_char("You failed to pick the lock.\n\r", ch);
        return ERROR_FAILED;
    }*/

    if (!*type) {
	send_to_char("Pick what?\n\r", ch);
        return ERROR_SYNTAX;
    }
    switch (generic_find(argument, FIND_OBJ_INV | FIND_OBJ_ROOM,
	ch, &target)) {

        case FIND_OBJ_INV:
        case FIND_OBJ_ROOM:

            /* this is an object */
            obj = (struct obj_data *)target;

            if (!(cont=(struct obj_info_container *)
                        get_obj_info(obj,ITEM_CONTAINER))) {
                send_to_char("That's not a container.\n\r", ch);
                return ERROR_FAILED;
            }
            if (!IS_SET(container->lock_state, CONT_CLOSED)) {
                send_to_char("Silly - it ain't even closed!\n\r", ch);
                return ERROR_FAILED;
            }
            if (container->lock_number < 0) {
                send_to_char("Odd - you can't seem to find a keyhole.\n\r", ch);
                return ERROR_FAILED;
            }
            if (!IS_SET(container->lock_state, CONT_LOCKED)) {
                send_to_char("Oho! This thing is NOT locked!\n\r", ch);
                return ERROR_FAILED;
            }
            if (IS_SET(container->lock_state, CONT_PICKPROOF)) {
                send_to_char("It resists your attempts at picking it.\n\r", ch);
                return ERROR_FAILED;
            }
            REMOVE_BIT(container->lock_state, CONT_LOCKED);
            send_to_char("*Click*\n\r", ch);
            act("$n fiddles with $p.", FALSE, ch, obj, 0, TO_ROOM);
            break;

        case FIND_DOOR: /* NOTE WARNING DANGER YIP DOODLE! not valid pbbbt */

            door = (struct room_direction_data *)target;

            if (!IS_SET(door->exit_info, EX_ISDOOR)) {
                send_to_char("That's absurd.\n\r", ch);
                return ERROR_FAILED;
            }
            if (!IS_SET(door->exit_info, EX_CLOSED)) {
                send_to_char("You realize that the door is already open.\n\r", ch);
                return ERROR_FAILED;
            }
            if (door->key < 0) {
                send_to_char("You can't seem to spot any lock to pick.\n\r", ch);
                return ERROR_FAILED;
            }
            if (!IS_SET(door->exit_info, EX_LOCKED)) {
                send_to_char("Oh.. it wasn't locked at all.\n\r", ch);
                return ERROR_FAILED;
            }
            if (IS_SET(door->exit_info, EX_PICKPROOF)) {
                send_to_char("You seem to be unable to pick this lock.\n\r", ch);
                return ERROR_FAILED;
            }
            REMOVE_BIT(door->exit_info, EX_LOCKED);
            if (obj->name)
                act("$n skillfully picks the lock of the $F.", 0, ch, 0,
            	    obj->name, TO_ROOM);
            else
                act("$n picks the lock.", TRUE, ch, 0, 0, TO_ROOM);
            send_to_char("The lock quickly yields to your skills.\n\r", ch);
            /* now for unlocking the other side, too */
            if ((other_room = door->to_room) != NOWHERE)
            	if (back = match_door(door,other_room))
	            if (back->to_room == ch->in_room)
	                REMOVE_BIT(back->exit_info, EX_LOCKED);
            return OKAY;
            break;
    }
    return ERROR_FAILED;
}

#endif


int do_chgpos(struct char_data *ch, char *argument, int cmd)
{
    /* Sleeping people can't change position (outside of "wake") */
    if(GET_POS(ch)==POS_SLEEP) {
        send_to_char("You'll need to wake up first.\n",ch);
        return ERROR_POSITION;
    }

    /* Let the idiots know who they are */
    if(cmd==GET_POS(ch)) {
        send_to_char("But you're already there.\n",ch);
        return ERROR_ALREADY_DONE;
    }

    /* determine if the character can change to the new position */
    if(!can_have_pos(ch,cmd)) {
        switch(cmd) {
            case POS_REST:
                send_to_char("This body wasn't built for resting.\n",ch);
                break;
            case POS_SIT:
                send_to_char("This body wasn't built for sitting.\n",ch);
                break;
            case POS_STAND:
                send_to_char("This body wasn't built for standing.\n",ch);
                break;
            case POS_LEVITATE:
            case POS_FLY:
            default:
                send_to_char("That would not seem possible.\n",ch);
                break;
        }
        return ERROR_NO_SENSE;
    }

    /* fetch message from... somewhere */
    switch(cmd) {
        case POS_REST:
            send_to_char("You achieve a position of relaxation.\n",ch);
            act("$n sits down and rests.",TRUE,ch,0,0,TO_ROOM);
            break;
        case POS_SIT:
            if(GET_POS(ch)<POS_SIT) {
                send_to_char("You rise to a sitting position.\n",ch);
                act("$n rises to a sitting position.",TRUE,ch,0,0,TO_ROOM);
            } else {
                send_to_char("You sit down.\n",ch);
                act("$n sits down.",TRUE,ch,0,0,TO_ROOM);
            }
            break;
        case POS_STAND:
            send_to_char("You assume your feet.\n",ch);
            act("$n assumes $s feet.",TRUE,ch,0,0,TO_ROOM);
            break;
        case POS_LEVITATE:
            send_to_char("You float motionless above the ground.\n",ch);
            act("$n floats above the ground, motionless.",TRUE,ch,0,0,TO_ROOM);
            break;
        case POS_FLY:
            send_to_char("You lift yourself to the sky.\n",ch);
            act("$n begins to fly around.",TRUE,ch,0,0,TO_ROOM);
            break;
        default:
            break;
    }

    /* now, we attain the position */
    GET_POS(ch)=cmd;

    return OKAY;
}


int do_sleep(struct char_data *ch, char *argument, int cmd)
{
    if(ch->physical->exhaustion>4) {
        send_to_char("You aren't tired enough - the best you can do is just rest.\n",ch);
        return ERROR_PHYS_PREVENTS;
    }

    switch(GET_POS(ch)) {
	case POS_STAND : 
	case POS_SIT  :
	case POS_REST  : {
	    send_to_char("You go to sleep.\n\r", ch);
	    act("$n lies down and falls asleep.", TRUE, ch, 0, 0, TO_ROOM);
	    GET_POS(ch) = POS_SLEEP;
            return OKAY;
	} break;
	case POS_SLEEP : {
	    send_to_char("You are already sound asleep.\n\r", ch);
	} break;
	default : {
	    if(world[ch->in_room].sector_type >=SECT_WATER_SWIM) 
		act("Here? That's not too wise.",FALSE,ch,0,0,TO_CHAR);
	    else {
		act("You stop floating around, and lie down to sleep.",
	    	 FALSE, ch, 0, 0, TO_CHAR);
		act("$n stops floating around, and lie down to sleep.",
		 TRUE, ch, 0, 0, TO_ROOM);
		GET_POS(ch) = POS_SLEEP;
                return OKAY;
	    }
	} break;
    }
    return ERROR_FAILED;
}


int do_wake(struct char_data *ch, char *argument, int cmd)
{
    struct char_data *tmp_char;
    char arg[MAX_STRING_LENGTH];


    one_argument(argument,arg);
    if (*arg) {
	if (GET_POS(ch) == POS_SLEEP) {
	    act("You can't wake people up if you are asleep yourself!",
		FALSE, ch,0,0,TO_CHAR);
            return ERROR_POSITION;
	}
	tmp_char = get_char_room_vis(ch, arg);
	if (tmp_char) {
	    if (tmp_char == ch) {
	        act("If you want to wake yourself up, just type 'wake'",
			FALSE, ch,0,0,TO_CHAR);
                return ERROR_SYNTAX;
	    }
	    if (GET_POS(tmp_char) == POS_SLEEP) {
		act("You wake $M up.", FALSE, ch, 0, tmp_char, TO_CHAR);
/*		if(IS_AFFECTED(tmp_char, AFF_SLEEP)){
		    affect_from_char(tmp_char, SKILL_SLEEP);
		}*/
		GET_POS(tmp_char) = POS_SIT;
		act("You are awakened by $n.", FALSE, ch, 0, tmp_char, TO_VICT);
                return OKAY;
	    } else {
		act("$N is already awake.",FALSE,ch,0,tmp_char, TO_CHAR);
                return ERROR_ALREADY_DONE;
	    }
	} else {
	    send_to_char("You do not see that person here.\n\r", ch);
            return ERROR_MISSING_TARGET;
	}
    }
    if (/*IS_AFFECTED(ch,AFF_SLEEP) ||*/ ch->physical->exhaustion==0) {
        send_to_char("You're too tired!\n\r", ch);
        return ERROR_FAILED;
    }
    if (GET_POS(ch) > POS_SLEEP) {
	send_to_char("You are already awake...\n\r", ch);
        return ERROR_ALREADY_DONE;
    }
    send_to_char("You wake, and sit up.\n\r", ch);
    act("$n awakens.", TRUE, ch, 0, 0, TO_ROOM);
    GET_POS(ch) = POS_SIT;
    return OKAY;
}


int do_follow(struct char_data *ch, char *argument, int cmd)
{
    char name[160];
    struct char_data *leader;

    void stop_follower(struct char_data *ch);
    void add_follower(struct char_data *ch, struct char_data *leader);


    one_argument(argument, name);

    if (*name) {
	if (!(leader = get_char_room_vis(ch, name))) {
	    send_to_char("I see no person by that name here!\n\r", ch);
	    return ERROR_MISSING_TARGET;
	}
    } else {
	send_to_char("Who do you wish to follow?\n\r", ch);
	return ERROR_SYNTAX;
    }
/*
    if (IS_AFFECTED(ch, AFF_CHARM) && (ch->master)) {

	act("But you only feel like following $N!",
	 FALSE, ch, 0, ch->master, TO_CHAR);
        return ERROR_MAGIC_PREVENTS;

    }*/
    /* Not Charmed follow person */

    if (leader == ch) {
        if (!ch->master) {
            send_to_char("You are already following yourself.\n\r", ch);
	    return ERROR_FAILED;
	}
	stop_follower(ch);
        return OKAY;
    }
    if (circle_follow(ch, leader)) {
	act("Sorry, but following in 'loops' is not allowed", FALSE, ch, 0, 0, TO_CHAR);
	return ERROR_FAILED;
    }
    if (ch->master)
	stop_follower(ch);

    add_follower(ch, leader);
    return OKAY;
}