/*
 * $Id: act_move.c,v 1.165 1999/04/21 14:44:27 kostik Exp $
 */

/***************************************************************************
 *     ANATOLIA 2.1 is copyright 1996-1997 Serdar BULUT, Ibrahim CANPUNAR  *    
 *     ANATOLIA has been brought to you by ANATOLIA consortium         *
 *   Serdar BULUT {Chronos}     bulut@rorqual.cc.metu.edu.tr       *    
 *   Ibrahim Canpunar  {Asena}  canpunar@rorqual.cc.metu.edu.tr    *    
 *   Murat BICER  {KIO}     mbicer@rorqual.cc.metu.edu.tr      *
 *   D.Baris ACAR {Powerman}    dbacar@rorqual.cc.metu.edu.tr      *    
 *     By using this code, you have agreed to follow the terms of the      *
 *     ANATOLIA license, in the file Anatolia/anatolia.licence             *    
 ***************************************************************************/

/***************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
 *                                                                         *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
 *  Chastain, Michael Quan, and Mitchell Tse.                              *
 *                                                                         *
 *  In order to use any part of this Merc Diku Mud, you must comply with   *
 *  both the original Diku license in 'license.doc' as well the Merc       *
 *  license in 'license.txt'.  In particular, you may not remove either of *
 *  these copyright notices.                                               *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 ***************************************************************************/

/***************************************************************************
*   ROM 2.4 is copyright 1993-1995 Russ Taylor             *
*   ROM has been brought to you by the ROM consortium          *
*       Russ Taylor (rtaylor@pacinfo.com)                  *
*       Gabrielle Taylor (gtaylor@pacinfo.com)             *
*       Brian Moore (rom@rom.efn.org)                  *
*   By using this code, you have agreed to follow the terms of the     *
*   ROM license, in the file Rom24/doc/rom.license             *
***************************************************************************/

#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#include <string.h>
#include "merc.h"
#include "update.h"
#include "mob_prog.h"
#include "obj_prog.h"
#include "fight.h"

char *  const   dir_name    []      =
{
    "north", "east", "south", "west", "up", "down"
};

const   int rev_dir     []      =
{
    2, 3, 0, 1, 5, 4
};

const   int movement_loss   [SECT_MAX]  =
{
    1, 2, 2, 3, 4, 6, 4, 1, 6, 10, 6
};

DECLARE_DO_FUN(do_look      );
DECLARE_DO_FUN(do_yell      );

/*
 * Local functions.
 */
int find_door   (CHAR_DATA *ch, char *arg);
int     find_exit   (CHAR_DATA *ch, char *arg);
bool    has_key     (CHAR_DATA *ch, int key);
bool    move_char_org   (CHAR_DATA *ch, int door, bool follow, bool is_charge);
int mount_success   (CHAR_DATA *ch, CHAR_DATA *mount, int canattack);

void move_char(CHAR_DATA *ch, int door, bool follow)
{
    move_char_org(ch, door, follow, FALSE);
}

bool move_char_org(CHAR_DATA *ch, int door, bool follow, bool is_charge)
{
    CHAR_DATA *fch;
    CHAR_DATA *fch_next;
    CHAR_DATA *mount;
    ROOM_INDEX_DATA *in_room;
    ROOM_INDEX_DATA *to_room;
    EXIT_DATA *pexit;
    bool room_has_pc;
    OBJ_DATA *obj;
    OBJ_DATA *obj_next;
    int act_flags;

    if (RIDDEN(ch) && !IS_NPC(ch->mount)) 
        return move_char_org(ch->mount,door,follow,is_charge);

    if (IS_AFFECTED(ch, AFF_DETECT_WEB) 
    || (MOUNTED(ch) && IS_AFFECTED(ch->mount, AFF_DETECT_WEB))) {
        WAIT_STATE(ch, PULSE_VIOLENCE);
        if (number_percent() < str_app[IS_NPC(ch) ?
            20 : get_curr_stat(ch,STAT_STR)].tohit * 5) {
            affect_strip(ch, gsn_web);
            REMOVE_BIT(ch->affected_by, AFF_DETECT_WEB);
            act_puts("When you attempt to leave the room, you "
                 "break the webs holding you tight.",
                 ch, NULL, NULL, TO_CHAR, POS_DEAD);
            act_puts("$n struggles against the webs which hold $m "
                 "in place, and break it.",
                 ch, NULL, NULL, TO_ROOM, POS_RESTING);
        }
        else {
            act_puts("You attempt to leave the room, but the webs "
                 "hold you tight.",
                 ch, NULL, NULL, TO_ROOM, POS_DEAD);
            act("$n struggles vainly against the webs which "
                "hold $m in place.",
                ch, NULL, NULL, TO_ROOM);
            return FALSE; 
        }
    }

    for (fch = ch->in_room->people; fch; fch = fch->next_in_room) {
        if (fch->target == ch
        &&  IS_NPC(fch)
        &&  fch->pIndexData->vnum == MOB_VNUM_SHADOW) {
            char_puts("You attempt to leave your shadow alone,"
                " but fail.\n", ch);
            return FALSE;
        }
    }

    if (door < 0 || door >= MAX_DIR) {
        bug("move_char_org: bad door %d.", door);
        return FALSE;
    }

    if (IS_AFFECTED(ch, AFF_HIDE | AFF_FADE)
    &&  !IS_AFFECTED(ch, AFF_SNEAK)) {
        REMOVE_BIT(ch->affected_by, AFF_HIDE);
        act_puts("You step out of shadows.",
             ch, NULL, NULL, TO_CHAR, POS_DEAD);
        act_puts("$n steps out of shadows.",
             ch, NULL, NULL, TO_ROOM, POS_RESTING);
    }

    if (IS_AFFECTED(ch, AFF_CAMOUFLAGE))  {
        int chance;

        if ((chance = get_skill(ch, gsn_camouflage_move)) == 0) {
            REMOVE_BIT(ch->affected_by, AFF_CAMOUFLAGE);
            act_puts("You step out from your cover.",
                 ch, NULL, NULL, TO_CHAR, POS_DEAD);
            act("$n steps out from $m's cover.",
                ch, NULL, NULL, TO_ROOM);
        }       
        else if (number_percent() < chance)
            check_improve(ch, gsn_camouflage_move, TRUE, 5);
        else {
            REMOVE_BIT(ch->affected_by, AFF_CAMOUFLAGE);
            act_puts("You step out from your cover.",
                 ch, NULL, NULL, TO_CHAR, POS_DEAD);
            act("$n steps out from $m's cover.",
                ch, NULL, NULL, TO_ROOM);
            check_improve(ch, gsn_camouflage_move, FALSE, 5);
        }       
    }

    /*
     * Exit trigger, if activated, bail out. Only PCs are triggered.
     */
    if (!IS_NPC(ch) && mp_exit_trigger(ch, door))
        return FALSE;

    in_room = ch->in_room;
    if ((pexit = in_room->exit[door]) == NULL
    ||  (to_room = pexit->to_room.r) == NULL 
    ||  !can_see_room(ch, pexit->to_room.r)) {
        char_puts("Alas, you cannot go that way.\n", ch);
        return FALSE;
    }

    if (IS_ROOM_AFFECTED(in_room, RAFF_RANDOMIZER) && !is_charge) {
        int d0;
        while (1) {
            d0 = number_range(0, MAX_DIR-1);
            if ((pexit = in_room->exit[d0]) == NULL
            ||  (to_room = pexit->to_room.r) == NULL 
            ||  !can_see_room(ch, pexit->to_room.r))
                continue;     
            door = d0;
            break;
        }
    }

    if (IS_SET(pexit->exit_info, EX_CLOSED) 
    &&  (!IS_AFFECTED(ch, AFF_PASS_DOOR) ||
         IS_SET(pexit->exit_info, EX_NOPASS))
    &&  !IS_TRUSTED(ch, ANGEL)) {
        if (IS_AFFECTED(ch, AFF_PASS_DOOR)
        &&  IS_SET(pexit->exit_info, EX_NOPASS)) {
            act_puts("You failed to pass through the $d.",
                 ch, NULL, pexit->keyword, TO_CHAR, POS_DEAD);
            act("$n tries to pass through the $d, but $e fails.",
                ch, NULL, pexit->keyword, TO_ROOM);
        }
        else {
            act_puts("The $d is closed.",
                 ch, NULL, pexit->keyword, TO_CHAR, POS_DEAD);
        }
        return FALSE;
    }

    if (IS_AFFECTED(ch, AFF_CHARM)
    &&  ch->master != NULL
    &&  in_room == ch->master->in_room) {
        char_puts("What? And leave your beloved master?\n", ch);
        return FALSE;
    }

/*    if (!is_room_owner(ch,to_room) && room_is_private(to_room))   */
    if (room_is_private(to_room)) {
        char_puts("That room is private right now.\n", ch);
        return FALSE;
    }

    if (MOUNTED(ch)) {
        if (MOUNTED(ch)->position < POS_FIGHTING) {
            char_puts("Your mount must be standing.\n", ch);
            return FALSE; 
        }
        if (!mount_success(ch, MOUNTED(ch), FALSE)) {
            char_puts("Your mount subbornly refuses to go that way.\n", ch);
            return FALSE;
        }
    }

    if (!IS_NPC(ch)) {
        int move;

        if (!IS_IMMORTAL(ch)) {
            if (IS_SET(to_room->room_flags, ROOM_GUILD)
            &&  !guild_ok(ch, to_room)) {
                char_puts("You aren't allowed there.\n", ch);
                return FALSE;
            }

            if (IS_PUMPED(ch)
            &&  IS_SET(to_room->room_flags, ROOM_PEACE | ROOM_GUILD)) {
                act_puts("You feel too bloody to go in there now.",
                     ch, NULL, NULL, TO_CHAR, POS_DEAD);
                return FALSE;
            }
        }

        if (in_room->sector_type == SECT_AIR
        ||  to_room->sector_type == SECT_AIR) {
            if (MOUNTED(ch)) {
                    if(!IS_AFFECTED(MOUNTED(ch), AFF_FLYING)) {
                        char_puts("You mount can't fly.\n", ch);
                           return FALSE;
                }
            } 
            else if (!IS_AFFECTED(ch, AFF_FLYING)
            && !IS_IMMORTAL(ch)) {
                act_puts("You can't fly.",
                     ch, NULL, NULL, TO_CHAR, POS_DEAD);
                return FALSE;
            } 
        }

        if ((in_room->sector_type == SECT_WATER_NOSWIM ||
             to_room->sector_type == SECT_WATER_NOSWIM)
        &&  (MOUNTED(ch) && !IS_AFFECTED(MOUNTED(ch),AFF_FLYING))) {
            act_puts("You can't take your mount there.\n",
                 ch, NULL, NULL, TO_CHAR, POS_DEAD);
            return FALSE;
        }  

        if ((in_room->sector_type == SECT_WATER_NOSWIM ||
             to_room->sector_type == SECT_WATER_NOSWIM)
        &&  (!MOUNTED(ch) && !IS_AFFECTED(ch, AFF_FLYING))) {
            OBJ_DATA *obj;
            bool found;

            /*
             * Look for a boat.
             */
            found = FALSE;

            if (IS_IMMORTAL(ch))
            found = TRUE;

            for (obj = ch->carrying; obj != NULL; obj = obj->next_content)
            {
            if (obj->pIndexData->item_type == ITEM_BOAT)
            {
                found = TRUE;
                break;
            }
            }
            if (!found)
            {
            char_puts("You need a boat to go there.\n", ch);
            return FALSE;
            }
        }

        move = (movement_loss[UMIN(SECT_MAX-1, in_room->sector_type)]
             + movement_loss[UMIN(SECT_MAX-1, to_room->sector_type)])/2;

        if (is_affected(ch, gsn_thumbling))
            move *= 2;
        else {
            if (IS_AFFECTED(ch,AFF_FLYING)
            || IS_AFFECTED(ch,AFF_HASTE))
                move /= 2;

            if (IS_AFFECTED(ch,AFF_SLOW))
                move *= 2;
        }

        if (!MOUNTED(ch)) {
            if (ch->move < move) {
                act_puts("You are too exhausted.",
                     ch, NULL, NULL, TO_CHAR, POS_DEAD);
                return FALSE;
            }

            ch->move -= move;

            if (ch->in_room->sector_type == SECT_DESERT
            ||  IS_WATER(ch->in_room))
                WAIT_STATE(ch, 2);
            else
                WAIT_STATE(ch, 1);
        }
    }

    if (!IS_AFFECTED(ch, AFF_SNEAK)
    &&  !IS_AFFECTED(ch, AFF_CAMOUFLAGE)
    &&  ch->invis_level < LEVEL_HERO) 
        act_flags = TO_ROOM;
    else
        act_flags = TO_ROOM | ACT_NOMORTAL;

    if (!IS_NPC(ch)
    &&  ch->in_room->sector_type != SECT_INSIDE
    &&  ch->in_room->sector_type != SECT_CITY
    &&  number_percent() < get_skill(ch, gsn_quiet_movement) 
    &&  !is_charge) {
        act(MOUNTED(ch) ? "$n leaves, riding on $N." : "$n leaves.",
            ch, NULL, MOUNTED(ch), act_flags);
        check_improve(ch,gsn_quiet_movement,TRUE,1);
    }
    else if (is_charge) {
        act("$n spurs $s $N, leaving $t.",
            ch, dir_name[door], ch->mount,  TO_ROOM);
    }
    else {
        act(MOUNTED(ch) ? "$n leaves $t, riding on $N." :
                  "$n leaves $t.",
            ch, dir_name[door], MOUNTED(ch), act_flags | ACT_TRANS);
    }

    if (IS_AFFECTED(ch, AFF_CAMOUFLAGE)
    &&  to_room->sector_type != SECT_FOREST
    &&  to_room->sector_type != SECT_MOUNTAIN
    &&  to_room->sector_type != SECT_HILLS) {
        REMOVE_BIT(ch->affected_by, AFF_CAMOUFLAGE);
        act_puts("You step out from your cover.",
             ch, NULL, NULL, TO_CHAR, POS_DEAD);
        act("$n steps out from $m's cover.",
            ch, NULL, NULL, TO_ROOM);
    }

    /* room record for tracking */
    if (!IS_NPC(ch))
        room_record(ch->name, in_room, door);

    /*
     * now, after all the checks are done we should
     * - take the char from the room
     * - print the message to chars in to_room about ch arrival
     * - put the char to to_room
     * - CHECK THAT CHAR IS NOT DEAD after char_to_room
     * - move all the followers and pull all the triggers
     */
    mount = MOUNTED(ch);
    char_from_room(ch);

    if (!IS_AFFECTED(ch, AFF_SNEAK) && ch->invis_level < LEVEL_HERO) 
        act_flags = TO_ALL;
    else
        act_flags = TO_ALL | ACT_NOMORTAL;

    if (!is_charge) 
        act(mount ? "$i has arrived, riding $N." : "$i has arrived.",
                to_room->people, ch, mount, act_flags);

    char_to_room(ch, to_room);

    if (mount) {
        char_from_room(mount);
        char_to_room(mount, to_room);
        ch->riding = TRUE;
        mount->riding = TRUE;
    }

    if (!JUST_KILLED(ch))
        do_look(ch, "auto");

    if (in_room == to_room) /* no circular follows */
        return TRUE;

    /*
     * move all the followers
     */
    for (fch = in_room->people; fch; fch = fch_next) {
        fch_next = fch->next_in_room;

        if (fch->master != ch || fch->position != POS_STANDING
        ||  !can_see_room(fch, to_room))
            continue;

        if (IS_SET(to_room->room_flags, ROOM_LAW)
        &&  IS_NPC(fch)
        &&  IS_SET(fch->pIndexData->act, ACT_AGGRESSIVE)) {
            act_puts("You can't bring $N into the city.",
                 ch, NULL, fch, TO_CHAR, POS_DEAD);
            act("You aren't allowed in the city.",
                fch, NULL, NULL, TO_CHAR);
            continue;
        }

        act_puts("You follow $N.", fch, NULL, ch, TO_CHAR, POS_DEAD);
        move_char(fch, door, TRUE);
    }

    if (JUST_KILLED(ch))
        return TRUE;

    room_has_pc = FALSE;
    for (fch = to_room->people; fch != NULL; fch = fch_next) {
        fch_next = fch->next_in_room;
        if (!IS_NPC(fch)) {
            room_has_pc = TRUE;
            break;
        }
    }

    if (!room_has_pc)
        return TRUE;

    /*
     * pull GREET and ENTRY triggers
     *
     * if someone is following the char, these triggers get activated
     * for the followers before the char, but it's safer this way...
     */
    for (fch = to_room->people; fch; fch = fch_next) {
        fch_next = fch->next_in_room;

        /* greet progs for items carried by people in room */
        for (obj = fch->carrying; obj; obj = obj_next) {
            obj_next = obj->next_content;
            oprog_call(OPROG_GREET, obj, ch, NULL);
        }
    }

    for (obj = ch->in_room->contents; obj != NULL; obj = obj_next) {
        obj_next = obj->next_content;
        oprog_call(OPROG_GREET, obj, ch, NULL);
    }

    if (!IS_NPC(ch))
            mp_greet_trigger(ch);

    for (obj = ch->carrying; obj; obj = obj_next) {
        obj_next = obj->next_content;
        oprog_call(OPROG_ENTRY, obj, NULL, NULL);
    }

    if (IS_NPC(ch) && HAS_TRIGGER(ch, TRIG_ENTRY))
        mp_percent_trigger(ch, NULL, NULL, NULL, TRIG_ENTRY);

    return TRUE;
}

void do_north(CHAR_DATA *ch, const char *argument)
{
    move_char(ch, DIR_NORTH, FALSE);
}

void do_east(CHAR_DATA *ch, const char *argument)
{
    move_char(ch, DIR_EAST, FALSE);
}

void do_south(CHAR_DATA *ch, const char *argument)
{
    move_char(ch, DIR_SOUTH, FALSE);
}

void do_west(CHAR_DATA *ch, const char *argument)
{
    move_char(ch, DIR_WEST, FALSE);
}

void do_up(CHAR_DATA *ch, const char *argument)
{
    move_char(ch, DIR_UP, FALSE);
}

void do_down(CHAR_DATA *ch, const char *argument)
{
    move_char(ch, DIR_DOWN, FALSE);
}

int find_exit(CHAR_DATA *ch, char *arg)
{
    int door;

         if (!str_cmp(arg, "n") || !str_cmp(arg, "north")) door = 0;
    else if (!str_cmp(arg, "e") || !str_cmp(arg, "east" )) door = 1;
    else if (!str_cmp(arg, "s") || !str_cmp(arg, "south")) door = 2;
    else if (!str_cmp(arg, "w") || !str_cmp(arg, "west" )) door = 3;
    else if (!str_cmp(arg, "u") || !str_cmp(arg, "up"   )) door = 4;
    else if (!str_cmp(arg, "d") || !str_cmp(arg, "down" )) door = 5;
    else {
        act_puts("I see no exit $T here.",
             ch, NULL, arg, TO_CHAR, POS_DEAD);
        return -1;
    }

    return door;
}

int find_door(CHAR_DATA *ch, char *arg)
{
    EXIT_DATA *pexit;
    int door;

         if (!str_cmp(arg, "n") || !str_cmp(arg, "north")) door = 0;
    else if (!str_cmp(arg, "e") || !str_cmp(arg, "east" )) door = 1;
    else if (!str_cmp(arg, "s") || !str_cmp(arg, "south")) door = 2;
    else if (!str_cmp(arg, "w") || !str_cmp(arg, "west" )) door = 3;
    else if (!str_cmp(arg, "u") || !str_cmp(arg, "up"   )) door = 4;
    else if (!str_cmp(arg, "d") || !str_cmp(arg, "down" )) door = 5;
    else {
        for (door = 0; door <= 5; door++) {
            if ((pexit = ch->in_room->exit[door]) != NULL
            &&   IS_SET(pexit->exit_info, EX_ISDOOR)
            &&   pexit->keyword != NULL
            &&   is_name(arg, pexit->keyword))
            return door;
        }
        act_puts("I see no $T here.", ch, NULL, arg, TO_CHAR, POS_DEAD);
        return -1;
    }

    if ((pexit = ch->in_room->exit[door]) == NULL) {
        act_puts("I see no door $T here.",
             ch, NULL, arg, TO_CHAR, POS_DEAD);
        return -1;
    }

    if (!IS_SET(pexit->exit_info, EX_ISDOOR)) {
        char_puts("You can't do that.\n", ch);
        return -1;
    }

    return door;
}

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

    one_argument(argument, arg, sizeof(arg));

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

    if ((obj = get_obj_here(ch, arg)) != NULL) {
    /* open portal */
        if (obj->pIndexData->item_type == ITEM_PORTAL)
        {
            if (!IS_SET(obj->value[1], EX_ISDOOR))
            {
            char_puts("You can't do that.\n", ch);
            return;
            }

            if (!IS_SET(obj->value[1], EX_CLOSED)) {
            char_puts("It's already open.\n", ch);
            return;
            }

            if (IS_SET(obj->value[1], EX_LOCKED)) {
            char_puts("It's locked.\n", ch);
            return;
            }

            REMOVE_BIT(obj->value[1], EX_CLOSED);
            act_puts("You open $p.", ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n opens $p.", ch, obj, NULL, TO_ROOM);
            return;
    }

        /* 'open object' */
        if (obj->pIndexData->item_type != ITEM_CONTAINER)
            { char_puts("That's not a container.\n", ch); return; }
        if (!IS_SET(obj->value[1], CONT_CLOSED))
            { char_puts("It's already open.\n", ch); return; }
        if (!IS_SET(obj->value[1], CONT_CLOSEABLE))
            { char_puts("You can't do that.\n", ch); return; }
        if (IS_SET(obj->value[1], CONT_LOCKED))
            { char_puts("It's locked.\n", ch); return; }

        REMOVE_BIT(obj->value[1], CONT_CLOSED);
        act_puts("You open $p.", ch, obj, NULL, TO_CHAR, POS_DEAD);
        act("$n opens $p.", ch, obj, NULL, TO_ROOM);
        return;
    }

    if ((door = find_door(ch, arg)) >= 0) {
        /* 'open door' */
        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))
            { char_puts("It's already open.\n", ch); return; }
        if ( IS_SET(pexit->exit_info, EX_LOCKED))
            { char_puts("It's locked.\n", ch); return; }

        REMOVE_BIT(pexit->exit_info, EX_CLOSED);
        act("$n opens the $d.", ch, NULL, pexit->keyword, TO_ROOM);
        char_puts("Ok.\n", ch);

        /* open the other side */
        if ((to_room   = pexit->to_room.r           ) != NULL
        &&   (pexit_rev = to_room->exit[rev_dir[door]]) != NULL
        &&   pexit_rev->to_room.r == ch->in_room) {
            ROOM_INDEX_DATA *in_room;

            REMOVE_BIT(pexit_rev->exit_info, EX_CLOSED);

            in_room = ch->in_room;
            ch->in_room = to_room;
            act("The $d opens.", ch, NULL, pexit_rev->keyword,
                TO_ROOM);
            ch->in_room = in_room;
        }
        return;
    }
}

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

    one_argument(argument, arg, sizeof(arg));

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

    if ((obj = get_obj_here(ch, arg)) != NULL) {
        /* portal stuff */
        if (obj->pIndexData->item_type == ITEM_PORTAL)
        {

            if (!IS_SET(obj->value[1],EX_ISDOOR)
            ||   IS_SET(obj->value[1],EX_NOCLOSE))
            {
            char_puts("You can't do that.\n", ch);
            return;
            }

            if (IS_SET(obj->value[1],EX_CLOSED)) {
            char_puts("It's already closed.\n", ch);
            return;
            }

            SET_BIT(obj->value[1],EX_CLOSED);
            act_puts("You close $p.", ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n closes $p.", ch, obj, NULL, TO_ROOM);
            return;
        }

        /* 'close object' */
        if (obj->pIndexData->item_type != ITEM_CONTAINER)
            { char_puts("That's not a container.\n", ch); return; }
        if (IS_SET(obj->value[1], CONT_CLOSED))
            { char_puts("It's already closed.\n", ch); return; }
        if (!IS_SET(obj->value[1], CONT_CLOSEABLE))
            { char_puts("You can't do that.\n", ch); return; }

        SET_BIT(obj->value[1], CONT_CLOSED);
        act_puts("You close $p.", ch, obj, NULL, TO_CHAR, POS_DEAD);
        act("$n closes $p.", ch, obj, NULL, TO_ROOM);
        return;
    }

    if ((door = find_door(ch, arg)) >= 0) {
        /* 'close door' */
        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))
            { char_puts("It's already closed.\n", ch); return; }

        SET_BIT(pexit->exit_info, EX_CLOSED);
        act("$n closes $d.", ch, NULL, pexit->keyword, TO_ROOM);
        char_puts("Ok.\n", ch);

        /* close the other side */
        if ((to_room   = pexit->to_room.r           ) != NULL
        &&   (pexit_rev = to_room->exit[rev_dir[door]]) != 0
        &&   pexit_rev->to_room.r == ch->in_room) {
            ROOM_INDEX_DATA *in_room;

            SET_BIT(pexit_rev->exit_info, EX_CLOSED);
            in_room = ch->in_room;
            ch->in_room = to_room;
            act("The $d closes.", ch, NULL, pexit_rev->keyword,
                TO_ROOM);
            ch->in_room = in_room;
        }
        return;
    }
}

/* 
 * Added can_see check. Kio.
 */
bool has_key(CHAR_DATA *ch, int key)
{
    OBJ_DATA *obj;

    for (obj = ch->carrying; obj; obj = obj->next_content)
        if (obj->pIndexData->vnum == key
        &&  can_see_obj(ch, obj))
            return TRUE;

    return FALSE;
}

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

    for (obj = ch->in_room->contents; obj; obj = obj->next_content)
        if (obj->pIndexData->vnum == key
        &&  can_see_obj(ch, obj))
            return TRUE;

    return FALSE;
}

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

    one_argument(argument, arg, sizeof(arg));

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

    if ((obj = get_obj_here(ch, arg)) != NULL) {
        /* portal stuff */
        if (obj->pIndexData->item_type == ITEM_PORTAL) {
            if (!IS_SET(obj->value[1], EX_ISDOOR)
            ||  IS_SET(obj->value[1], EX_NOCLOSE)) {
            char_puts("You can't do that.\n", ch);
            return;
            }
            if (!IS_SET(obj->value[1], EX_CLOSED)) {
            char_puts("It's not closed.\n", ch);
            return;
            }

            if (obj->value[4] < 0 || IS_SET(obj->value[1], EX_NOLOCK)) {
            char_puts("It can't be locked.\n", ch);
            return;
            }

            if (!has_key(ch, obj->value[4])) {
            char_puts("You lack the key.\n", ch);
            return;
            }

            if (IS_SET(obj->value[1], EX_LOCKED)) {
            char_puts("It's already locked.\n", ch);
            return;
            }

            SET_BIT(obj->value[1], EX_LOCKED);
            act_puts("You lock $p.", ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n locks $p.", ch, obj, NULL, TO_ROOM);
            return;
        }

        /* 'lock object' */
        if (obj->pIndexData->item_type != ITEM_CONTAINER)
            { char_puts("That's not a container.\n", ch); return; }
        if (!IS_SET(obj->value[1], CONT_CLOSED))
            { char_puts("It's not closed.\n", ch); return; }
        if (obj->value[2] < 0)
            { char_puts("It can't be locked.\n", ch); return; }
        if (!has_key(ch, obj->value[2]))
            { char_puts("You lack the key.\n", ch); return; }
        if (IS_SET(obj->value[1], CONT_LOCKED))
            { char_puts("It's already locked.\n", ch); return; }

        SET_BIT(obj->value[1], CONT_LOCKED);
        act_puts("You lock $p.", ch, obj, NULL, TO_CHAR, POS_DEAD);
        act("$n locks $p.", ch, obj, NULL, TO_ROOM);
        return;
    }

    if ((door = find_door(ch, arg)) >= 0)
    {
        /* 'lock door' */
        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))
            { char_puts("It's not closed.\n", ch); return; }
        if (pexit->key < 0)
            { char_puts("It can't be locked.\n", ch); return; }
        if (!has_key(ch, pexit->key) && 
               !has_key_ground(ch, pexit->key))
            { char_puts("You lack the key.\n", ch); return; }
        if (IS_SET(pexit->exit_info, EX_LOCKED))
            { char_puts("It's already locked.\n", ch); return; }

        SET_BIT(pexit->exit_info, EX_LOCKED);
        char_puts("*Click*\n", ch);
        act("$n locks the $d.", ch, NULL, pexit->keyword, TO_ROOM);

        /* lock the other side */
        if ((to_room   = pexit->to_room.r           ) != NULL
        &&   (pexit_rev = to_room->exit[rev_dir[door]]) != 0
        &&   pexit_rev->to_room.r == ch->in_room) {
            ROOM_INDEX_DATA *in_room;

            SET_BIT(pexit_rev->exit_info, EX_LOCKED);

            in_room = ch->in_room;
            ch->in_room = to_room;
            act("The $d clicks.",
                ch, NULL, pexit_rev->keyword, TO_ROOM);
            ch->in_room  = in_room;
        }
        return;
    }
}

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

    one_argument(argument, arg, sizeof(arg));

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

    if ((obj = get_obj_here(ch, arg)) != NULL) {
    /* portal stuff */
        if (obj->pIndexData->item_type == ITEM_PORTAL) {
            if (IS_SET(obj->value[1],EX_ISDOOR)) {
            char_puts("You can't do that.\n", ch);
            return;
            }

            if (!IS_SET(obj->value[1],EX_CLOSED)) {
            char_puts("It's not closed.\n", ch);
            return;
            }

            if (obj->value[4] < 0) {
            char_puts("It can't be unlocked.\n", ch);
            return;
            }

            if (!has_key(ch,obj->value[4])) {
            char_puts("You lack the key.\n", ch);
            return;
            }

            if (!IS_SET(obj->value[1],EX_LOCKED)) {
            char_puts("It's already unlocked.\n", ch);
            return;
            }

            REMOVE_BIT(obj->value[1],EX_LOCKED);
            act_puts("You unlock $p.", ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n unlocks $p.", ch, obj, NULL, TO_ROOM);
            return;
        }

        /* 'unlock object' */
        if (obj->pIndexData->item_type != ITEM_CONTAINER)
            { char_puts("That's not a container.\n", ch); return; }
        if (!IS_SET(obj->value[1], CONT_CLOSED))
            { char_puts("It's not closed.\n", ch); return; }
        if (obj->value[2] < 0)
            { char_puts("It can't be unlocked.\n", ch); return; }
        if (!has_key(ch, obj->value[2]))
            { char_puts("You lack the key.\n", ch); return; }
        if (!IS_SET(obj->value[1], CONT_LOCKED))
            { char_puts("It's already unlocked.\n", ch); return; }

        REMOVE_BIT(obj->value[1], CONT_LOCKED);
        act_puts("You unlock $p.", ch, obj, NULL, TO_CHAR, POS_DEAD);
        act("$n unlocks $p.", ch, obj, NULL, TO_ROOM);
        return;
    }

    if ((door = find_door(ch, arg)) >= 0)
    {
        /* 'unlock door' */
        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))
            { char_puts("It's not closed.\n", ch); return; }
        if (pexit->key < 0)
            { char_puts("It can't be unlocked.\n", ch); return; }
        if (!has_key(ch, pexit->key) && 
               !has_key_ground(ch, pexit->key))
            { char_puts("You lack the key.\n", ch); return; }
        if (!IS_SET(pexit->exit_info, EX_LOCKED))
            { char_puts("It's already unlocked.\n", ch); return; }

        REMOVE_BIT(pexit->exit_info, EX_LOCKED);
        char_puts("*Click*\n", ch);
        act("$n unlocks the $d.", ch, NULL, pexit->keyword, TO_ROOM);

        /* unlock the other side */
        if ((to_room   = pexit->to_room.r           ) != NULL
        &&   (pexit_rev = to_room->exit[rev_dir[door]]) != NULL
        &&   pexit_rev->to_room.r == ch->in_room) {
            ROOM_INDEX_DATA *in_room;

            REMOVE_BIT(pexit_rev->exit_info, EX_LOCKED);

            in_room = ch->in_room;
            ch->in_room = to_room;
            act("The $d clicks.",
                ch, NULL, pexit_rev->keyword, TO_ROOM);
            ch->in_room = in_room;
        }
        return;
    }
}

void do_pick(CHAR_DATA *ch, const char *argument)
{
    char arg[MAX_INPUT_LENGTH];
    CHAR_DATA *gch;
    OBJ_DATA *obj;
    int door;
    int chance;

    if ((chance = get_skill(ch, gsn_pick)) == 0) {
        char_puts("Huh?\n", ch);
        return;
    }

    one_argument(argument, arg, sizeof(arg));

    if (arg[0] == '\0') {
        char_puts("Pick what?\n", ch);
        return;
    }

    if (MOUNTED(ch)) {
          char_puts("You can't pick while mounted.\n", ch);
          return;
    }

    WAIT_STATE(ch, SKILL(gsn_pick)->beats);

    /* look for guards */
    for (gch = ch->in_room->people; gch; gch = gch->next_in_room) {
        if (IS_NPC(gch)
        &&  IS_AWAKE(gch)
        &&  ch->level + 5 < gch->level) {
            act("$N is standing too close to lock.",
                ch, NULL, gch, TO_CHAR);
            return;
        }
    }

    if (!IS_NPC(ch) && number_percent() > chance) {
        char_puts("You failed.\n", ch);
        check_improve(ch, gsn_pick, FALSE, 2);
        return;
    }

    if ((obj = get_obj_here(ch, arg)) != NULL) {
        /* portal stuff */
        if (obj->pIndexData->item_type == ITEM_PORTAL) {
            if (!IS_SET(obj->value[1],EX_ISDOOR)) { 
            char_puts("You can't do that.\n", ch);
            return;
            }

            if (!IS_SET(obj->value[1],EX_CLOSED)) {
            char_puts("It's not closed.\n", ch);
            return;
            }

            if (obj->value[4] < 0) {
            char_puts("It can't be unlocked.\n", ch);
            return;
            }

            if (IS_SET(obj->value[1],EX_PICKPROOF)) {
            char_puts("You failed.\n", ch);
            return;
            }

            REMOVE_BIT(obj->value[1],EX_LOCKED);
            act_puts("You pick the lock on $p.", ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n picks the lock on $p.", ch, obj, NULL, TO_ROOM);
            check_improve(ch, gsn_pick, TRUE, 2);
            return;
        }

        
        /* 'pick object' */
        if (obj->pIndexData->item_type != ITEM_CONTAINER)
            { char_puts("That's not a container.\n", ch); return; }
        if (!IS_SET(obj->value[1], CONT_CLOSED))
            { char_puts("It's not closed.\n", ch); return; }
        if (obj->value[2] < 0)
            { char_puts("It can't be unlocked.\n", ch); return; }
        if (!IS_SET(obj->value[1], CONT_LOCKED))
            { char_puts("It's already unlocked.\n", ch); return; }
        if (IS_SET(obj->value[1], CONT_PICKPROOF))
            { char_puts("You failed.\n", ch); return; }

        REMOVE_BIT(obj->value[1], CONT_LOCKED);
        act_puts("You pick the lock on $p.", ch, obj, NULL, TO_CHAR, POS_DEAD);
        act("$n picks the lock on $p.", ch, obj, NULL, TO_ROOM);
        check_improve(ch, gsn_pick, TRUE, 2);
        return;
    }

    if ((door = find_door(ch, arg)) >= 0) {
        /* 'pick door' */
        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) && !IS_IMMORTAL(ch))
            { char_puts("It's not closed.\n", ch); return; }
        if (pexit->key < 0 && !IS_IMMORTAL(ch))
            { char_puts("It can't be picked.\n", ch); return; }
        if (!IS_SET(pexit->exit_info, EX_LOCKED))
            { char_puts("It's already unlocked.\n", ch); return; }
        if (IS_SET(pexit->exit_info, EX_PICKPROOF) && !IS_IMMORTAL(ch))
            { char_puts("You failed.\n", ch); return; }

        REMOVE_BIT(pexit->exit_info, EX_LOCKED);
        char_puts("*Click*\n", ch);
        act("$n picks the $d.", ch, NULL, pexit->keyword, TO_ROOM);
        check_improve(ch, gsn_pick, TRUE, 2);

        /* pick the other side */
        if ((to_room   = pexit->to_room.r           ) != NULL
        &&   (pexit_rev = to_room->exit[rev_dir[door]]) != NULL
        &&   pexit_rev->to_room.r == ch->in_room)
            REMOVE_BIT(pexit_rev->exit_info, EX_LOCKED);
    }
}

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

    if (argument[0] != '\0') {
        if (ch->position == POS_FIGHTING) {
            char_puts("Maybe you should finish fighting first?\n", ch);
            return;
        }
        obj = get_obj_list(ch,argument,ch->in_room->contents);
        if (obj == NULL)
        {
            char_puts("You don't see that here.\n", ch);
            return;
        }
        if (obj->pIndexData->item_type != ITEM_FURNITURE
        ||  (!IS_SET(obj->value[2],STAND_AT)
        &&   !IS_SET(obj->value[2],STAND_ON)
        &&   !IS_SET(obj->value[2],STAND_IN)))
        {
            char_puts("You can't seem to find a place to stand.\n", ch);
            return;
        }
        if (ch->on != obj && count_users(obj) >= obj->value[0])
        {
            act_puts("There's no room to stand on $p.", ch, obj, NULL, TO_ROOM, POS_DEAD);
            return;
        }
    }
    switch (ch->position) {
    case POS_SLEEPING:
        if (IS_AFFECTED(ch, AFF_SLEEP))
            { char_puts("You can't wake up!\n", ch); return; }
        
        if (obj == NULL) {
            char_puts("You wake and stand up.\n", ch);
            act("$n wakes and stands up.", ch, NULL, NULL, TO_ROOM);
                
            ch->on = NULL;
        }
        else if (IS_SET(obj->value[2],STAND_AT))
        {
           act_puts("You wake and stand at $p.",
                ch, obj, NULL, TO_CHAR, POS_DEAD);
           act("$n wakes and stands at $p.", ch, obj, NULL, TO_ROOM);
                
        }
        else if (IS_SET(obj->value[2],STAND_ON))
        {
           act_puts("You wake and stand on $p.",
                ch, obj, NULL, TO_CHAR, POS_DEAD);
           act("$n wakes and stands on $p.", ch, obj, NULL, TO_ROOM);
        }
        else 
        {
           act_puts("You wake and stand in $p.",
                ch, obj, NULL, TO_CHAR, POS_DEAD);
           act("$n wakes and stands in $p.", ch, obj, NULL, TO_ROOM);
        }

        if (IS_HARA_KIRI(ch)) {
            char_puts("You feel your blood heats your body.\n", ch);
            REMOVE_BIT(ch->plr_flags, PLR_HARA_KIRI);
        }

        ch->position = POS_STANDING;
        do_look(ch,"auto");
        break;

    case POS_RESTING: case POS_SITTING:
        if (obj == NULL)
        {
            char_puts("You stand up.\n", ch);
            act("$n stands up.", ch, NULL, NULL, TO_ROOM);
            ch->on = NULL;
        }
        else if (IS_SET(obj->value[2],STAND_AT))
        {
            act_puts("You stand at $p.", ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n stands at $p.", ch, obj, NULL, TO_ROOM);
        }
        else if (IS_SET(obj->value[2],STAND_ON))
        {
            act_puts("You stand on $p.", ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n stands on $p.", ch, obj, NULL, TO_ROOM);
        }
        else
        {
            act_puts("You stand in $p.", ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n stands in $p.", ch, obj, NULL, TO_ROOM);
        }
        ch->position = POS_STANDING;
        break;

    case POS_STANDING:
        char_puts("You are already standing.\n", ch);
        break;

    case POS_FIGHTING:
        char_puts("You are already fighting!\n", ch);
        break;
    }
}

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

    if (ch->position == POS_FIGHTING) {
        char_puts("You are already fighting!\n", ch);
        return;
    }

    if (MOUNTED(ch)) {
          char_puts("You can't rest while mounted.\n", ch);
          return;
    }

    if (RIDDEN(ch)) {
          char_puts("You can't rest while being ridden.\n", ch);
          return;
    }

    if (IS_AFFECTED(ch, AFF_SLEEP))
    { char_puts("You are already sleeping.\n", ch); return; }

    /* okay, now that we know we can rest, find an object to rest on */
    if (argument[0] != '\0') {
        obj = get_obj_list(ch,argument,ch->in_room->contents);
        if (obj == NULL) {
            char_puts("You don't see that here.\n", ch);
            return;
        }
    }
    else obj = ch->on;

    if (obj != NULL) {
        if (!IS_SET(obj->pIndexData->item_type,ITEM_FURNITURE) 
        ||  (!IS_SET(obj->value[2],REST_ON) &&
             !IS_SET(obj->value[2],REST_IN) &&
             !IS_SET(obj->value[2],REST_AT))) {
            char_puts("You can't rest on that.\n", ch);
            return;
        }

        if (obj != NULL && ch->on != obj
        &&  count_users(obj) >= obj->value[0]) {
            act_puts("There's no more room on $p.",
                 ch, obj, NULL, TO_CHAR, POS_DEAD);
            return;
        }
        
        ch->on = obj;
    }

    switch (ch->position) {
    case POS_SLEEPING:
        if (obj == NULL) {
            char_puts("You wake up and start resting.\n", ch);
            act("$n wakes up and starts resting.",
            ch, NULL, NULL, TO_ROOM);
                
        }
        else if (IS_SET(obj->value[2],REST_AT)) {
            act_puts("You wake up and rest at $p.",
                 ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n wakes up and rests at $p.",
                ch, obj, NULL, TO_ROOM);
        }
        else if (IS_SET(obj->value[2],REST_ON)) {
            act_puts("You wake up and rest on $p.",
                 ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n wakes up and rests on $p.",
                ch, obj, NULL, TO_ROOM);
        }
        else {
            act_puts("You wake up and rest in $p.",
                 ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n wakes up and rests in $p.",
                ch, obj, NULL, TO_ROOM);
        }
        ch->position = POS_RESTING;
        break;

    case POS_RESTING:
        char_puts("You are already resting.\n", ch);
        break;

    case POS_STANDING:
        if (obj == NULL) {
            char_puts("You rest.\n", ch);
            act("$n sits down and rests.",
                ch, NULL, NULL, TO_ROOM);
        }
        else if (IS_SET(obj->value[2], REST_AT)) {
            act_puts("You sit down at $p and rest.",
                ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n sits down at $p and rests.",
                ch, obj, NULL, TO_ROOM);
        }
        else if (IS_SET(obj->value[2], REST_ON)) {
            act_puts("You sit down on $p and rest.",
                ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n sits down on $p and rests.",
                ch, obj, NULL, TO_ROOM);
        }
        else {
            act_puts("You sit down in $p and rest.",
                ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n sits down in $p and rests.",
                ch, obj, NULL, TO_ROOM);
        }
        ch->position = POS_RESTING;
        break;

    case POS_SITTING:
        if (obj == NULL) {
            char_puts("You rest.\n", ch);
            act("$n rests.", ch, NULL, NULL, TO_ROOM);
        }
        else if (IS_SET(obj->value[2],REST_AT)) {
            act_puts("You rest at $p.",
                 ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n rests at $p.", ch, obj, NULL, TO_ROOM);
        } 
        else if (IS_SET(obj->value[2],REST_ON)) {
            act_puts("You rest on $p.",
                 ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n rests on $p.", ch, obj, NULL, TO_ROOM);
        }
        else {
            act_puts("You rest in $p.",
                 ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n rests in $p.", ch, obj, NULL, TO_ROOM);
        }
        ch->position = POS_RESTING;

        if (IS_HARA_KIRI(ch)) {
            char_puts("You feel your blood heats your body.\n",
                  ch);
            REMOVE_BIT(ch->plr_flags, PLR_HARA_KIRI);
        }
        break;
    }
}

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

    if (ch->position == POS_FIGHTING) {
        char_puts("Maybe you should finish fighting first?\n", ch);
        return;
    }

    if (MOUNTED(ch)) {
          char_puts("You can't sit while mounted.\n", ch);
          return;
    }

    if (RIDDEN(ch)) {
          char_puts("You can't sit while being ridden.\n", ch);
          return;
    }

    if (IS_AFFECTED(ch, AFF_SLEEP))
    { char_puts("You are already sleeping.\n", ch); return; }

    /* okay, now that we know we can sit, find an object to sit on */
    if (argument[0] != '\0') {
        obj = get_obj_list(ch,argument,ch->in_room->contents);
        if (obj == NULL) {
            char_puts("You don't see that here.\n", ch);
            return;
        }
    }
    else
        obj = ch->on;

    if (obj != NULL) {
        if (!IS_SET(obj->pIndexData->item_type,ITEM_FURNITURE)
        ||  (!IS_SET(obj->value[2],SIT_ON)
        &&   !IS_SET(obj->value[2],SIT_IN)
        &&   !IS_SET(obj->value[2],SIT_AT))) {
            char_puts("You can't sit on that.\n", ch);
            return;
        }

        if (obj != NULL
        &&  ch->on != obj
        &&  count_users(obj) >= obj->value[0]) {
            act_puts("There's no more room on $p.",
                 ch, obj, NULL, TO_CHAR, POS_DEAD);
            return;
        }

        ch->on = obj;
    }

    switch (ch->position) {
    case POS_SLEEPING:
        if (obj == NULL) {
            char_puts("You wake and sit up.\n", ch);
            act("$n wakes and sits up.", ch, NULL, NULL, TO_ROOM);
        }
        else if (IS_SET(obj->value[2],SIT_AT)) {
            act_puts("You wake and sit at $p.",
                 ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n wakes and sits at $p.", ch, obj, NULL, TO_ROOM);
        }
        else if (IS_SET(obj->value[2],SIT_ON)) {
            act_puts("You wake and sit on $p.",
                 ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n wakes and sits on $p.", ch, obj, NULL, TO_ROOM);
        }
        else {
            act_puts("You wake and sit in $p.",
                 ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n wakes and sits in $p.", ch, obj, NULL, TO_ROOM);
        }

        ch->position = POS_SITTING;
        break;

    case POS_RESTING:
        if (obj == NULL)
            char_puts("You stop resting.\n", ch);
        else if (IS_SET(obj->value[2],SIT_AT)) {
            act_puts("You sit at $p.",
                 ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n sits at $p.", ch, obj, NULL, TO_ROOM);
        }
        else if (IS_SET(obj->value[2],SIT_ON)) {
            act_puts("You sit on $p.",
                 ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n sits on $p.", ch, obj, NULL, TO_ROOM);
        }
        else {
            act_puts("You sit in $p.",
                 ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n sits in $p.", ch, obj, NULL, TO_ROOM);
        }
        ch->position = POS_SITTING;
        break;

    case POS_SITTING:
        char_puts("You are already sitting down.\n", ch);
        break;

    case POS_STANDING:
        if (obj == NULL) {
            char_puts("You sit down.\n", ch);
            act("$n sits down on the ground.",
                ch, NULL, NULL, TO_ROOM);
        }
        else if (IS_SET(obj->value[2], SIT_AT)) {
            act_puts("You sit down at $p.",
                 ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n sits down at $p.", ch, obj, NULL, TO_ROOM);
        }
        else if (IS_SET(obj->value[2],SIT_ON)) {
            act_puts("You sit down on $p.",
                 ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n sits down on $p.", ch, obj, NULL, TO_ROOM);
        }
        else {
            act_puts("You sit down in $p.",
                 ch, obj, NULL, TO_CHAR, POS_DEAD);
            act("$n sits down in $p.", ch, obj, NULL, TO_ROOM);
        }
        ch->position = POS_SITTING;
        break;
    }

    if (IS_HARA_KIRI(ch)) {
         char_puts("You feel your blood heats your body.\n", ch);
         REMOVE_BIT(ch->plr_flags, PLR_HARA_KIRI);
    }
}

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

    if (MOUNTED(ch)) {
        char_puts("You can't sleep while mounted.\n", ch);
        return;
    }

    if (RIDDEN(ch)) {
        char_puts("You can't sleep while being ridden.\n", ch);
        return;
    }

    switch (ch->position) {
    case POS_SLEEPING:
        char_puts("You are already sleeping.\n", ch);
        break;

    case POS_RESTING:
    case POS_SITTING:
    case POS_STANDING: 
        if (argument[0] == '\0' && ch->on == NULL) {
            char_puts("You go to sleep.\n", ch);
            act("$n goes to sleep.", ch, NULL, NULL, TO_ROOM);
        }
        else { /* find an object and sleep on it */
            if (argument[0] == '\0')
                obj = ch->on;
            else
                obj = get_obj_list(ch, argument,
                           ch->in_room->contents);

            if (obj == NULL) {
                char_puts("You don't see that here.\n", ch);
                return;
            }

            if (obj->pIndexData->item_type != ITEM_FURNITURE
            ||  (!IS_SET(obj->value[2], SLEEP_ON) &&
                 !IS_SET(obj->value[2],SLEEP_IN) &&
                 !IS_SET(obj->value[2],SLEEP_AT))) {
                char_puts("You can't sleep on that.\n", ch);
                return;
            }

            if (ch->on != obj
            &&  count_users(obj) >= obj->value[0]) {
                act_puts("There's no room on $p for you.",
                     ch, obj, NULL, TO_CHAR, POS_DEAD);
                return;
            }

            ch->on = obj;
            if (IS_SET(obj->value[2], SLEEP_AT)) {
                act_puts("You go to sleep at $p.",
                     ch, obj, NULL, TO_CHAR, POS_DEAD);
                act("$n goes to sleep at $p.",
                    ch, obj, NULL, TO_ROOM);
            }
            else if (IS_SET(obj->value[2], SLEEP_ON)) {
                act_puts("You go to sleep on $p.",
                     ch, obj, NULL, TO_CHAR, POS_DEAD);
                act("$n goes to sleep on $p.",
                    ch, obj, NULL, TO_ROOM);
            }
            else {
                act_puts("You go to sleep in $p.",
                     ch, obj, NULL, TO_CHAR, POS_DEAD);
                act("$n goes to sleep in $p.",
                    ch, obj, NULL, TO_ROOM);
            }
        }
        ch->position = POS_SLEEPING;
        break;

    case POS_FIGHTING:
        char_puts("You are already fighting.\n", ch);
        break;
    }
}

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

    one_argument(argument, arg, sizeof(arg));
    if (arg[0] == '\0')
        { do_stand(ch, argument); return; }

    if (!IS_AWAKE(ch))
        { char_puts("You are asleep yourself!\n", ch); return; }

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

    if (IS_AWAKE(victim)) { 
        act_puts("$N is already awake.",
             ch, NULL, victim, TO_CHAR, POS_DEAD);
        return;
    }

    if (IS_AFFECTED(victim, AFF_SLEEP)) { 
        act_puts("You can't wake $M!",
             victim, NULL, ch, TO_VICT, POS_DEAD); 
        return; 
    }

    act_puts("$n wakes you", ch, NULL, victim, TO_VICT, POS_SLEEPING);
    do_stand(victim, str_empty);
    return;
}

void do_sneak(CHAR_DATA *ch, const char *argument)
{
    AFFECT_DATA af;
    int     chance;

    if ((chance = get_skill(ch, gsn_sneak)) == 0)
        return;

    if (MOUNTED(ch)) {
          char_puts("You can't sneak while mounted.\n", ch);
          return;
    }

    char_puts("You attempt to move silently.\n", ch);
    affect_strip(ch, gsn_sneak);

    if (IS_AFFECTED(ch, AFF_SNEAK))
        return;

    if (number_percent() < chance) {
        check_improve(ch, gsn_sneak, TRUE, 3);
        af.where     = TO_AFFECTS;
        af.type      = gsn_sneak;
        af.level     = ch->level; 
        af.duration  = ch->level;
        af.location  = APPLY_NONE;
        af.modifier  = 0;
        af.bitvector = AFF_SNEAK;
        affect_to_char(ch, &af);
    }
    else
        check_improve(ch, gsn_sneak, FALSE, 3);
}

void do_hide(CHAR_DATA *ch, const char *argument)
{
    int chance;
    flag32_t sector;

    if (MOUNTED(ch)) {
          char_puts("You can't hide while mounted.\n", ch);
          return;
    }

    if (RIDDEN(ch)) {
          char_puts("You can't hide while being ridden.\n", ch);
          return;
    }

    if (IS_AFFECTED(ch, AFF_FAERIE_FIRE) )  {
        char_puts("You cannot hide while glowing.\n", ch);
        return;
    }

    char_puts("You attempt to hide.\n", ch);

    if ((chance = get_skill(ch, gsn_hide)) == 0) {
        char_puts("Huh?\n", ch);
        return;
    }

    sector = ch->in_room->sector_type;
    if (sector == SECT_FOREST
    ||  sector == SECT_HILLS
    ||  sector == SECT_MOUNTAIN)
        chance += 15;
    else if (sector == SECT_CITY)
        chance -= 15;
    
    if (number_percent() < chance) {
        SET_BIT(ch->affected_by, AFF_HIDE);
        check_improve(ch, gsn_hide, TRUE, 3);
    }
    else  {
        REMOVE_BIT(ch->affected_by, AFF_HIDE);
        check_improve(ch, gsn_hide, FALSE, 3);
    }
}

void do_camouflage(CHAR_DATA *ch, const char *argument)
{
    int     sn;
    int     chance;
    flag32_t        sector;

    if (MOUNTED(ch)) {
        char_puts("You can't camouflage while mounted.\n", ch);
        return;
    }

    if (RIDDEN(ch)) {
        char_puts("You can't camouflage while being ridden.\n", ch);
        return;
    }

    if (IS_AFFECTED(ch, AFF_FAERIE_FIRE))  {
        char_puts("You can't camouflage yourself while glowing.\n", ch);
        return;
    }

    if ((sn = sn_lookup("camouflage")) < 0
    ||  (chance = get_skill(ch, sn)) == 0) {
        char_puts("You don't know how to camouflage yourself.\n", ch);
        return;
    }

    sector = ch->in_room->sector_type;
    if (sector != SECT_FOREST
    &&  sector != SECT_HILLS
    &&  sector != SECT_MOUNTAIN) {
        char_puts("There's no cover here.\n", ch);
        act("$n tries to camouflage $mself against the lone leaf on the ground.",
            ch, NULL, NULL, TO_ROOM);
        return;
    }

    char_puts("You attempt to camouflage yourself.\n", ch);
    WAIT_STATE(ch, SKILL(sn)->beats);

    if (IS_AFFECTED(ch, AFF_CAMOUFLAGE))
        REMOVE_BIT(ch->affected_by, AFF_CAMOUFLAGE);

    if (IS_NPC(ch) || number_percent() < chance) {
        SET_BIT(ch->affected_by, AFF_CAMOUFLAGE);
        check_improve(ch, sn, TRUE, 1);
    }
    else
        check_improve(ch, sn, FALSE, 1);
}

/*
 * Contributed by Alander
 */
void do_visible(CHAR_DATA *ch, const char *argument)
{
    if (IS_AFFECTED(ch, AFF_HIDE | AFF_FADE)) {
        REMOVE_BIT(ch->affected_by, AFF_HIDE | AFF_FADE);
        act_puts("You step out of shadows.",
             ch, NULL, NULL, TO_CHAR, POS_DEAD);
        act_puts("$n steps out of shadows.",
             ch, NULL, NULL, TO_ROOM, POS_RESTING);
    }

    if (IS_AFFECTED(ch, AFF_CAMOUFLAGE)) {
        REMOVE_BIT(ch->affected_by, AFF_CAMOUFLAGE);
        act_puts("You step out from your cover.",
             ch, NULL, NULL, TO_CHAR, POS_DEAD);
        act("$n steps out from $m's cover.",
            ch, NULL, NULL, TO_ROOM);
    }

    if (IS_AFFECTED(ch, AFF_INVIS | AFF_IMP_INVIS)) {
        REMOVE_BIT(ch->affected_by, AFF_INVIS | AFF_IMP_INVIS);
        affect_bit_strip(ch, TO_AFFECTS, AFF_INVIS | AFF_IMP_INVIS);
        act_puts("You fade into existence.",
             ch, NULL, NULL, TO_CHAR, POS_DEAD);
        act("$n fades into existence.", ch, NULL, NULL, TO_ROOM);
    }
}

void do_recall(CHAR_DATA *ch, const char *argument)
{
    ROOM_INDEX_DATA *location;
    CHAR_DATA *pet;
 
    if (IS_NPC(ch)) {
        char_puts("Only players can recall.\n", ch);
        return;
    }

    if (ch->level >= 10 && !IS_IMMORTAL(ch)) {
        char_puts("Recall is for only levels below 10.\n", ch);
        return;
    }

    if (ch->desc) {
        if (IS_PUMPED(ch)) {
            act_puts("You are too pumped to pray now.",
                 ch, NULL, NULL, TO_CHAR, POS_DEAD);
            return;
        }
        location = get_recall(ch);
    }
    else 
        location = get_random_recall();

    act("$n prays for transportation!", ch, NULL, NULL, TO_ROOM);
    
    if (ch->in_room == location)
        return;

    if (IS_SET(ch->in_room->room_flags, ROOM_NORECALL)
    ||  IS_AFFECTED(ch, AFF_CURSE) 
    ||  IS_RAFFECTED(ch->in_room, RAFF_CURSE)) {
        char_puts("The gods have forsaken you.\n", ch);
        return;
    }

    pet = ch->pet;
    ch->move /= 2;
    recall(ch, location);

    if (pet && !IS_AFFECTED(pet, AFF_SLEEP)) {
        if (pet->position != POS_STANDING)
            do_stand(pet, str_empty);
        recall(pet, location);
    }
}

void do_train(CHAR_DATA *ch, const char *argument)
{
    char buf[MAX_STRING_LENGTH];
    CHAR_DATA *mob;
    int stat = - 1;
    char *pOutput = NULL;

    if (IS_NPC(ch))
        return;

    /*
     * Check for trainer.
     */
    for (mob = ch->in_room->people; mob; mob = mob->next_in_room)
        if (IS_NPC(mob)
        &&  IS_SET(mob->pIndexData->act,
               ACT_PRACTICE | ACT_TRAIN | ACT_GAIN))
            break;

    if (mob == NULL) {
        char_puts("You can't do that here.\n", ch);
        return;
    }

    if (argument[0] == '\0') {
        char_printf(ch, "You have %d training sessions.\n",
                ch->train);
        argument = "foo";
    }

    if (!str_cmp(argument, "str")) {
        stat        = STAT_STR;
        pOutput     = "strength";
    }
    else if (!str_cmp(argument, "int")) {
        stat        = STAT_INT;
        pOutput     = "intelligence";
    }
    else if (!str_cmp(argument, "wis")) {
        stat        = STAT_WIS;
        pOutput     = "wisdom";
    }
    else if (!str_cmp(argument, "dex")) {
        stat        = STAT_DEX;
        pOutput     = "dexterity";
    }
    else if (!str_cmp(argument, "con")) {
        stat        = STAT_CON;
        pOutput     = "constitution";
    }
    else if (!str_cmp(argument, "cha")) {
        stat        = STAT_CHA;
        pOutput     = "charisma";
    }
    else {
        snprintf(buf, sizeof(buf),
             GETMSG("You can train: %s%s%s%s%s%s", ch->lang),
             ch->perm_stat[STAT_STR] < get_max_train(ch, STAT_STR) ?
                " str" : str_empty,
             ch->perm_stat[STAT_INT] < get_max_train(ch, STAT_INT) ?
                " int" : str_empty,
             ch->perm_stat[STAT_WIS] < get_max_train(ch, STAT_WIS) ?
                " wis" : str_empty,
             ch->perm_stat[STAT_DEX] < get_max_train(ch, STAT_DEX) ?
                " dex" : str_empty,
             ch->perm_stat[STAT_CON] < get_max_train(ch, STAT_CON) ?
                " con" : str_empty,
             ch->perm_stat[STAT_CHA] < get_max_train(ch, STAT_CHA) ?
                " cha" : str_empty);

        if (buf[strlen(buf)-1] != ':')
            char_printf(ch, "%s.\n", buf);
        else {
            /*
             * This message dedicated to Jordan ... you big stud!
             */
            act("You have nothing left to train, you $T!",
                ch, NULL,
                ch->sex == SEX_MALE   ? "big stud" :
                ch->sex == SEX_FEMALE ? "hot babe" :
                            "wild thing",
                TO_CHAR | ACT_TRANS);
        }
        return;
    }

    if (ch->perm_stat[stat] >= get_max_train(ch,stat)) {
        act_puts("Your $T is already at maximum.",
             ch, NULL, pOutput, TO_CHAR | ACT_TRANS, POS_DEAD);
        return;
    }

    if (ch->train < 1) {
        char_puts("You don't have enough training sessions.\n", ch);
        return;
    }

    ch->train--;
    ch->perm_stat[stat] += 1;
    act_puts("Your $T increases!",
         ch, NULL, pOutput, TO_CHAR | ACT_TRANS, POS_DEAD);
    act("$n's $T increases!", ch, NULL, pOutput, TO_ROOM | ACT_TRANS);
}

void do_track(CHAR_DATA *ch, const char *argument)
{
    ROOM_HISTORY_DATA *rh;
    EXIT_DATA *pexit;
    static char *door[] = { "north","east","south","west","up","down",
                              "that way" };
    int d;
    int chance;

    if ((chance = get_skill(ch, gsn_track)) == 0) {
        char_puts("There are no train tracks here.\n", ch);
        return;
    }

    WAIT_STATE(ch, SKILL(gsn_track)->beats);
    act("$n checks the ground for tracks.", ch, NULL, NULL, TO_ROOM);

    if (number_percent() < chance) {
        if (IS_NPC(ch)) {
            if (ch->last_fought != NULL
            &&  !IS_SET(ch->pIndexData->act, ACT_NOTRACK))
                add_mind(ch, ch->last_fought->name);
        }

        for (rh = ch->in_room->history; rh != NULL; rh = rh->next)
            if (is_name(argument, rh->name)) {
                check_improve(ch, gsn_track, TRUE, 1);
            if ((d = rh->went) == -1)
                continue;
            char_printf(ch, "%s's tracks lead %s.\n",
                     rh->name, door[d]);
            if ((pexit = ch->in_room->exit[d]) != NULL
            &&  IS_SET(pexit->exit_info, EX_ISDOOR)
            &&  pexit->keyword != NULL)
                doprintf(do_open, ch, "%s", door[d]);
            move_char(ch, rh->went, FALSE);
            return;
        }
    }

    char_puts("You don't see any tracks.\n", ch);
    check_improve(ch, gsn_track, FALSE, 1);
}

void do_vampire(CHAR_DATA *ch, const char *argument)
{
    AFFECT_DATA af;
    int level, duration;
    int chance;
 
    if (is_affected(ch, gsn_vampire)) {
        char_puts("But you are already vampire. Kill them! Kill them!\n", ch);
        return;
    }

    if ((chance = get_skill(ch, gsn_vampire)) == 0) {
        char_puts("You try to show yourself even more ugly.\n", ch);
        return;
    }

    if (chance < 100) {
        char_puts("Go and ask the questor. He'll help you.\n", ch);
        return;
    }

    if (weather_info.sunlight == SUN_LIGHT 
    ||  weather_info.sunlight == SUN_RISE) {
        char_puts("You should wait for the evening or night to transform to a vampire.\n", ch);
        return;
    }

    level = ch->level;
    duration = level / 10 + 5;

    af.type      = gsn_vampire;
    af.level     = level;
    af.duration  = duration;

/* negative immunity */
    af.where = TO_IMMUNE;
    af.location = APPLY_NONE;
    af.modifier = 0;
    af.bitvector = IMM_NEGATIVE;
    affect_to_char(ch, &af);

/* haste */
    af.where     = TO_AFFECTS;
    af.location  = APPLY_DEX;
    af.modifier  = 1 + (level /20);
    af.bitvector = AFF_HASTE;
    affect_to_char(ch, &af);

/* giant strength + infrared */
    af.location  = APPLY_STR;
    af.modifier  = 1 + (level / 20);
    af.bitvector = 0;
    affect_to_char(ch, &af);

/* size */
    af.location  = APPLY_SIZE;
    af.modifier  = 1 + (level / 50);
    affect_to_char(ch, &af);

/* damroll */
    af.where     = TO_AFFECTS;
    af.location  = APPLY_DAMROLL;
    af.modifier  = ch->damroll;
    af.bitvector = AFF_BERSERK;
    affect_to_char(ch, &af);

/* flying, infrared */
    af.where     = TO_AFFECTS;
    af.location  = 0;
    af.modifier  = 0;
    af.bitvector = AFF_SNEAK | AFF_FLYING | AFF_INFRARED;
    affect_to_char(ch, &af);

    char_puts("You feel yourself getting greater and greater.\n", ch);
    act("You cannot recognize $n anymore.", ch, NULL, NULL, TO_ROOM);
}

void do_vbite(CHAR_DATA *ch, const char *argument)
{
    char arg[MAX_INPUT_LENGTH];
    CHAR_DATA *victim;
    int chance;

    one_argument(argument, arg, sizeof(arg));

    if ((chance = get_skill(ch, gsn_vampiric_bite)) == 0) {
        char_puts("You don't know how to bite creatures.\n", ch);
        return;
    }

    if (!is_affected(ch, gsn_vampire)) {
        char_puts("You must transform vampire before biting.\n", ch);
        return;
    }

    if (arg[0] == '\0') {
        char_puts("Bite whom?\n", ch);
        return;
    }

    if ((victim = get_char_room(ch, arg)) == NULL) {
        WAIT_STATE(ch, MISSING_TARGET_DELAY);
        char_puts("They aren't here.\n", ch);
        return;
    }

    if (victim->position != POS_SLEEPING) {
        char_puts("They must be sleeping.\n", ch);
        return;
    }

    if (IS_NPC(ch) && !IS_NPC(victim))
        return;

    if (victim == ch) {
        char_puts("How can you sneak upon yourself?\n", ch);
        return;
    }

    if (victim->fighting != NULL) {
        char_puts("You can't bite a fighting person.\n", ch);
        return;
    }

    if (is_safe(ch, victim))
        return;

    if (victim->hit < (8 * victim->max_hit / 10) ) {
        act_puts("$N is hurt and suspicious ... doesn't worth up.",
             ch, NULL, victim, TO_CHAR, POS_DEAD);
        return;
    }


    WAIT_STATE(ch, SKILL(gsn_vampiric_bite)->beats);

    if (!IS_AWAKE(victim)
    &&  (IS_NPC(ch) ||
         number_percent() < ((chance * 7 / 10) +
        (2 * (ch->level - victim->level)) ))) {
        check_improve(ch,gsn_vampiric_bite,TRUE,1);
        one_hit(ch, victim, gsn_vampiric_bite, WEAR_WIELD);
    }
    else {
        check_improve(ch, gsn_vampiric_bite, FALSE, 1);
        damage(ch, victim, 0, gsn_vampiric_bite, DAM_NONE, DAMF_SHOW);
    }
    if (!IS_NPC(victim) && victim->position==POS_FIGHTING) 
        doprintf(do_yell, victim, 
            "Help! %s tried to bite me!", PERS(ch, victim));
}

void do_bash_door(CHAR_DATA *ch, const char *argument)
{
    char arg[MAX_INPUT_LENGTH];
    CHAR_DATA *gch;
    int chance;
    int damage_bash,door;
    int beats;

    ROOM_INDEX_DATA *to_room;
    EXIT_DATA *pexit;
    EXIT_DATA *pexit_rev;

    one_argument(argument, arg, sizeof(arg));
 
    if ((chance = get_skill(ch, gsn_bash_door)) == 0) {
        char_puts("Bashing? What's that?\n", ch);
        return;
    }
 
    if (MOUNTED(ch)) {
            char_puts("You can't bash doors while mounted.\n", ch);
        return;
    }

    if (RIDDEN(ch)) {
        char_puts("You can't bash doors while being ridden.\n", ch);
        return;
    }

    if (arg[0] == '\0') {
        char_puts("Bash wich door or direction?\n", ch);
        return;
    }

    if (ch->fighting) { 
        char_puts("Wait until the fight finishes.\n", ch);
        return;
    }

    /* look for guards */
    for (gch = ch->in_room->people; gch; gch = gch->next_in_room)
        if (IS_NPC(gch)
        &&  IS_AWAKE(gch) && ch->level + 5 < gch->level) {
            act_puts("$N is standing too close to door.",
                 ch, NULL, gch, TO_CHAR, POS_DEAD);
            return;
        }

    if ((door = find_door(ch, arg)) < 0)
        return;

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

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

    if (!IS_SET(pexit->exit_info, EX_LOCKED)) {
        char_puts("Just try to open it.\n", ch);
        return;
    }

    if (IS_SET(pexit->exit_info, EX_NOPASS)) {
        char_puts("A mystical shield protects exit.\n", ch); 
        return;
    }

    chance -= 90;

    /* modifiers */

    /* size and weight */
    chance += get_carry_weight(ch) / 100;
    chance += (ch->size - 2) * 20;

    /* stats */
    chance += get_curr_stat(ch, STAT_STR);

    if (IS_AFFECTED(ch,AFF_FLYING))
        chance -= 10;

    act_puts("You slam into $d, and try to break $d!",
         ch, NULL, pexit->keyword, TO_CHAR, POS_DEAD);
    act("$n slams into $d, and tries to break it!",
        ch, NULL, pexit->keyword, TO_ROOM);

    if (room_dark(ch->in_room))
        chance /= 2;

    beats = SKILL(gsn_bash_door)->beats;
    /* now the attack */
    if (number_percent() < chance) {
        check_improve(ch, gsn_bash_door, TRUE, 1);

        REMOVE_BIT(pexit->exit_info, EX_LOCKED);
        REMOVE_BIT(pexit->exit_info, EX_CLOSED);
        act("$n bashes the $d and breaks the lock.",
            ch, NULL, pexit->keyword, TO_ROOM);
        act_puts("You successed to open the door.",
             ch, NULL, NULL, TO_CHAR, POS_DEAD);

/* open the other side */
        if ((to_room = pexit->to_room.r) != NULL
        &&  (pexit_rev = to_room->exit[rev_dir[door]]) != NULL
        &&  pexit_rev->to_room.r == ch->in_room) {
            ROOM_INDEX_DATA *in_room;

            REMOVE_BIT(pexit_rev->exit_info, EX_CLOSED);
            REMOVE_BIT(pexit_rev->exit_info, EX_LOCKED);

            in_room = ch->in_room;
            ch->in_room = to_room;
            act("$n bashes the $d and breaks the lock.",
                ch, NULL, pexit->keyword, TO_ROOM);
            ch->in_room = in_room;
        }

        check_improve(ch, gsn_bash_door, TRUE, 1);
        WAIT_STATE(ch, beats);
    }
    else {
        act_puts("You fall flat on your face!",
             ch, NULL, NULL, TO_CHAR, POS_DEAD);
        act_puts("$n falls flat on $s face.",
             ch, NULL, NULL, TO_ROOM, POS_RESTING);
        check_improve(ch, gsn_bash_door, FALSE, 1);
        ch->position = POS_RESTING;
        WAIT_STATE(ch, beats * 3 / 2); 
        damage_bash = ch->damroll +
                  number_range(4,4 + 4* ch->size + chance/5);
        damage(ch, ch, damage_bash, gsn_bash_door, DAM_BASH, DAMF_SHOW);
    }
}

void do_blink(CHAR_DATA *ch, const char *argument)
{
    char arg[MAX_INPUT_LENGTH];

    if (get_skill(ch, gsn_blink) == 0) {
        char_puts("Huh?\n", ch);
        return;
    }

    argument = one_argument(argument, arg, sizeof(arg));

    if (arg[0] == '\0') {
        char_printf(ch, "Your current blink status: %s.\n",
                IS_SET(ch->plr_flags, PLR_BLINK) ? "ON" : "OFF");
        return;
    }

    if (!str_cmp(arg, "ON")) {
        SET_BIT(ch->plr_flags, PLR_BLINK);
        char_puts("Now, your current blink status is ON.\n", ch);
        return;
    }

    if (!str_cmp(arg, "OFF")) {
        REMOVE_BIT(ch->plr_flags, PLR_BLINK);
        char_puts("Now, your current blink status is OFF.\n", ch);
        return;
    }

    char_printf(ch, "What's that? Is %s a status?\n", arg);
}

void do_vanish(CHAR_DATA *ch, const char *argument)
{
    int chance;
    int sn;
    int min_mana;

    if ((sn = sn_lookup("vanish")) < 0
    ||  (chance = get_skill(ch, sn)) == 0) {
        char_puts("Huh?\n", ch);
        return;
    }

    if (ch->mana < (min_mana = SKILL(sn)->min_mana)) {
        char_puts("You don't have enough power.\n", ch);
        return;
    }
    ch->mana -= min_mana;
    WAIT_STATE(ch, SKILL(sn)->beats);

    if (number_percent() > chance) {
        char_puts("You failed.\n", ch);
        check_improve(ch, sn, FALSE, 1);
        return;
    }

    if (IS_SET(ch->in_room->room_flags, 
           ROOM_NORECALL | ROOM_PEACE | ROOM_SAFE)) {
        char_puts("You failed.\n", ch);
        return;
    }

    act("$n throws down a small globe.", ch, NULL, NULL, TO_ROOM);
    check_improve(ch, sn, TRUE, 1);

    if (!IS_NPC(ch) && ch->fighting && number_bits(1) == 1) {
        char_puts("You failed.\n", ch);
        return;
    }

    stop_fighting(ch, TRUE);
    transfer_char(ch, NULL, get_random_room(ch, ch->in_room->area),
              "$N is gone!", NULL, "$N appears from nowhere.");
}

void do_fade(CHAR_DATA *ch, const char *argument)
{
    int chance;
    if ((chance=get_skill(ch, gsn_fade)) == 0)
        return;

    if (MOUNTED(ch)) {
          char_puts("You can't fade while mounted.\n", ch);
          return;
    }

    if (RIDDEN(ch)) {
          char_puts("You can't fade while being ridden.\n", ch);
          return;
    }

    char_puts("You attempt to fade.\n", ch);
    if (number_percent()<=chance) {
        SET_BIT(ch->affected_by, AFF_FADE);
        check_improve(ch, gsn_fade, TRUE, 3);
    }
    else check_improve(ch, gsn_fade, FALSE, 3);
}

void do_vtouch(CHAR_DATA *ch, const char *argument)
{
    CHAR_DATA *victim;
    AFFECT_DATA af;
    int chance;
    int sn;

    if ((sn = sn_lookup("vampiric touch")) < 0
    ||  (chance = get_skill(ch, sn)) == 0) {
        char_puts("You lack the skill to draining touch.\n", ch);
        return;
    }

    if (!is_affected(ch, gsn_vampire)) {
        char_puts("Let it be.\n", ch);
        return;
    }

    if (IS_AFFECTED(ch, AFF_CHARM))  {
        char_puts("You don't want to drain your master.\n", ch);
        return;
    } 

    if ((victim = get_char_room(ch,argument)) == NULL) {
        WAIT_STATE(ch, MISSING_TARGET_DELAY);
        char_puts("They aren't here.\n", ch);
        return;
    }

    if (ch == victim) {
        char_puts("Even you are not so stupid.\n", ch);
        return;
    }

    if (is_affected(victim, sn))
        return;

    if (is_safe(ch,victim))
        return;

    WAIT_STATE(ch, SKILL(sn)->beats);

    SET_FIGHT_TIME(victim);
    SET_FIGHT_TIME(ch);

    if (number_percent() < 0.85 * chance
    &&  !IS_CLAN_GUARD(victim)
    &&  !IS_IMMORTAL(victim)) {
        act_puts("You deadly touch $n's neck and put $m to nightmares.",
             victim, NULL, ch, TO_VICT, POS_DEAD); 
        act_puts("$N deadly touches your neck and puts you "
             "to nightmares.", victim, NULL, ch, TO_CHAR, POS_DEAD);
        act("$N deadly touches $n's neck and puts $m to nightmares.",
            victim, NULL, ch, TO_NOTVICT);

        check_improve(ch, sn, TRUE, 1);
        
        af.type = sn;
        af.where = TO_AFFECTS;
        af.level = ch->level;
        af.duration = ch->level / 20 + 1;
        af.location = APPLY_NONE;
        af.modifier = 0;
        af.bitvector = AFF_SLEEP;
        affect_join(victim,&af);

        if (IS_AWAKE(victim))
            victim->position = POS_SLEEPING;
    } else {
        damage(ch, victim, 0, sn, DAM_NONE, DAMF_SHOW);
        check_improve(ch, sn, FALSE, 1);
    }
}

void do_fly(CHAR_DATA *ch, const char *argument)
{
    char arg[MAX_INPUT_LENGTH];

    if (IS_NPC(ch))
        return;

    argument = one_argument(argument, arg, sizeof(arg));

    if (!str_cmp(arg,"up")) {
        race_t *r;

        if (is_affected(ch, gsn_thumbling)) {
            char_puts("Stop jumping like a crazy rabbit first.\n",
                  ch);
            return;
        }

        if (IS_AFFECTED(ch, AFF_FLYING)) {             
            char_puts("You are already flying.\n", ch); 
            return;
        }

        if (is_bit_affected(ch, TO_AFFECTS, AFF_FLYING)
        ||  ((r = race_lookup(ch->race)) && (r->aff & AFF_FLYING))
        ||  has_obj_affect(ch, AFF_FLYING)) {
            SET_BIT(ch->affected_by, AFF_FLYING);
            char_puts("You start to fly.\n", ch);
        }
        else {
            char_puts("To fly find potion or wings.\n", ch); 
            return;
        }
    }
    else if (!str_cmp(arg,"down")) {
        if (IS_AFFECTED(ch,AFF_FLYING)) {
            REMOVE_BIT(ch->affected_by, AFF_FLYING);
            char_puts("You slowly touch the ground.\n", ch);
        }
        else {             
            char_puts("You are already on the ground.\n", ch); 
            return;
        }
    }
    else {
        char_puts("Type fly with 'up' or 'down'.\n", ch);
        return;
    }

    WAIT_STATE(ch, SKILL(gsn_fly)->beats);   
}
         
void do_push(CHAR_DATA *ch, const char *argument)
{
    char arg1 [MAX_INPUT_LENGTH];
    char arg2 [MAX_INPUT_LENGTH];
    CHAR_DATA *victim;
    EXIT_DATA *pexit;
    int percent;
    int door;
    int sn;

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

    if (arg1[0] == '\0' || arg2[0] == '\0') {
        char_puts("Push whom to what direction?\n", ch);
        return;
    }

    if (MOUNTED(ch)) {
        char_puts("You can't push while mounted.\n", ch);
        return;
    }

    if (RIDDEN(ch)) {
        char_puts("You can't push while being ridden.\n", ch);
        return;
    }

    if (IS_NPC(ch) && IS_SET(ch->affected_by, AFF_CHARM) 
        && (ch->master != NULL)) {
        char_puts("You are too dazed to push anyone.\n", ch);
        return;
    }

    if ((sn = sn_lookup("push")) < 0)
        return;

    if ((victim = get_char_room(ch, arg1)) == NULL) {
        WAIT_STATE(ch, MISSING_TARGET_DELAY);
        char_puts("They aren't here.\n", ch);
        return;
    }

    if (!IS_NPC(victim) && victim->desc == NULL) {
        char_puts("You can't do that.\n", ch);
        return;
    }

    if (victim == ch) {
        char_puts("That's pointless.\n", ch);
        return;
    }

    if (victim->position == POS_FIGHTING) {
        char_puts("Wait until the fight finishes.\n", ch);
        return;
    }

    if ((door = find_exit(ch, arg2)) < 0)
        return;

    WAIT_STATE(ch, SKILL(sn)->beats);

    if ((pexit = ch->in_room->exit[door])
    &&  IS_SET(pexit->exit_info, EX_ISDOOR)) {
        if (IS_SET(pexit->exit_info, EX_CLOSED)) {
            char_puts("The door is closed.\n", ch); 
            return;
        }
        if (IS_SET(pexit->exit_info, EX_LOCKED)) {
            char_puts("The door is locked.\n", ch); 
            return;
        }
    }


    if (IS_AFFECTED(ch, AFF_DETECT_WEB)) {
        char_puts("You're webbed, and want to do WHAT?!?\n", ch);
        act("$n stupidly tries to push $N while webbed.",
            ch, NULL, victim, TO_ROOM);
        return; 
    }

    if (IS_AFFECTED(victim, AFF_DETECT_WEB)) {
        act_puts("You attempt to push $N, but the webs hold $m "
             "in place.", victim, NULL, ch, TO_VICT, POS_DEAD);
        act("$n attempts to push $n, but fails as the webs hold "
            "$n in place.", victim, NULL, ch, TO_NOTVICT);
        return; 
    }

    if (is_safe(ch,victim))
        return;

    percent  = number_percent() + (IS_AWAKE(victim) ? 10 : -50);
    percent += can_see(victim, ch) ? -10 : 0;

    if (victim->position == POS_FIGHTING
    ||  (IS_NPC(victim) && IS_SET(victim->pIndexData->act, ACT_NOTRACK))
    ||  (!IS_NPC(ch) && percent > get_skill(ch, sn))
    ||  pexit->to_room.r->area != ch->in_room->area) {
        /*
         * Failure.
         */

        char_puts("Oops.\n", ch);
        if (!IS_AFFECTED(victim, AFF_SLEEP)) {
            victim->position = victim->position == POS_SLEEPING ? 
                       POS_STANDING : victim->position;
            act("$n tried to push you.",
                ch, NULL, victim, TO_VICT);
        }
        act("$n tried to push $N.", ch, NULL, victim, TO_NOTVICT);

        if (IS_AWAKE(victim))
            doprintf(do_yell, victim,
                 "Keep your hands out of me, %s!",
                 ch->name);
        if (!IS_NPC(ch) && IS_NPC(victim)) {
            check_improve(ch, sn, FALSE, 2);
            multi_hit(victim, ch, TYPE_UNDEFINED);
        }
        return;
    }

    act_puts("You push $N to $t.",
         ch, dir_name[door], victim, TO_CHAR | ACT_TRANS, POS_SLEEPING);
    act_puts("$n pushes you to $t.",
         ch, dir_name[door], victim, TO_VICT | ACT_TRANS, POS_SLEEPING);
    act("$n pushes $N to $t.",
        ch, dir_name[door], victim, TO_NOTVICT | ACT_TRANS);
    move_char(victim, door, FALSE);

    check_improve(ch, sn, TRUE, 1);
}

void do_crecall(CHAR_DATA *ch, const char *argument)
{
    ROOM_INDEX_DATA *location;
    clan_t *clan;
    CHAR_DATA *pet;
    AFFECT_DATA af;
    int sn;

    if ((sn = sn_lookup("clan recall")) < 0
    ||  get_skill(ch, sn) == 0
    ||  (clan = clan_lookup(ch->clan)) == NULL) {
        char_puts("Huh?\n", ch);
        return;
    }

    if (is_affected(ch, sn)) {
        act_puts("You can't pray now.",
             ch, NULL, NULL, TO_CHAR, POS_DEAD);
        return;
    }

    if (ch->desc && IS_PUMPED(ch)) {
        act_puts("You are too pumped to pray now.",
             ch, NULL, NULL, TO_CHAR, POS_DEAD);
        return;
    }

    act("$n prays upper Lord of Battleragers for transportation.",
        ch, NULL, NULL, TO_ROOM);
    
    if ((location = get_room_index(clan->recall_vnum)) == NULL) {
        char_puts("You are completely lost.\n", ch);
        return;
    }

    if (ch->in_room == location)
        return;

    if (IS_SET(ch->in_room->room_flags, ROOM_NORECALL)
    ||  IS_AFFECTED(ch, AFF_CURSE) 
    ||  IS_RAFFECTED(ch->in_room, RAFF_CURSE)) {
        char_puts("The gods have forsaken you.\n", ch);
        return;
    }

    ch->move /= 2;
    af.type      = sn;
    af.level     = ch->level;
    af.duration  = SKILL(sn)->beats;
    af.location  = APPLY_NONE;
    af.modifier  = 0;
    af.bitvector = 0;
    affect_to_char(ch, &af);

    pet = ch->pet;
    recall(ch, location);

    if (pet && !IS_AFFECTED(pet, AFF_SLEEP)) {
        if (pet->position != POS_STANDING)
            do_stand(pet, str_empty);
        recall(pet, location);
    }
}

void do_escape(CHAR_DATA *ch, const char *argument)
{
    ROOM_INDEX_DATA *was_in;
    ROOM_INDEX_DATA *now_in;
    EXIT_DATA *pexit;
    CHAR_DATA *victim;
    char arg[MAX_INPUT_LENGTH];
    int door;
    int chance;
    int sn;

    if ((victim = ch->fighting) == NULL) {
        if (ch->position == POS_FIGHTING)
            ch->position = POS_STANDING;
        char_puts("You aren't fighting anyone.\n", ch);
        return;
    }

    argument = one_argument(argument, arg, sizeof(arg));

    if (arg[0] == '\0') {
        char_puts("Escape to what direction?\n", ch);
        return;
    }

    if (MOUNTED(ch)) {
          char_puts("You can't escape while mounted.\n", ch);
          return;
    }

    if (RIDDEN(ch)) {
          char_puts("You can't escape while being ridden.\n", ch);
          return;
    }

    if ((sn = sn_lookup("escape")) < 0
    ||  (chance = get_skill(ch, sn)) == 0) {
        char_puts("Try flee. It may fit better to you.\n", ch);
        return;
    }

    was_in = ch->in_room;

    if ((door = find_exit(ch, arg)) < 0) {
        char_puts("PANIC! You couldn't escape!\n", ch);
        return;
    }

    if ((pexit = was_in->exit[door]) == 0
    ||  pexit->to_room.r == NULL
    ||  (IS_SET(pexit->exit_info, EX_CLOSED) &&
         (!IS_AFFECTED(ch, AFF_PASS_DOOR) ||
          IS_SET(pexit->exit_info, EX_NOPASS)) &&
         !IS_TRUSTED(ch, ANGEL))
    ||  IS_SET(pexit->exit_info, EX_NOFLEE)
    ||  (IS_NPC(ch) &&
         IS_SET(pexit->to_room.r->room_flags, ROOM_NOMOB))) {
        char_puts("Something prevents you to escape that direction.\n", ch); 
        return;
    }

    if (number_percent() > chance) {
        act_puts("You failed to escape.",
             ch, NULL, NULL, TO_CHAR, POS_DEAD);
        check_improve(ch, sn, FALSE, 1);    
        return;
    }

    check_improve(ch, sn, TRUE, 1); 
    move_char(ch, door, FALSE);
    if ((now_in = ch->in_room) == was_in) {
        char_puts("It's pointless to escape there.\n", ch);
        return;
    }

    ch->in_room = was_in;
    act("$n has escaped!", ch, NULL, NULL, TO_ROOM);
    ch->in_room = now_in;

    if (!IS_NPC(ch)) {
        act_puts("You escaped from combat!",
             ch, NULL, NULL, TO_CHAR, POS_DEAD);
        if (ch->level < LEVEL_HERO) {
            char_printf(ch, "You lose %d exps.\n", 10);
            gain_exp(ch, -10);
        }
    }
    else
        /* Once fled, the mob will not go after */
        ch->last_fought = NULL;

    stop_fighting(ch, TRUE);
}

void do_layhands(CHAR_DATA *ch, const char *argument)
{
    CHAR_DATA *victim;
    AFFECT_DATA af;
    int sn;

    if ((sn = sn_lookup("lay hands")) < 0
    ||  get_skill(ch, sn) == 0) {
        char_puts("You lack the skill to heal others with touching.\n", ch);
        return;
    }

    WAIT_STATE(ch, SKILL(sn)->beats);

    if ((victim = get_char_room(ch,argument)) == NULL) {
        char_puts("They aren't here.\n", ch);
        return;
    }

    if (is_affected(ch, sn)) {
         char_puts("You can't concentrate enough.\n", ch);
         return;
    }

    af.type = sn;
    af.where = TO_AFFECTS;
    af.level = ch->level;
    af.duration = 2;
    af.location = APPLY_NONE;
    af.modifier = 0;
    af.bitvector = 0;
    affect_to_char (ch, &af);

    victim->hit = UMIN(victim->hit + ch->level * 2, victim->max_hit);
    update_pos(victim);
    char_puts("A warm feeling fills your body.\n", victim);

    if (IS_AFFECTED(victim, AFF_BLIND)) 
        spell_cure_blindness(sn_lookup("cure blindness"), ch->level, 
            ch, (void*)victim, TARGET_CHAR);
    if (IS_AFFECTED(victim, AFF_PLAGUE))
        spell_cure_disease (sn_lookup("cure disease"), ch->level,
            ch, (void*)victim, TARGET_CHAR);
    if (IS_AFFECTED(victim, AFF_POISON))
        spell_cure_poison(sn_lookup("cure poison"), ch->level,
            ch, (void*)victim, TARGET_CHAR);

    if (ch != victim)
        char_puts("Ok.\n", ch);
    check_improve(ch, sn, TRUE, 1);
}

int mount_success(CHAR_DATA *ch, CHAR_DATA *mount, int canattack)
{
    int percent;
    int success;
    int chance;

    if ((chance = get_skill(ch, gsn_riding)) == 0)
        return FALSE;

    percent = number_percent() + (ch->level < mount->level ? 
          (mount->level - ch->level) * 3 : 
          (mount->level - ch->level) * 2);

    if (!ch->fighting)
        percent -= 25;

    if (!IS_NPC(ch) && IS_DRUNK(ch)) {
        percent += chance / 2;
        char_puts("Due to your being under the influence, riding seems "
              "a bit harder...\n", ch);
    }

    success = percent - chance;

    if (success <= 0) { /* Success */
        check_improve(ch, gsn_riding, TRUE, 1);
        return TRUE;
    }

    check_improve(ch, gsn_riding, FALSE, 1);
    if (success >= 10 && MOUNTED(ch) == mount) {
        act_puts("You lose control and fall off of $N.",
             ch, NULL, mount, TO_CHAR, POS_DEAD);
        act("$n loses control and falls off of $N.",
            ch, NULL, mount, TO_NOTVICT);
        act_puts("$n loses control and falls off of you.",
             ch, NULL, mount, TO_VICT, POS_SLEEPING);

        ch->riding = FALSE;
        mount->riding = FALSE;
        if (ch->position > POS_STUNNED) 
            ch->position=POS_SITTING;
    
        ch->hit -= 5;
        update_pos(ch);
    }
    if (success >= 40 && canattack) {
        act_puts("$N doesn't like the way you've been treating $M.",
             ch, NULL, mount, TO_CHAR, POS_DEAD);
        act("$N doesn't like the way $n has been treating $M.",
            ch, NULL, mount, TO_NOTVICT);
        act_puts("You don't like the way $n has been treating you.",
             ch, NULL, mount, TO_VICT, POS_SLEEPING);

        act_puts("$N snarls and attacks you!",
             mount, NULL, ch, TO_VICT, POS_DEAD);
        act("$N snarls and attacks $n!",
            mount, NULL, ch, TO_NOTVICT);
        act_puts("You snarl and attack $n!",
             mount, NULL, ch, TO_CHAR, POS_SLEEPING);

        damage(mount, ch, number_range(1, mount->level),
            gsn_kick, DAM_BASH, DAMF_SHOW);
    }
    return FALSE;
}

/*
 * It is not finished yet to implement all.
 */
void do_mount(CHAR_DATA *ch, const char *argument)
{
    char        arg[MAX_INPUT_LENGTH];
    CHAR_DATA * mount;

    argument = one_argument(argument, arg, sizeof(arg));

    if (arg[0] == '\0') {
        if (ch->mount && ch->mount->in_room == ch->in_room)
            mount = ch->mount;
        else {
            char_puts("Mount what?\n", ch);
            return;
        }
    }
    else if ((mount = get_char_room(ch, arg)) == NULL) {
        char_puts("You don't see that here.\n", ch);
        return;
    }
 
    if (!IS_NPC(mount) || !IS_SET(mount->pIndexData->act, ACT_RIDEABLE)
    ||  IS_SET(mount->pIndexData->act, ACT_NOTRACK)) { 
        char_puts("You can't ride that.\n", ch); 
        return;
    }
  
    if (mount->level - 5 > ch->level) {
        char_puts("That beast is too powerful for you to ride.\n", ch);
        return;
    }

    if ((mount->mount) && (!mount->riding) && (mount->mount != ch)) {
        act_puts("$N belongs to $i, not you.",
             ch, mount->mount, mount,
             TO_CHAR, POS_DEAD);
        return;
    } 

    if (mount->position < POS_STANDING) {
        char_puts("Your mount must be standing.\n", ch);
        return;
    }

    if (RIDDEN(mount)) {
        char_puts("This beast is already ridden.\n", ch);
        return;
    } else if (MOUNTED(ch)) {
        char_puts("You are already riding.\n", ch);
        return;
    }

    if(!mount_success(ch, mount, TRUE)) {
        char_puts("You fail to mount the beast.\n", ch);  
        return; 
    }

    act_puts("You hop on $N's back.", ch, NULL, mount, TO_CHAR, POS_DEAD);
    act("$n hops on $N's back.", ch, NULL, mount, TO_NOTVICT);
    act_puts("$n hops on your back!", ch, NULL, mount, TO_VICT, POS_SLEEPING);
 
    ch->mount = mount;
    ch->riding = TRUE;
    mount->mount = ch;
    mount->riding = TRUE;
  
    affect_bit_strip(ch, TO_AFFECTS, AFF_INVIS | AFF_IMP_INVIS | AFF_SNEAK);
    REMOVE_BIT(ch->affected_by, AFF_HIDE | AFF_FADE | AFF_CAMOUFLAGE |
                    AFF_INVIS | AFF_IMP_INVIS | AFF_SNEAK);
}

void do_dismount(CHAR_DATA *ch, const char *argument)
{
    CHAR_DATA *mount;

    if ((mount = MOUNTED(ch))) {
        act_puts("You dismount from $N.",
             ch, NULL, mount, TO_CHAR, POS_DEAD); 
        act("$n dismounts from $N.", ch, NULL, mount, TO_NOTVICT);
        act_puts("$n dismounts from you.",
             ch, NULL, mount, TO_VICT, POS_SLEEPING);

        ch->riding = FALSE;
        mount->riding = FALSE;
    }
    else {
        char_puts("You aren't mounted.\n", ch);
        return;
    }
} 

int send_arrow(CHAR_DATA *ch, CHAR_DATA *victim,OBJ_DATA *arrow, 
           int door, int chance ,int bonus) 
{
    EXIT_DATA *pExit;
    ROOM_INDEX_DATA *dest_room;
    AFFECT_DATA *paf;
    int damroll = 0, hitroll = 0, sn;
    AFFECT_DATA af;

    if (arrow->value[0] == WEAPON_SPEAR)  
        sn = gsn_spear;
    else 
        sn = gsn_arrow;

    for (paf = arrow->affected; paf != NULL; paf = paf->next) {
        if (paf->location == APPLY_DAMROLL)
            damroll += paf->modifier;
        if (paf->location == APPLY_HITROLL)
            hitroll += paf->modifier;
    }

    dest_room = ch->in_room;
    chance += (hitroll + str_app[get_curr_stat(ch,STAT_STR)].tohit
           + (get_curr_stat(ch,STAT_DEX) - 18)) * 2;
    damroll *= 10;
    while (1) {
        chance -= 10;
        if (victim->in_room == dest_room) {
            if (number_percent() < chance) { 
                if (check_obj_dodge(ch, victim, arrow, chance))
                    return 0;
                act("$p strikes you!",
                    victim, arrow, NULL, TO_CHAR);
                act_puts("Your $p strikes $N!",
                     ch, arrow, victim, TO_CHAR, POS_DEAD);
                if (ch->in_room == victim->in_room)
                    act("$n's $p strikes $N!",
                        ch, arrow, victim, TO_NOTVICT);
                else {
                    act("$n's $p strikes $N!",
                        ch, arrow, victim, TO_ROOM);
                    act("$p strikes $n!",
                        victim, arrow, NULL, TO_ROOM);
                }
                if (is_safe(ch, victim)
                ||  (IS_NPC(victim) &&
                     IS_SET(victim->pIndexData->act, ACT_NOTRACK))) {
                    act("$p falls from $n doing no visible damage...",
                        victim, arrow, NULL, TO_ALL);
                    act("$p falls from $n doing no visible damage...",
                        ch, arrow, NULL, TO_CHAR);
                    obj_to_room(arrow, victim->in_room);
                }
                else {
                    int dam;

                    dam = dice(arrow->value[1],
                           arrow->value[2]);
                    dam = number_range(dam, 2 * dam);
                    dam += damroll + bonus + (10 * str_app[get_curr_stat(ch, STAT_STR)].todam);
                    if (IS_WEAPON_STAT(arrow,
                               WEAPON_POISON)) {
                        int level;
                        AFFECT_DATA *poison, af;

                 if ((poison = affect_find(arrow->affected,gsn_poison)) == NULL)
                    level = arrow->level;
                 else
                    level = poison->level;
                 if (!saves_spell(level,victim,DAM_POISON))
                 {
                    char_puts("You feel poison coursing through your veins.",
                      victim);
                    act("$n is poisoned by the venom on $p.",
                victim,arrow,NULL,TO_ROOM);

                    af.where     = TO_AFFECTS;
                    af.type      = gsn_poison;
                    af.level     = level * 3/4;
                    af.duration  = level / 2;
                    af.location  = APPLY_STR;
                    af.modifier  = -1;
                    af.bitvector = AFF_POISON;
                    affect_join(victim, &af);
                 }

            }
            if (IS_WEAPON_STAT(arrow,WEAPON_FLAMING))
            {
                 act("$n is burned by $p.",victim,arrow,NULL,TO_ROOM);
                 act("$p sears your flesh.",victim,arrow,NULL,TO_CHAR);
                 fire_effect((void *) victim,arrow->level,dam,TARGET_CHAR);
                }
            if (IS_WEAPON_STAT(arrow,WEAPON_FROST))
                {
                    act("$p freezes $n.",victim,arrow,NULL,TO_ROOM);
                    act("The cold touch of $p surrounds you with ice.",
                        victim,arrow,NULL,TO_CHAR);
                    cold_effect(victim,arrow->level,dam,TARGET_CHAR);
                }
                if (IS_WEAPON_STAT(arrow,WEAPON_SHOCKING))
                {
                    act("$n is struck by lightning from $p.",victim,arrow,NULL,TO_ROOM);
                    act("You are shocked by $p.",victim,arrow,NULL,TO_CHAR);
                    shock_effect(victim,arrow->level,dam,TARGET_CHAR);
                }

            if (dam > victim->max_hit / 10 
                && number_percent() < 50)
            {
              af.where     = TO_AFFECTS;
              af.type      = sn;
              af.level     = ch->level; 
              af.duration  = -1;
              af.location  = APPLY_HITROLL;
              af.modifier  = - (dam / 20);
              if (IS_NPC(victim)) af.bitvector = 0;
                else af.bitvector = AFF_CORRUPTION;

              affect_join(victim, &af);

              obj_to_char(arrow,victim);
              equip_char(victim,arrow,WEAR_STUCK_IN);
            }
                else obj_to_room(arrow,victim->in_room); 

            damage(ch, victim, dam, sn, DAM_PIERCE, DAMF_SHOW);
            path_to_track(ch,victim,door);

            }
            return TRUE;
          }
          else {
              obj_to_room(arrow,victim->in_room);
                  act("$p sticks in the ground at your feet!",victim,arrow,NULL, TO_ALL);
                  return FALSE;
                }
         }
        pExit = dest_room->exit[ door ];
         if (!pExit) break;
        else {
            dest_room = pExit->to_room.r;
            if (dest_room->people) {
                act("$p sails into the room from the $T!",
                    dest_room->people, arrow,
                    dir_name[rev_dir[door]],
                    TO_ALL | ACT_TRANS);
            }

        }
    }
    return FALSE;
}

static OBJ_DATA *find_arrow(CHAR_DATA *ch)
{
    OBJ_DATA *arrow;
    OBJ_DATA *obj;

    if ((arrow = get_eq_char(ch, WEAR_HOLD)))
        return arrow;

    for (obj = ch->carrying; obj; obj = obj->next_content) {
        if (obj->wear_loc == WEAR_NONE
        ||  obj->pIndexData->item_type != ITEM_CONTAINER
        ||  !IS_SET(obj->value[1], CONT_QUIVER)
        ||  !obj->contains)
            continue;
        return obj->contains;
    }

    return NULL;
}

DO_FUN(do_charge) 
{
    CHAR_DATA* victim;
    OBJ_DATA* wield;
    int chance, direction;
    EXIT_DATA *pexit;
    ROOM_INDEX_DATA *to_room;

    char arg1[512], arg2[512];


    if (IS_NPC(ch) || !(chance = get_skill(ch, gsn_charge))) {
        char_puts("Huh?\n", ch);
        return;
    }

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

    if (arg1 == '\0' || arg2 == '\0') {
        char_puts("Charge whom?\n", ch);
        return;
    }

    if ((wield = get_eq_char(ch, WEAR_WIELD)) == NULL) {
        char_puts("You need a weapon to charge.\n", ch);
        return;
    }

    if (wield->value[0] != WEAPON_LANCE && wield->value[0] != WEAPON_SPEAR) {
        char_puts("You need lance or spear to charge.\n", ch);
        return;
    }

    if ((direction = find_exit(ch, arg1)) <0 || direction >= MAX_DIR) {
        char_puts("Charge whom?\n", ch);
        return;
    }

    if ((victim = find_char(ch, arg2, direction, 1)) == NULL) { 
        WAIT_STATE(ch, MISSING_TARGET_DELAY);
        return;
    }

    if (ch->in_room == victim->in_room) {
        act("$N is here. Just MURDER $M.", ch, NULL, victim, TO_CHAR);
        return;
    }

    if (ch->mount == NULL) {
        char_puts("You have to be riding.\n", ch);
        return;
    }

    if (is_safe(ch, victim))
        return;

    if (victim->hit < victim->max_hit*9/10) {
        act("$N is already bleeding, your honour do not allow you attack $M.", ch, NULL, victim, TO_CHAR);
        return;
    }

    chance = chance * get_skill(ch, gsn_riding)/100;

    if (!move_char_org(ch, direction, FALSE, TRUE))
        return;
    act("$n gallops from $t, charging you!",
        ch, dir_name[rev_dir[direction]], victim, TO_VICT);
    act("$n gallops from $t, charging $N!",
        ch, dir_name[rev_dir[direction]], victim, TO_NOTVICT);


    if (number_percent() < chance) {
        one_hit(ch, victim, gsn_charge, WEAR_WIELD);
        WAIT_STATE(victim, SKILL(gsn_charge)->beats * 2);
        WAIT_STATE(ch, SKILL(gsn_charge)->beats);
        check_improve(ch, gsn_charge, TRUE, 1);
    }
    else {
        damage(ch, victim, 0, gsn_charge, DAM_NONE, TRUE);
        check_improve(ch, gsn_charge, FALSE, 1);
        if (number_percent() > get_skill(ch, gsn_riding)) {
            if ((pexit=ch->in_room->exit[direction]) == NULL
            || (to_room = pexit->to_room.r) == NULL
            || !can_see_room(ch, to_room)
            || IS_ROOM_AFFECTED(ch->in_room, RAFF_RANDOMIZER)
            || IS_SET(pexit->exit_info, EX_CLOSED)) {
                WAIT_STATE(ch, SKILL(gsn_charge)->beats*2);
                return;
            }
            else {
                act("$n cannot hold $s $N.\n",
                    ch, dir_name[direction], ch->mount, TO_NOTVICT);
                act("You cannot hold your $N.",
                    ch, NULL, ch->mount, TO_CHAR);
                move_char(ch, direction, FALSE);
                WAIT_STATE(ch, SKILL(gsn_charge)->beats*5);
                return;
            }
        }
        WAIT_STATE(ch, SKILL(gsn_charge)->beats*2);
    }
}

DO_FUN(do_shoot)
{
    CHAR_DATA *victim;
    OBJ_DATA *wield;
    OBJ_DATA *arrow; 
    char arg1[512],arg2[512];
    bool success;
    int chance, direction;
    int range = (ch->level / 10) + 1;
    
    if (IS_NPC(ch))
        return; /* Mobs can't use bows */

    if (IS_NPC(ch) || (chance = get_skill(ch, gsn_bow)) == 0) {
        char_puts("You don't know how to shoot.\n",ch);
        return;
    }

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

    if (arg1[0] == '\0' || arg2[0] == '\0') {
        char_puts("Shoot what direction and whom?\n", ch);
        return;
    }

    if (ch->fighting) {
        CHAR_DATA *vch;

        for (vch = ch->in_room->people; vch; vch = vch->next_in_room)
            if (vch->fighting == ch)
                break;
        if (vch) {
            char_puts("You cannot concentrate "
                  "on shooting arrows.\n", ch);
            return;
        }
    }

    direction = find_exit(ch, arg1);

    if (direction < 0 || direction >= MAX_DIR) {
        char_puts("Shoot which direction and whom?\n",ch);
        return;
    }
        
    if ((victim = find_char(ch, arg2, direction, range)) == NULL) {
        WAIT_STATE(ch, MISSING_TARGET_DELAY);
        char_puts("They aren't there.\n", ch);
        return;
    }

    if (!IS_NPC(victim) && victim->desc == NULL) {
        char_puts("You can't do that.\n", ch);
        return;
    }

    if (victim == ch) {
        char_puts("That's pointless.\n", ch);
        return;
    }

    wield = get_eq_char(ch, WEAR_WIELD);

    if (!wield || wield->pIndexData->item_type != ITEM_WEAPON
    ||  wield->value[0] != WEAPON_BOW) {
        char_puts("You need a bow to shoot!\n", ch);
        return;     
    }

    if (get_eq_char(ch, WEAR_SECOND_WIELD)
    ||  get_eq_char(ch, WEAR_SHIELD)) {
        char_puts("Your second hand should be free!\n",ch);
        return;     
    }

    if ((arrow = find_arrow(ch)) == NULL) {
         char_puts("You need an arrow to shoot!\n", ch);
         return;        
    }
        
    if (arrow->pIndexData->item_type != ITEM_WEAPON
    ||  arrow->value[0] != WEAPON_ARROW) {
        char_puts("That's not the right kind of arrow!\n", ch);
        return;
    }
        
    if (is_safe(ch, victim))
        return;

    WAIT_STATE(ch, SKILL(gsn_bow)->beats);

    chance = (chance - 50) * 2;
    if (ch->position == POS_SLEEPING)
        chance += 40;
    if (ch->position == POS_RESTING)
        chance += 10;
    if (victim->position == POS_FIGHTING)
        chance -= 40;
    chance += GET_HITROLL(ch);

    act_puts("You shoot $p to $T.",
         ch, arrow, dir_name[direction], TO_CHAR | ACT_TRANS, POS_DEAD);
    act("$n shoots $p to $T.",
        ch, arrow, dir_name[direction], TO_ROOM | ACT_TRANS);

    if (arrow->carried_by)
        obj_from_char(arrow);
    else if (arrow->in_obj)
        obj_from_obj(arrow);

    success = send_arrow(ch, victim, arrow, direction, chance,
                 dice(wield->value[1],wield->value[2]));
    check_improve(ch, gsn_bow, TRUE, 1);
}

char *find_way(CHAR_DATA *ch,ROOM_INDEX_DATA *rstart, ROOM_INDEX_DATA *rend) 
{
    int direction;
    static char buf[1024];
    EXIT_DATA *pExit;
    char buf2[2];

    snprintf(buf, sizeof(buf), "Bul: ");
    while (1) {
        if ((rend == rstart))
            return buf;

        if ((direction = find_path(rstart->vnum, rend->vnum,
                       ch, -40000, 0)) == -1) {
            strnzcat(buf, sizeof(buf), " BUGGY");
            return buf;
        }

        if (direction < 0 || direction > 5) {
            strnzcat(buf, sizeof(buf), " VERY BUGGY");
            return buf;
        }

        buf2[0] = dir_name[direction][0];
        buf2[1] = '\0';
        strnzcat(buf, sizeof(buf), buf2);

        /* find target room */
        pExit = rstart->exit[ direction ];
        if (!pExit)  {
            strnzcat(buf, sizeof(buf), " VERY VERY BUGGY");
            return buf;
        }
        else
            rstart = pExit->to_room.r;
    }
}   

void do_human(CHAR_DATA *ch, const char *argument)
{
    if (!is_affected(ch, gsn_vampire)) {
        char_puts("You are already a human.\n", ch);
        return;
    }

    affect_strip(ch, gsn_vampire);
    char_puts("You return to your original size.\n", ch);
}

void do_throw_spear(CHAR_DATA *ch, const char *argument)
{
    CHAR_DATA *victim;
    OBJ_DATA *spear;
    char arg1[512],arg2[512];
    bool success;
    int chance,direction;
    int range = (ch->level / 10) + 1;

    if (IS_NPC(ch) || (chance = get_skill(ch, gsn_spear)) == 0) {
        char_puts("You don't know how to throw a spear.\n",ch);
        return;
    }

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

    if (arg1[0] == '\0' || arg2[0] == '\0') {
        char_puts("Throw spear what direction and whom?\n", ch);
        return;
    }

    if (ch->fighting) {
        CHAR_DATA *vch;

        for (vch = ch->in_room->people; vch; vch = vch->next_in_room)
            if (vch->fighting == ch)
                break;
        if (vch) {
            char_puts("You cannot concentrate on throwing "
                  "spears.\n", ch);
            return;
        }
    }

    direction = find_exit(ch, arg1);
    if (direction < 0 || direction >= MAX_DIR) {
        char_puts("Throw which direction and whom?\n",ch);
        return;
    }
        
    if ((victim = find_char(ch, arg2, direction, range)) == NULL) {
        WAIT_STATE(ch, MISSING_TARGET_DELAY);
        char_puts("They aren't there.\n", ch);
        return;
    }

    if (!IS_NPC(victim) && victim->desc == NULL) {
        char_puts("You can't do that.\n", ch);
        return;
    }

    if (victim == ch) {
        char_puts("That's pointless.\n", ch);
        return;
    }

    spear = get_eq_char(ch, WEAR_WIELD);
    if (!spear || spear->pIndexData->item_type != ITEM_WEAPON
    ||  spear->value[0] != WEAPON_SPEAR) {
        char_puts("You need a spear to throw!\n",ch);
        return;     
    }

    if (get_eq_char(ch,WEAR_SECOND_WIELD) || get_eq_char(ch,WEAR_SHIELD)) {
        char_puts("Your second hand should be free!\n",ch);
        return;     
    }

    if (is_safe(ch,victim))
        return;

    WAIT_STATE(ch, SKILL(gsn_spear)->beats);

    chance = (chance - 50) * 2;
    if (ch->position == POS_SLEEPING)
        chance += 40;
    if (ch->position == POS_RESTING)
        chance += 10;
    if (victim->position == POS_FIGHTING)
        chance -= 40;
    chance += GET_HITROLL(ch);

    act_puts("You throw $p to $T.", 
         ch, spear, dir_name[direction], TO_CHAR, POS_DEAD);
    act("$n throws $p to $T.",
        ch, spear, dir_name[direction], TO_ROOM | ACT_TRANS);

    obj_from_char(spear);
    success = send_arrow(ch,victim,spear,direction,chance,
            dice(spear->value[1],spear->value[2]));
    check_improve(ch, gsn_spear, TRUE, 1);
}

/* RT Enter portals */
void do_enter(CHAR_DATA *ch, const char *argument)
{    
    ROOM_INDEX_DATA *location; 
    ROOM_INDEX_DATA *old_room;
    OBJ_DATA *portal;
    CHAR_DATA *fch, *fch_next, *mount;

    if (ch->fighting != NULL) 
        return;

    /* nifty portal stuff */
    if (argument[0] == '\0') {
        char_puts("Nope, can't do it.\n",ch);
        return;
    }

    old_room = ch->in_room;
    portal = get_obj_list(ch, argument, ch->in_room->contents);
    
    if (portal == NULL) {
        char_puts("You don't see that here.\n",ch);
        return;
    }

    if (portal->pIndexData->item_type != ITEM_PORTAL 
    ||  (IS_SET(portal->value[1], EX_CLOSED) && !IS_TRUSTED(ch, ANGEL))) {
        char_puts("You can't seem to find a way in.\n", ch);
        return;
    }

    if (IS_SET(portal->value[2], GATE_NOCURSE)
    &&  !IS_TRUSTED(ch, ANGEL)
    &&  (IS_AFFECTED(ch, AFF_CURSE) ||
         IS_SET(old_room->room_flags, ROOM_NORECALL) ||
         IS_RAFFECTED(old_room, RAFF_CURSE))) {
        char_puts("Something prevents you from leaving...\n",ch);
        return;
    }

    if (IS_SET(portal->value[2], GATE_RANDOM) || portal->value[3] == -1) {
        location = get_random_room(ch, NULL);
        portal->value[3] = location->vnum; /* keeps record */
    }
    else if (IS_SET(portal->value[2], GATE_BUGGY) && (number_percent() < 5))
        location = get_random_room(ch, NULL);
    else
        location = get_room_index(portal->value[3]);

    if (location == NULL
    ||  location == old_room
    ||  !can_see_room(ch, location) 
    ||  (room_is_private(location) && !IS_TRUSTED(ch, IMPLEMENTOR))) {
        act("$p doesn't seem to go anywhere.", ch, portal,NULL,TO_CHAR);
        return;
    }

    if (IS_NPC(ch) && IS_SET(ch->pIndexData->act, ACT_AGGRESSIVE)
    &&  IS_SET(location->room_flags, ROOM_LAW)) {
            char_puts("Something prevents you from leaving...\n",ch);
            return;
    }

    act(MOUNTED(ch) ? "$n steps into $p, riding on $N." :
              "$n steps into $p.",
        ch, portal, MOUNTED(ch), TO_ROOM);
    
    act(IS_SET(portal->value[2], GATE_NORMAL_EXIT) ?
        "You enter $p." :
        "You walk through $p and find yourself somewhere else...",
        ch, portal, NULL, TO_CHAR); 

    mount = MOUNTED(ch);
    char_from_room(ch);

    if (IS_SET(portal->value[2], GATE_GOWITH)) {/* take the gate along */
        obj_from_room(portal);
        obj_to_room(portal, location);
    }

    if (IS_SET(portal->value[2], GATE_NORMAL_EXIT))
        act_puts3(mount ? "$i has arrived, riding $I" :
                  "$i has arrived.",
              location->people, ch, portal, mount,
              TO_ROOM, POS_RESTING);
    else
        act_puts3(mount ? "$i has arrived through $P, riding $I." :
                      "$i has arrived through $P.",
              location->people, ch, portal, mount,
              TO_ROOM, POS_RESTING);

    char_to_room(ch, location);

    if (mount) {
        char_from_room(mount);
        char_to_room(mount, location);
        ch->riding = TRUE;
        mount->riding = TRUE;
    }

    if (!JUST_KILLED(ch))
        do_look(ch,"auto");

    /* charges */
    if (portal->value[0] > 0) {
        portal->value[0]--;
        if (portal->value[0] == 0)
            portal->value[0] = -1;
    }

    /* protect against circular follows */
    if (old_room == location)
        return;

    for (fch = old_room->people; fch != NULL; fch = fch_next) {
            fch_next = fch->next_in_room;

        /* no following through dead portals */
            if (portal == NULL || portal->value[0] == -1) 
                continue;
 
            if (fch->master != ch || fch->position != POS_STANDING)
            continue;

            if (IS_SET(location->room_flags, ROOM_LAW)
            &&  IS_NPC(fch)
        &&  IS_SET(fch->pIndexData->act, ACT_AGGRESSIVE)) {
                act("You can't bring $N into the city.",
                        ch, NULL, fch, TO_CHAR);
            act("You aren't allowed in the city.",
                fch, NULL, NULL, TO_CHAR);
            continue;
            }
 
            act("You follow $N.", fch, NULL, ch, TO_CHAR);
        do_enter(fch,argument);
    }

    if (portal != NULL && portal->value[0] == -1) {
        act("$p fades out of existence.", ch, portal, NULL, TO_CHAR);
        if (ch->in_room == old_room)
            act("$p fades out of existence.",
                ch, portal, NULL, TO_ROOM);
        else if (old_room->people != NULL) {
            act("$p fades out of existence.", 
                old_room->people, portal, NULL, TO_CHAR);
            act("$p fades out of existence.",
                old_room->people,portal,NULL,TO_ROOM);
        }
        extract_obj(portal, 0);
    }

    if (JUST_KILLED(ch))
        return;

    /* 
     * If someone is following the char, these triggers get
     * activated for the followers before the char,
     * but it's safer this way...
     */
    if (IS_NPC(ch) && HAS_TRIGGER(ch, TRIG_ENTRY))
        mp_percent_trigger(ch, NULL, NULL, NULL, TRIG_ENTRY);
    if (!IS_NPC(ch))
        mp_greet_trigger(ch);
}

void do_settraps(CHAR_DATA *ch, const char *argument)
{
    int chance;

    if ((chance = get_skill(ch, gsn_settraps)) == 0) {
        char_puts("You don't know how to set traps.\n",ch);
        return;
    }

    if (!ch->in_room)
        return;

    if (IS_SET(ch->in_room->room_flags, ROOM_LAW)) {
        char_puts("A mystical power protects the room.\n",ch);
        return;
    }

    WAIT_STATE(ch, SKILL(gsn_settraps)->beats);

    if (IS_NPC(ch) || number_percent() <  chance * 7 / 10) {
      AFFECT_DATA af,af2;

      check_improve(ch,gsn_settraps,TRUE,1);

      if (is_affected_room(ch->in_room, gsn_settraps))
      {
    char_puts("This room has already trapped.\n",ch);
    return;
       }

      if (is_affected(ch,gsn_settraps))
      {
    char_puts("This skill is used too recently.\n",ch);
    return;
      }
   
      af.where     = TO_ROOM_AFFECTS;
      af.type      = gsn_settraps;
      af.level     = ch->level;
      af.duration  = ch->level / 40;
      af.location  = APPLY_NONE;
      af.modifier  = 0;
      af.bitvector = RAFF_THIEF_TRAP;
      affect_to_room(ch->in_room, &af);

      af2.where     = TO_AFFECTS;
      af2.type      = gsn_settraps;
      af2.level     = ch->level;
    
      if (!IS_IMMORTAL(ch) && IS_PUMPED(ch))
         af2.duration  = 1;
      else af2.duration = ch->level / 10;

      af2.modifier  = 0;
      af2.location  = APPLY_NONE;
      af2.bitvector = 0;
      affect_to_char(ch, &af2);
      char_puts("You set the room with your trap.\n", ch);
      act("$n set the room with $s trap.",ch,NULL,NULL,TO_ROOM);
      return;
    }
    else check_improve(ch,gsn_settraps,FALSE,1);

   return;
}

void do_thumbling(CHAR_DATA *ch, const char *argument)
{
    char arg[MAX_STRING_LENGTH];
    int chance;
    bool attack;
    AFFECT_DATA af;

    if (IS_NPC(ch) || (chance = get_skill(ch, gsn_thumbling)) == 0) {
        char_puts("You don't know how to do that.\n", ch);
        return;
    }

    one_argument(argument, arg, sizeof(arg));

    if (arg[0] == '\0') {
        if (is_affected(ch, gsn_thumbling)) {
            affect_strip(ch, gsn_thumbling);
            char_puts("Ok.\n", ch);
        }
        return;
    }

    if (!str_prefix(arg, "attack"))
        attack = TRUE;
    else if (!str_prefix(arg, "defense"))
        attack = FALSE;
    else {
        char_puts("Aglebargle, glip-glop?\n", ch);
        return;
    }

    WAIT_STATE(ch, SKILL(gsn_thumbling)->beats);

    if (is_affected(ch, gsn_thumbling)) {
        char_puts("You do the best you can.\n", ch);
        return;
    }

    if (IS_AFFECTED(ch, AFF_FLYING)) {
        char_puts("Touch the ground first.\n", ch);
        return;
    }

    if (number_percent() > chance) {
        act("You failed to reach the true source of tennis ball power.", ch, NULL, NULL, TO_CHAR);
        act("$n falls to the ground flat on $s face.", ch, NULL, NULL, TO_ROOM);
        check_improve(ch, gsn_thumbling, FALSE, 3);
        return;
    }

    af.where    = TO_AFFECTS;
    af.type     = gsn_thumbling;
    af.level    = ch->level;
    af.duration = -1;
    af.bitvector    = 0;

    if (attack) {
        af.modifier = ch->level / 3;
        af.location = APPLY_HITROLL;
        affect_to_char(ch, &af);
        af.location = APPLY_DAMROLL;
    }
    else {
        af.modifier = - ch->level * 2;
        af.location = APPLY_AC;
    }
    affect_to_char(ch, &af);

    act("You start to jump like a tennis ball!", ch, NULL, NULL, TO_CHAR);
    act("$n starts to jump like a tennis ball!", ch, NULL, NULL, TO_ROOM);

    check_improve(ch, gsn_thumbling, TRUE, 3);
}

void do_forest(CHAR_DATA* ch, const char* argument)
{
    char arg[MAX_STRING_LENGTH];
    AFFECT_DATA af;
    bool attack;

    if (IS_NPC(ch) || !get_skill(ch, gsn_forest_fighting)) {
        char_puts("Huh?\n", ch);
        return;
    }
    
    one_argument(argument, arg, sizeof(arg));
    if (arg == '\0') {
        char_puts("Usage: forest {{ attack|defence|normal}", ch);
        return;
    }

    if (!str_prefix(arg, "normal")) {
        if (!is_affected(ch, gsn_forest_fighting)) {
            char_puts("You do not use your knowledge of forest "
                  "in fight.\n", ch);
            return;
        }
        else {
            char_puts("You stop using your knowledge of forest in "
                  "fight.\n", ch);
            affect_strip(ch, gsn_forest_fighting);
            return;
        }
    }

    if (!str_prefix(arg, "defence"))
        attack = FALSE;
    else if (!str_prefix(arg, "attack"))
        attack = TRUE;
    else {
        do_forest(ch, str_empty);
        return;
    }

    if (is_affected(ch, gsn_forest_fighting))
        affect_strip(ch, gsn_forest_fighting);
    
    af.where    = TO_AFFECTS;
    af.type     = gsn_forest_fighting;
    af.level    = ch->level;
    af.duration = -1;
    af.bitvector    = 0;

    if (attack) {
        af.modifier = ch->level/8;
        af.location = APPLY_HITROLL;
        affect_to_char(ch, &af);
        af.location = APPLY_DAMROLL;
        act_puts("You feel yourself wild.",
             ch, NULL, NULL, TO_CHAR, POS_DEAD);
        act("$n looks wild.", ch, NULL, NULL, TO_ROOM);
    }
    else {
        af.modifier = -ch->level;
        af.location = APPLY_AC;
        act_puts("You feel yourself protected.",
             ch, NULL, NULL, TO_CHAR, POS_DEAD);
        act("$n looks protected.", ch, NULL, NULL, TO_ROOM);
    }

    affect_to_char(ch, &af);
}

