/
roa/
roa/lib/boards/
roa/lib/config/
roa/lib/edits/
roa/lib/help/
roa/lib/misc/
roa/lib/plrobjs/
roa/lib/quests/
roa/lib/socials/
roa/lib/www/
roa/lib/www/LEDSign/
roa/lib/www/LEDSign/fonts/
roa/lib/www/LEDSign/scripts/
roa/src/s_inc/
roa/src/sclient/
roa/src/sclient/binary/
roa/src/sclient/text/
roa/src/util/
/************************************************************************
	Realms of Aurealis 		James Rhone aka Vall of RoA

move.c					Code relating to character move-
					ment.

		******** Heavily modified and expanded ********
		*** BE AWARE OF ALL RIGHTS AND RESERVATIONS ***
		******** Heavily modified and expanded ********
		        All rights reserved henceforth. 

    Please note that no guarantees are associated with any code from
Realms of Aurealis.  All code which has been released to the general
public has been done so with an 'as is' pretense.  RoA is based on both
Diku and CircleMUD and ALL licenses from both *MUST* be adhered to as well
as the RoA license.   *** Read, Learn, Understand, Improve ***
*************************************************************************/
#include "conf.h"
#include "sysdep.h"

#include "structures.h"
#include "utils.h"
#include "comm.h"
#include "interpreter.h"
#include "acmd.h"
#include "handler.h"
#include "db.h"
#include "magic.h"
#include "mudlimits.h"
#include "screen.h"
#include "house.h"
#include "objsave.h"
#include "fight.h"
#include "lists.h"
#include "darkenelf.h"
#include "thief.h"
#include "affect.h"

/* external vars  */
extern struct arena_data arena_info;
extern int	rev_dir[];
extern char	*rev_dir_str[];
extern char	*dirs[];
extern char	*comm_dirs[];

/* external functs */
chdata 	*get_mount_in_room(chdata *ch);
int 	check_room_affects(chdata *ch, int room);
BOOL 	room_align_probs(chdata *ch, int bitv);
int	special(chdata *ch, int cmd, char *arg);
void	death_cry(chdata *ch);
void    raw_kill(chdata *ch);

/* return TRUE if char cant enter room because of race or class RoA jtr*/
/* added a couple mob checks and arena checks too */
BOOL room_restricts(chdata *ch, int room)
{
  int tozone = world[room].zone;
  chdata *orig = NULL;

  if (ch->desc)
    orig = TRUE_CHAR(ch);
  else
    orig = ch;
 
  /* check for a house */
  if (!House_can_enter(orig, world[room].number)) 
  {
    send_to_char("That's private property -- no trespassing!\r\n", ch);
    return TRUE;
  }

  if (ROOM_FLAGGED(room, NO_MOB) && IS_NPC(ch) && 
      (!orig || !IS_IMMORTAL(orig)) && !SPC_FLAGGED(ch, SPC_SHOPKEEP))
  {
     send_to_char("An unknown force prevents you from doing that.\n\r",ch);
     return TRUE;
  }

  if (MOB_FLAGGED(ch, MOB_STAY_ZONE) && 
      world[room].zone != world[ch->in_room].zone)
  {
    send_to_char("You must stay in yer zone boy... \n\r",ch);
    return TRUE;
  } 

  if (IN_ARENA(ch) && !ZONE_FLAGGED(tozone, Z_ARENA))
  {
    send_to_char("You may not leave the designated Arena.\n\r",ch);
    return TRUE;
  }

  if (IS_PC(ch) && !IS_IMMORTAL(ch) && !IN_ARENA(ch) && ZONE_FLAGGED(tozone, Z_ARENA))
  {
    send_to_char("That area is flagged Arena.  If you wish to participate,\n\r",ch);
    send_to_char("talk to a GOD+ and they will teleport you there.\n\r",ch);
    return TRUE;
  }

  if (ZONE_FLAGGED(tozone, Z_LOCKED) || ZONE_FLAGGED(tozone, Z_CLOSED))
    if (!IS_IMMORTAL(ch) || IS_NPC(ch)) 
     {
       send_to_char("Sorry, this area is temporarily down for construction.\n\r",ch);   
       return TRUE;
     }
    else  
     send_to_char("%B%1WARNING%0: Zone is under construction.\n\r",ch);
 
  if (IS_IMMORTAL(ch))
    return FALSE;

  if (IN_ARENA(ch) && !arena_info.locked)
  {
    send_to_char("You must wait until the Arena begins.\n\r",ch);
    return TRUE;
  }

  if ((IS_NAT_MAGE(ch) && ROOM_FLAGGED(room, NO_MAGE)) ||
     (IS_NAT_RANGER(ch) && ROOM_FLAGGED(room, NO_RANGER)) ||
     (IS_NAT_BARD(ch) && ROOM_FLAGGED(room, NO_BARD)) ||
     (IS_NAT_CLERIC(ch) && ROOM_FLAGGED(room, NO_CLERIC)) ||
     (IS_NAT_SHAMAN(ch) && ROOM_FLAGGED(room, NO_SHAMAN)) ||
     (IS_NAT_WARRIOR(ch) && ROOM_FLAGGED(room, NO_WARRIOR)) || 
     (IS_NAT_WARLOCK(ch) && ROOM_FLAGGED2(room, NO_WARLOCK)) || 
     (IS_NAT_MONK(ch) && ROOM_FLAGGED2(room, NO_MONK)) || 
     (IS_NAT_MADEPT(ch) && ROOM_FLAGGED2(room, NO_MADEPT)) || 
     (IS_NAT_DRUID(ch) && ROOM_FLAGGED2(room, NO_DRUID)) || 
     (IS_NAT_THIEF(ch) && ROOM_FLAGGED(room, NO_THIEF)))
  {
    send_to_char("Forces beyond your comprehension prevent your class from going there.\n\r",ch);
    return TRUE;
  }

  if ((IS_HUMAN(ch) && ROOM_FLAGGED2(room, NO_HUMAN)) ||
     (IS_ELF(ch) && ROOM_FLAGGED2(room, NO_ELF)) ||
     (IS_HALF_ELF(ch) && ROOM_FLAGGED2(room, NO_HALF_ELF)) ||
     (IS_ORC(ch) && ROOM_FLAGGED2(room, NO_ORC)) ||
     (IS_OGRE(ch) && ROOM_FLAGGED2(room, NO_OGRE)) ||
     (IS_DROW(ch) && ROOM_FLAGGED2(room, NO_DROW)) ||
     (IS_DWARF(ch) && ROOM_FLAGGED2(room, NO_DWARF)) ||
     (IS_PIXIE(ch) && ROOM_FLAGGED2(room, NO_PIXIE)) ||
     (IS_NIXIE(ch) && ROOM_FLAGGED2(room, NO_NIXIE)) ||
     (IS_DRAGON(ch) && ROOM_FLAGGED2(room, NO_DRAGON)))
  {
    send_to_char("Forces beyond your comprehension prevent your race from going there.\n\r",ch);
    return TRUE;
  }

  return FALSE;
}

/* returns size of player based on race RoA */
int get_size(chdata *ch)
{
   int size = 0;

   if (IS_IMMORTAL(ch))
     size = 0;
   else
   if (IS_NPC(ch))
     size = 1;
   else
   switch (GET_RACE(ch)) {
   case RACE_DRAGON:
     size = 4;
     break;   
   case RACE_OGRE:
   case RACE_ORC:
     size = 3;
     break;
   case RACE_NIXIE: 
   case RACE_PIXIE:
     size = 1;
     break;
   default:
     size = 2;   
     break;
   }
   return size;
}

// dts now use dice roll for damage, if dice roll is <= 0, total death
BOOL check_death_trap(chdata *ch, chdata *mount)
{
   int room = ch->in_room;
   int dam, ret = CHAR_OK;
   rmdata *r;

   if (IS_IMMORTAL(ch))
     return FALSE;

   r = &world[room];

   if (!PC_MOUNTING(ch) || (mount && PC_MOUNTING(ch) && !IS_FLYING(mount)))
   if (ROOM_FLAGGED(room, DEATH) && !IS_FLYING(ch)) 
   {
    // if no dice, total death (by default this is true)
    if (r->numdice <= 0 || r->sizedice <= 0)
    {
      log_death_trap(ch);
      death_cry(ch);
      write_reimb_file(ch);
      delete_object_file(ch);
      if (PC_MOUNTING(ch))
        raw_kill(mount);
      SET_BIT(CHAR_FLAGS(ch), CH_HIT_DT);
      extract_char(ch);
      return(TRUE);
    }
    else
    {
      dam = dice(r->numdice, r->sizedice);
      if (damage(ch, ch, dam, TYPE_UNDEFINED, TRUE) == CHAR_DIED)
	return (TRUE);
    }
   }

   if (!PC_MOUNTING(ch) || (mount && PC_MOUNTING(ch) && IS_FLYING(mount)))
   if (ROOM_FLAGGED(room, FLY_DEATH) && IS_FLYING(ch)) 
   {
    // if no dice, total death (by default this is true)
    if (r->numdice <= 0 || r->sizedice <= 0)
    {
      log_death_trap(ch);
      death_cry(ch);
      write_reimb_file(ch);
      delete_object_file(ch);
      if (PC_MOUNTING(ch))
        raw_kill(mount);
      SET_BIT(CHAR_FLAGS(ch), CH_HIT_DT);
      extract_char(ch);
      return(TRUE);
    }
    else
    {
      dam = dice(r->numdice, r->sizedice);
      if (damage(ch, ch, dam, TYPE_UNDEFINED, TRUE) == CHAR_DIED)
	return (TRUE);
    }
   }

   // if there is a snare here, trigger it!
   if (TRAPS(r))
   {
     ret = triptrap(TRAPS(r), ch, FALSE);
     TRAPS(r) = trap_from_room(TRAPS(r), r);
   }

   if (ret == CHAR_DIED)
     return TRUE;

   return FALSE;
}

// Added checks and modifiers for spell 'rock to mud' 07/09/98 -callahan
int get_move_loss(chdata *ch, int toroom)
{
  int mloss, zone;
  extern int	movement_loss[];
  struct room_affect_type *raf;
  rmdata *rm = NULL;

  rm = &world[toroom];

  // get the natural walking movement loss
  mloss = (movement_loss[(int) world[ch->in_room].terrain_type] + 
   	   movement_loss[(int) world[toroom].terrain_type]) / 2;

  // in excessive accumulations... up the movement cost...
  zone = world[ch->in_room].zone;

  if (ZONE_ACCUM(zone) >= 24)
  {
    mloss = (int) (mloss * 2);
  }
  else
  if (ZONE_ACCUM(zone) >= 18)
  {
    mloss = (int) (mloss * 1.75);
  }
  else
  if (ZONE_ACCUM(zone) >= 12)
  {
    mloss = (int) (mloss * 1.5);
  }
  else
  if (ZONE_ACCUM(zone) >= 6)
  {
    mloss = (int) (mloss * 1.25);
  }

  // Check for movement loss due to 'rock to mud' in next room. Also
  // added check for damage from nature's caltrops. 07/10/98 -callahan
  for (raf = rm->room_affects; raf; raf = raf->next)
    if (raf->spell == SPELL_ROCK_TO_MUD)  {
      if (affected_by_spell(ch, SPELL_WINGS)) {
        act("You fly over the mud filling the adjoining area.",
            FALSE, ch, 0, 0, TO_CHAR);

        act("$n flies over the mud filling the adjoining area.",
            FALSE, ch, 0, 0, TO_ROOM);
      } else {
        act("Incredible effort is expelled in moving through the area...",
            FALSE, ch, 0, 0, TO_CHAR);

        act("$n slows immensely as $e becomes quagmired in mud...",
            FALSE, ch, 0, 0, TO_ROOM);

        if (raf->caster) 
          Moves(ch) -= Level(raf->caster);
        else
          Moves(ch) -= LEV_IMPL;
      }
    } else if (raf->spell == SPELL_NATURES_CALTROPS)  {
      if (affected_by_spell(ch, SPELL_WINGS)) {
        act("You fly over the deadly spikes in the adjoining area.",
            FALSE, ch, 0, 0, TO_CHAR);

        act("$n flies over the deadly spikes in the adjoining area.",
            FALSE, ch, 0, 0, TO_ROOM);
      } else {
        act("You impale your feet nastily upon the spikes which have grown "
            "from the ground!", FALSE, ch, 0, 0, TO_CHAR);

        act("$n impales $s feet nastily upon the spikes which have grown "
            "from the ground!", FALSE, ch, 0, 0, TO_ROOM);
    
          if (raf->caster)   
            Hits(ch) -= (Level(raf->caster) / 5) + number(1, 10);
          else  
            Hits(ch) -= (LEV_IMPL / 5) + number(1, 10);
      }
    }


  return mloss;	// for the moment
}

int char_carrying_type(chdata *ch, int type)
{
  obdata *obj;

  for (obj = ch->carrying; obj; obj = obj->next_content)
    if (obj->type_flag == type)
      return TRUE;
  return FALSE;
}

// return true if thie obj VNUM is in the list...
int obj_vnum_in_list(int vnum, obdata *list)
{
  obdata *obj;

  for (obj = list; obj; obj = obj->next_content)
    if (GET_OBJ_VNUM(obj) == vnum)
      return TRUE;
  return FALSE;
}

/*   Returns :  1: If success.   0: If fail   -1: If dead. */
int	do_simple_move(chdata *ch, int cmd, int following)
{
   extern char *delete_doubledollar(char *string);
   extern void perform_reaction(chdata *mob, char *str, chdata *ch, obdata *ob);
   extern int special(chdata *ch, int cmd, char *arg);
   extern BOOL wandering_shopkeep(chdata *ch);
   extern void send_soundfile_to_client(dsdata *d, char *sname);
   BOOL has_boat, in_flight;
   chdata *mount, *mob;
   int was_in, mv_needed, size, mountsize, tozone, toroom;
   struct room_affect_type *raf;
   rmdirdata *d;

   if (special(ch, cmd + 1, ""))  /* Check for special routines (North is 1) */
      return(FALSE);

   if (IS_NPC(ch) && IS_MOUNTED(ch)) 
      return FALSE;  /* mount cant move by itself*/
   
   if (IS_HELD(ch))
   {
      send_to_char("You can't move!!  You are held!\n\r",ch);
      return FALSE;
   }

   // must make save to be able to move
   if (spell_affects_room(ch->in_room, SPELL_VOID))
   {
     if (IS_IMMORTAL(ch) || saves_spell(ch, SPELL_VOID) || 
	 char_spell_affects_room(ch, SPELL_VOID, ch->in_room))
       send_to_char("You break free of the %4void%0!\n\r",ch);
     else
     {
	send_to_char("A %4temporal void%0 prevents you from leaving!\n\r",ch);
	return (FALSE);
     }
   }

   if (IS_NPC(ch) && GET_POS(ch) < POS_FIGHTING)
   {
      send_to_char("Get Up !!!!! \n\r",ch);
      do_stand(ch, "", 0, 0);
      return FALSE;
   }

   if (INVALID_ROOM(ch->in_room))
   {
     send_to_char("You are in NOWHERE.  Seek immortal help.\n\r",ch);
     return FALSE;
   }

   d = EXIT(ch, cmd);
   toroom = d->to_room;
   tozone = 0;
   if (!INVALID_ROOM(toroom))
     tozone = world[toroom].zone;

   /* check class / race / mob / house / arena restrictive rooms RoA */
   if (!tozone || !REAL_ZONE(tozone) || room_restricts(ch, toroom))
     return FALSE;

   // must make save to be able to move
   if (spell_affects_room(toroom, SPELL_ABSOLUTE_WARD))
   {
     if (IS_IMMORTAL(ch) || saves_spell(ch, SPELL_ABSOLUTE_WARD) || 
	 char_spell_affects_room(ch, SPELL_ABSOLUTE_WARD, toroom))
       send_to_char("You resist the %4ward%0!\n\r",ch);
     else
     {
	send_to_char("A %4ward%0 of some sort prevents you from going there!\n\r",ch);
	return (FALSE);
     }
   }

   // must make save to be able to move
   if ((raf = spell_affects_room(toroom, SPELL_CIRCLE_OF_WARDING)))
   {
     if (IS_IMMORTAL(ch) || saves_spell(ch, SPELL_CIRCLE_OF_WARDING) || 
	 char_spell_affects_room(ch, SPELL_CIRCLE_OF_WARDING, toroom) || 
         !room_align_probs(ch, raf->bitvector))
       send_to_char("You resist the %4ward%0!\n\r",ch);
     else
     {
	send_to_char("A %4ward%0 of some sort prevents you from going there!\n\r",ch);
	return (FALSE);
     }
   }

   if (IS_PC(ch) && PC_MOUNTING(ch))
     mount = get_mount_in_room(ch);
   else 
     mount = NULL;

   if (PC_MOUNTING(ch) && !mount)
     GET_POS(ch) = POS_STANDING;  /* reset in case of errors -roa */

   if (world[toroom].terrain_type == TERRAIN_INSIDE && PC_MOUNTING(ch))
   {
     send_to_char("Mounts may not go inside...\n\r",ch);
     return (FALSE);
   }

   mv_needed = get_move_loss(ch, toroom);

   if (PC_MOUNTING(ch) && mount)
     in_flight = IS_FLYING(mount);
   else
     in_flight = IS_FLYING(ch);

   if ((has_boat = MOB_FLAGGED(ch, MOB_SWIMMER)))
      mv_needed /= 2;
   else
   if ((has_boat = in_flight)) 
      mv_needed /= 2;

   // add additional movement loss if ranger markings are in toroom  3/27/98 -jtrhone
   if (obj_vnum_in_list(190, world[toroom].contents))
     mv_needed += 4;

   /* if in or about to go in a AIR room  JTRHONE ROA */
   if (world[ch->in_room].terrain_type == TERRAIN_AIR || world[toroom].terrain_type == TERRAIN_AIR)
      if (!in_flight)
      {
        send_to_char("To journey through the sky you need some way to fly.\n\r",ch);
        return (FALSE);
      }

   /* if in or about to go in a UWATER room  JTRHONE ROA */
   if (world[ch->in_room].terrain_type == TERRAIN_UWATER || 
       world[toroom].terrain_type == TERRAIN_UWATER) 
     if (!HAS_GILLS(ch))
      {
        send_to_char("To journey underwater you need some form of gills.\n\r",ch);
        return(FALSE);
      }
  
   /* if in or about to go in a WATER_NOSWIM room  STANDARD */
   if (!has_boat && ((world[ch->in_room].terrain_type == TERRAIN_WATER_NOSWIM) || 
                    (world[toroom].terrain_type == TERRAIN_WATER_NOSWIM)))
   {
      if (!(has_boat = HAS_GILLS(ch))) 
        // Added for Druid spell water walk. 06/07/98 -callahan
        if (!(has_boat = affected_by_spell(ch, SPELL_WATER_WALK)))
          has_boat = char_carrying_type(ch, ITEM_BOAT);

      if (!has_boat) 
      {
	 send_to_char("You must find a way to travel above or in the water.\n\r", ch);
	 return(FALSE);
      }
   }

   if (!PC_MOUNTING(ch)) 
   {
     if (GET_MOVE(ch) < mv_needed && IS_PC(ch)) 
     {
       send_to_char("You are too exhausted.\n\r", ch);
       return(FALSE);
     }
   }
   else /* pc is mounting */
   if (mount && GET_MOVE(mount) < mv_needed) 
   {
     send_to_char("Your mount is too exhausted to continue.\n\r", ch);
     return(FALSE);
   }

   mountsize = 1;
   size = get_size(ch);
   if (IS_PPL_LIMIT(world[toroom]) && 
       ((world[toroom].num_present + (PC_MOUNTING(ch) ? (size + mountsize) : size) ) >
       (world[toroom].max_contains)))
     if (!IS_IMMORTAL(ch))
     {
       send_to_char("That area is too crowded for you to enter.\n\r",ch);
       return FALSE;
     } 
     else
       send_to_char("Warning: Room Max_Contains exceeded.\n\r",ch);
    
   if (!PC_MOUNTING(ch))
   { 
     if (!IS_IMMORTAL(ch) && IS_PC(ch))
       GET_MOVE(ch) -= mv_needed;
   }
   else
   if (mount && IS_PC(ch))
     GET_MOVE(mount) -= mv_needed;

   if (PC_MOUNTING(ch) && mount)
   {
     sprintf(buf, "$n leaves %s riding $N.",dirs[cmd]);
     act(buf, TRUE, ch, 0, mount, TO_ROOM);
   }
   else 
   if (!IS_AFFECTED(ch, AFF_SNEAK)) {
     // Send oenter message of exit to the room. 03/24/98 -callahan
     if (world[InRoom(ch)].dir_option[cmd]->oenter && 
         !strstr(world[InRoom(ch)].dir_option[cmd]->oenter, "(def)")) {
       strcpy(buf, world[InRoom(ch)].dir_option[cmd]->oenter);

       delete_doubledollar(buf);
       buf[strlen(buf) - 2] = '\0';
       act(buf, TRUE, ch, 0, 0, TO_ROOM);
     } else if (IS_PC(ch) && ch->pc_specials->saved.walkOut)
       sprintf(buf2, "$n %s %s.",ch->pc_specials->saved.walkOut, dirs[cmd]);
     else if (IS_NPC(ch) && MWALKOUT(ch) && *MWALKOUT(ch) != '(')
       sprintf(buf2, "$n %s %s.",MWALKOUT(ch), dirs[cmd]);
     else
       sprintf(buf2, "$n leaves %s.", dirs[cmd]);
     act(buf2, TRUE, ch, 0, 0, TO_ROOM);
   }

  // Send enter message to character. 03/24/98 -callahan
  if (world[InRoom(ch)].dir_option[cmd]->enter && 
      !strstr(world[InRoom(ch)].dir_option[cmd]->enter, "(def)")) {
    send_to_char(world[InRoom(ch)].dir_option[cmd]->enter, ch);
    send_to_char("\r\n", ch);
  }

   // ok, going to new zone... send associated sound file... 2/18/98 -jtrhone
   if (IS_PC(ch) && tozone != world[ch->in_room].zone && zone_table[tozone].sound_file &&
       ch->desc && ch->desc->cdesc)
   {
     send_soundfile_to_client(ch->desc, zone_table[tozone].sound_file);
     ch->desc->zone_stimer = zone_table[tozone].music_timer;
     ch->desc->music_zone  = tozone;
   }
   
   // ok, we checked, we sent messages, now lets move
   was_in = ch->in_room;

   char_from_room(ch);
   char_to_room(ch, DIR(was_in, cmd)->to_room);

   if (PC_MOUNTING(ch) && mount)
   {
     char_from_room(mount);
     char_to_room(mount, DIR(was_in, cmd)->to_room);

     sprintf(buf, "$n arrives from %s riding $N.", rev_dir_str[cmd]);
     act(buf, TRUE, ch, 0, mount, TO_ROOM);
   }
   else
   if (!IS_AFFECTED(ch, AFF_SNEAK)) 
   {
     // Send odrop message of exit to next room. 03/24/98 -callahan
     if (world[was_in].dir_option[cmd]->odrop && 
         !strstr(world[was_in].dir_option[cmd]->odrop, "(def)")) {
       strcpy(buf, world[was_in].dir_option[cmd]->odrop);

       delete_doubledollar(buf);
       buf[strlen(buf) - 2] = '\0';
       act(buf, TRUE, ch, 0, 0, TO_ROOM);
     } else if (IS_PC(ch) && ch->pc_specials->saved.walkIn)
      sprintf(buf2, "$n %s from %s.", ch->pc_specials->saved.walkIn, rev_dir_str[cmd]);
     else if (IS_NPC(ch) && MWALKIN(ch) && *MWALKIN(ch) != '(')
       sprintf(buf2, "$n %s from %s.",MWALKIN(ch), rev_dir_str[cmd]);
     else
      sprintf(buf2, "$n has arrived from %s.", rev_dir_str[cmd]);     

     act(buf2, TRUE, ch, 0, 0, TO_ROOM);
   }

   do_look_at_room(ch, 0, 0);
  
  // Moved here per Silver's request at the 3rd AR Fest. 05/31/98 -callahan
  // Send drop message to character. 03/24/98 -callahan
  if (world[was_in].dir_option[cmd]->drop && 
      !strstr(world[was_in].dir_option[cmd]->drop, "(def)")) {
    send_to_char(world[was_in].dir_option[cmd]->drop, ch);
    send_to_char("\r\n", ch);   
  } 

   for (mob = world[ch->in_room].people; mob; mob=mob->next_in_room)
    if (SPC_FLAGGED(mob, SPC_SHOPKEEP) && MSTR1(mob) && !CHARMED(mob))
     if (wandering_shopkeep(mob) || (SHOPKEEP_OPEN(mob) && mob->in_room ==
	real_room(mob->npc_specials.shop_data->vshop)))
     {
	perform_reaction(mob, MSTR1(mob), ch, NULL);
	break;
     }

   if (check_room_affects(ch, ch->in_room) == CHAR_DIED)
     return -1;

   if (check_death_trap(ch, mount))
     return -1;  /* died */

   return (TRUE);  /* moved ok */
}

// updated to check for secrets and traps  4/16/98 -jtrhone
ACMD(do_move)
{
   int	was_in;
   struct follow_type *k, *next_dude;
   rmdirdata *d = NULL;

   if (IN_NOWHERE(ch)) return;

   --cmd;

   if (!IS_IMMORTAL(ch))
     if (ROOM_FLAGGED2(ch->in_room, CONFUSION) || 
        (spell_affects_room(ch->in_room, SPELL_CONFUSION) && !saves_spell(ch, SPELL_CONFUSION)))
     {
       send_to_char("%BConfusion clouds your mind%0.\n\r",ch);
       cmd = number(0, NUM_OF_DIRS -1);
     }

   d = EXIT(ch, cmd);

   // do not let secret exits stick out... 4/10/98 -jtrhone
   if (!d || EXIT_FLAGGED(d, EX_SECRET)) 
      send_to_char("Alas, you cannot go that way...\n\r", ch);
   else // direction is possible
   { 
      if (EXIT_CLOSED(d))
      {
	 if (d->keyword) 
         {
	    sprintf(buf2, "The %s seems to be closed.\n\r", fname(d->keyword));
	    send_to_char(buf2, ch);
	 } 
         else
	    send_to_char("It seems to be closed.\n\r", ch);
         return;
      } 

      // if they have traps and they trip it, do not move... 4/16/98 -jtrhone
      if (TRAPS(d))
      {
        if ((GET_DEX(ch) < 18 || !number(0,1)))
        {
          triptrap(TRAPS(d), ch, FALSE);
          TRAPS(d) = trap_from_dir(TRAPS(d), d);
          return;
        }
      }

      if (INVALID_ROOM(d->to_room))
	 send_to_char("Alas, that would lead into %4l i m b o... %0\n\r", ch);
      else 
      if (!ch->followers && !ch->master)
	 do_simple_move(ch, cmd, FALSE);
      else 
      {
	 if (CHARMED(ch) && ch->master && SAME_ROOM(ch, ch->master))
	 {
	    send_to_char("You cannot leave your master!\n\r", ch);
	    act("$n looks very confused.", FALSE, ch, 0, 0, TO_ROOM);
	 } 
	 else 
	 {
	    was_in = ch->in_room;
	    if (do_simple_move(ch, cmd, TRUE) == 1) 
            {
		for (k = ch->followers; k; k = next_dude) 
		{
		   next_dude = k->next;
		   if ((was_in == k->follower->in_room) && 
		       (GET_POS(k->follower) >= POS_STANDING) && !FIGHTING(k->follower)) 
		   {
		     act("You follow $N.", FALSE, k->follower, 0, ch, TO_CHAR);
		     do_move(k->follower, argument, cmd + 1, 0);
		   }
		}
            }
	 }
      }
   }
}

int find_door(chdata *ch, char *type, char *dir)
{
   int	door;

   if (*dir) /* a direction was specified */ 
   {
      if ((door = search_block(dir, comm_dirs, FALSE)) == -1) 
      {
	 send_to_char("Valid dirs: N, S, E, W, NE, NW, SE, SW, U, D.\n\r", ch);
	 return(-1);
      }

      if (EXIT(ch, door) && !EXIT_FLAGGED(EXIT(ch, door), EX_SECRET))
      {
	 if (EXIT(ch, door)->keyword)
         {
	    if (isname(type, EXIT(ch, door)->keyword))
	       return(door);
	    else 
            {
	       sprintf(buf2, "What %s?\n\r", type);
	       send_to_char(buf2, ch);
	       return(-1);
	    }
         }
	 else
	    return(door);
      }
      else 
      {
	 send_to_char("There isn't anything there.\n\r", ch);
	 return(-1);
      }
   } 
   else /* try to locate the keyword */	 
   {
      for (door = 0; door < NUM_OF_DIRS; door++)
	 if (EXIT(ch, door) && !EXIT_FLAGGED(EXIT(ch, door), EX_SECRET))
	    if (EXIT(ch, door)->keyword)
	       if (isname(type, EXIT(ch, door)->keyword))
		  return(door);

      sprintf(buf2, "What %s?\n\r", type);
      send_to_char(buf2, ch);
      return(-1);
   }
}

ACMD(do_open)
{
   int	door, other_room;
   char	type[MAX_INPUT_LENGTH], dir[MAX_INPUT_LENGTH];
   rmdirdata *d, *back;
   obdata *obj;
   chdata *victim;

   two_arguments(argument, type, dir);

   if (!*type)
      send_to_char("Open what?\n\r", ch);
   else 
   if (generic_find(argument, FIND_OBJ_INV | FIND_OBJ_ROOM, ch, &victim, &obj))
   {
      if (obj->type_flag != ITEM_CONTAINER)
	 send_to_char("That's not a container.\n\r", ch);
      else if (!IS_SET(obj->value[1], CONT_CLOSED))
	 send_to_char("But it's already open!\n\r", ch);
      else if (!IS_SET(obj->value[1], CONT_CLOSEABLE))
	 send_to_char("You can't do that.\n\r", ch);
      else if (IS_SET(obj->value[1], CONT_LOCKED))
	 send_to_char("It seems to be locked.\n\r", ch);
      else
       {
	 REMOVE_BIT(obj->value[1], CONT_CLOSED);
	 send_to_char("Ok.\n\r", ch);
	 act("$n opens $p.", FALSE, ch, obj, 0, TO_ROOM);
      }

      // now, check for traps and if they detonated one 4/16/98 -jtrhone
      if (TRAPS(obj))  // should have assassin check, but we're not gonna for now
        if (GET_DEX(ch) < 18 || !number(0,1))
	{
          triptrap(TRAPS(obj), ch, FALSE);
          TRAPS(obj) = trap_from_obj(TRAPS(obj), obj);
	}
   }
   else /* perhaps it is a door */
   if ((door = find_door(ch, type, dir)) >= 0)
   {
      d = EXIT(ch, door);
      if (!EXIT_ISDOOR(d))
	 send_to_char("That seems to be impossible.\n\r", ch);
      else if (EXIT_OPEN(d))
	 send_to_char("It's already open!\n\r", ch);
      else if (EXIT_LOCKED(d))
	 send_to_char("It seems to be locked.\n\r", ch);
      else if (EXIT_JAMMED(d))
	 send_to_char("It seems to be jammed.\n\r", ch);
      else 
      {
         UNFLAG_EXIT(d, EX_CLOSED);
	 if (d->keyword)
	    act("$n opens the $F.", FALSE, ch, 0, d->keyword,TO_ROOM);
	 else
	    act("$n opens something.", FALSE, ch, 0, 0, TO_ROOM);
	 send_to_char("Ok.\n\r", ch);

	 /* now for opening the OTHER side of the door! */
	 if ((other_room = d->to_room) != NOWHERE)
	    if ((back = DIR(other_room, rev_dir[door])))
	       if (back->to_room == ch->in_room) 
	       {
		  UNFLAG_EXIT(back, EX_CLOSED);
		  if (back->keyword) 
		  {
		     sprintf(buf, "The %s is opened from the other side.\n\r", fname(back->keyword));
		     send_to_room_not_busy(buf, d->to_room);
		  } else
		     send_to_room_not_busy("Something is opened from the other side.\n\r", d->to_room);
	       }
      }

      // now, check for traps and if they detonated one 4/16/98 -jtrhone
      if (TRAPS(d))  // should have assassin check, but we're not gonna for now
        if (GET_DEX(ch) < 18 || !number(0,1))
        {
          triptrap(TRAPS(d), ch, FALSE);
          TRAPS(d) = trap_from_dir(TRAPS(d), d);
        }
   }
}

ACMD(do_close)
{
   int	door, other_room;
   char	type[MAX_INPUT_LENGTH], dir[MAX_INPUT_LENGTH];
   rmdirdata *d, *back;
   obdata *obj;
   chdata *victim;

   two_arguments(argument, type, dir);

   if (!*type)
      send_to_char("Close what?\n\r", ch);
   else 
   if (generic_find(argument, FIND_OBJ_INV | FIND_OBJ_ROOM, ch, &victim, &obj))
   {
      if (obj->type_flag != ITEM_CONTAINER)
	 send_to_char("That's not a container.\n\r", ch);
      else if (IS_SET(obj->value[1], CONT_CLOSED))
	 send_to_char("But it's already closed!\n\r", ch);
      else if (!IS_SET(obj->value[1], CONT_CLOSEABLE))
	 send_to_char("That's impossible.\n\r", ch);
      else {
	 SET_BIT(obj->value[1], CONT_CLOSED);
	 send_to_char("Ok.\n\r", ch);
	 act("$n closes $p.", FALSE, ch, obj, 0, TO_ROOM);
      }

      // now, check for traps and if they detonated one 4/16/98 -jtrhone
      if (TRAPS(obj))  // should have assassin check, but we're not gonna for now
        if (GET_DEX(ch) < 18 || !number(0,1))
	{
          triptrap(TRAPS(obj), ch, FALSE);
          TRAPS(obj) = trap_from_obj(TRAPS(obj), obj);
	}
   }
   else /* Or a door */
   if ((door = find_door(ch, type, dir)) >= 0)
   {
      d = EXIT(ch, door);
      if (!EXIT_ISDOOR(d))
	 send_to_char("That's absurd.\n\r", ch);
      else 
      if (EXIT_CLOSED(d))
	 send_to_char("It's already closed!\n\r", ch);
      else 
      if (EXIT_JAMMED(d))
	 send_to_char("It seems to be jammed.\n\r", ch);
      else 
      {
	 FLAG_EXIT(d, EX_CLOSED);
	 if (d->keyword)
	    act("$n closes the $F.", 0, ch, 0, d->keyword,TO_ROOM);
	 else
	    act("$n closes something.", FALSE, ch, 0, 0, TO_ROOM);
	 send_to_char("Ok.\n\r", ch);

	 /* now for closing the other side, too */
	 if ((other_room = d->to_room) != NOWHERE)
	    if ((back = DIR(other_room, rev_dir[door])))
	       if (back->to_room == ch->in_room) 
	       {
		  FLAG_EXIT(back, EX_CLOSED);
		  if (back->keyword) {
		     sprintf(buf, "The %s closes quietly.\n\r", back->keyword);
		     send_to_room_not_busy(buf, d->to_room);
		  } else
		     send_to_room_not_busy("Something closes quietly.\n\r", d->to_room);
	       }
      }

      // now, check for traps and if they detonated one 4/16/98 -jtrhone
      if (TRAPS(d))  // should have assassin check, but we're not gonna for now
        if (GET_DEX(ch) < 18 || !number(0,1))
        {
          triptrap(TRAPS(d), ch, FALSE);
          TRAPS(d) = trap_from_dir(TRAPS(d), d);
        }
   }
}

int has_key(chdata *ch, int key)
{
   obdata *o;

   for (o = ch->carrying; o; o = o->next_content)
      if (GET_OBJ_VNUM(o) == key)
	 return(TRUE);

   if (EQ(ch, W_HOLD) && GET_OBJ_VNUM(EQ(ch, W_HOLD)) == key)
     return(TRUE);

   return(FALSE);
}

ACMD(do_lock)
{
   int	door, other_room;
   char	type[MAX_INPUT_LENGTH], dir[MAX_INPUT_LENGTH];
   rmdirdata *d, *back;
   obdata *obj;
   chdata *victim;

   two_arguments(argument, type, dir);

   if (!*type)
      send_to_char("Lock what?\n\r", ch);
   else 
   if (generic_find(argument, FIND_OBJ_INV | FIND_OBJ_ROOM, ch, &victim, &obj))
   {
      if (obj->type_flag != ITEM_CONTAINER)
	 send_to_char("That's not a container.\n\r", ch);
      else if (!IS_SET(obj->value[1], CONT_CLOSED))
	 send_to_char("Maybe you should close it first...\n\r", ch);
      else if (obj->value[2] < 0)
	 send_to_char("That thing can't be locked.\n\r", ch);
      else if (!has_key(ch, obj->value[2]))
	 send_to_char("You don't seem to have the proper key.\n\r", ch);
      else if (IS_SET(obj->value[1], CONT_LOCKED))
	 send_to_char("It is locked already.\n\r", ch);
      else {
	 SET_BIT(obj->value[1], CONT_LOCKED);
	 send_to_char("*Cluck*\n\r", ch);
	 act("$n locks $p - 'cluck', it says.", FALSE, ch, obj, 0, TO_ROOM);
      }

      // now, check for traps and if they detonated one 4/16/98 -jtrhone
      if (TRAPS(obj))  // should have assassin check, but we're not gonna for now
        if (GET_DEX(ch) < 18 || !number(0,1))
	{
          triptrap(TRAPS(obj), ch, FALSE);
          TRAPS(obj) = trap_from_obj(TRAPS(obj), obj);
	}
   }
   else /* a door, perhaps */
   if ((door = find_door(ch, type, dir)) >= 0)
   {
      d = EXIT(ch, door);
      if (!EXIT_ISDOOR(d))
	 send_to_char("That's absurd.\n\r", ch);
      else if (EXIT_OPEN(d))
	 send_to_char("You have to close it first.\n\r", ch);
      else if (d->key < 0)
	 send_to_char("There does not seem to be any keyholes.\n\r", ch);
      else if (!has_key(ch, d->key))
	 send_to_char("You don't have the proper key.\n\r", ch);
      else if (EXIT_JAMMED(d))
	 send_to_char("It seems to be jammed.\n\r", ch);
      else if (EXIT_LOCKED(d))
	 send_to_char("It's already locked!\n\r", ch);
      else 
      {
	 FLAG_EXIT(d, EX_LOCKED);
	 if (d->keyword)
	    act("$n locks the $F.", 0, ch, 0,  d->keyword, TO_ROOM);
	 else
	    act("$n locks something.", FALSE, ch, 0, 0, TO_ROOM);
	 send_to_char("*Click*\n\r", ch);

	 /* now for locking the other side, too */
	 if ((other_room = d->to_room) != NOWHERE)
	    if ((back = DIR(other_room, rev_dir[door])))
	       if (back->to_room == ch->in_room)
		  FLAG_EXIT(back, EX_LOCKED);
      }

      // now, check for traps and if they detonated one 4/16/98 -jtrhone
      if (TRAPS(d))  // should have assassin check, but we're not gonna for now
        if (GET_DEX(ch) < 18 || !number(0,1))
        {
          triptrap(TRAPS(d), ch, FALSE);
          TRAPS(d) = trap_from_dir(TRAPS(d), d);
        }
   }
}

ACMD(do_unlock)
{
   int	door, other_room;
   char	type[MAX_INPUT_LENGTH], dir[MAX_INPUT_LENGTH];
   rmdirdata *d, *back = NULL;
   obdata *obj = NULL;
   chdata *victim = NULL;

   two_arguments(argument, type, dir);

   if (!*type)
      send_to_char("Unlock what?\n\r", ch);
   else 
   if (generic_find(argument, FIND_OBJ_INV | FIND_OBJ_ROOM, ch, &victim, &obj))
   {
      if (obj->type_flag != ITEM_CONTAINER)
	 send_to_char("That's not a container.\n\r", ch);
      else if (!IS_SET(obj->value[1], CONT_CLOSED))
	 send_to_char("Silly - it ain't even closed!\n\r", ch);
      else if (obj->value[2] < 0)
	 send_to_char("Odd - you can't seem to find a keyhole.\n\r", ch);
      else if (!has_key(ch, obj->value[2]))
	 send_to_char("You don't seem to have the proper key.\n\r", ch);
      else if (!IS_SET(obj->value[1], CONT_LOCKED))
	 send_to_char("Oh.. it wasn't locked, after all.\n\r", ch);
      else {
	 REMOVE_BIT(obj->value[1], CONT_LOCKED);
	 send_to_char("*Click*\n\r", ch);
	 act("$n unlocks $p.", FALSE, ch, obj, 0, TO_ROOM);
      }

      // now, check for traps and if they detonated one 4/16/98 -jtrhone
      if (TRAPS(obj))  // should have assassin check, but we're not gonna for now
        if (GET_DEX(ch) < 18 || !number(0,1))
	{
          triptrap(TRAPS(obj), ch, FALSE);
          TRAPS(obj) = trap_from_obj(TRAPS(obj), obj);
	}
   }
   else /* it is a door */
   if ((door = find_door(ch, type, dir)) >= 0)
   {
      d = EXIT(ch, door);
      if (!EXIT_ISDOOR(d))
	 send_to_char("That's absurd.\n\r", ch);
      else if (EXIT_OPEN(d))
	 send_to_char("It's not closed!\n\r", ch);
      else if (d->key < 0)
	 send_to_char("You can't seem to spot any keyholes.\n\r", ch);
      else if (!has_key(ch, d->key))
	 send_to_char("You do not have the proper key for that.\n\r", ch);
      else if (EXIT_JAMMED(d))
	 send_to_char("It seems to be jammed.\n\r", ch);
      else if (EXIT_UNLOCKED(d))
	 send_to_char("It's already unlocked, it seems.\n\r", ch);
      else {
	 UNFLAG_EXIT(d, EX_LOCKED);
	 if (d->keyword)
	    act("$n unlocks the $F.", 0, ch, 0, d->keyword,TO_ROOM);
	 else
	    act("$n unlocks something.", FALSE, ch, 0, 0, TO_ROOM);
	 send_to_char("*click*\n\r", ch);

	 /* now for unlocking the other side, too */
	 if ((other_room = d->to_room) != NOWHERE)
	    if ((back = DIR(other_room, rev_dir[door])))
	       if (back->to_room == ch->in_room)
		  UNFLAG_EXIT(back, EX_LOCKED);
      }

      // now, check for traps and if they detonated one 4/16/98 -jtrhone
      if (TRAPS(d))  // should have assassin check, but we're not gonna for now
        if (GET_DEX(ch) < 18 || !number(0,1))
	{
          triptrap(TRAPS(d), ch, FALSE);
          TRAPS(d) = trap_from_dir(TRAPS(d), d);
	}
   }
}

void do_enter_transport(chdata *ch, int r_room)
{
   obdata *obj = 0;
   BOOL has = FALSE;

   if (world[r_room].max_contains >= 0)
   if ((world[r_room].num_present + get_size(ch)) > world[r_room].max_contains)
   {
     send_to_char("There isn't enough room for you!\n\r",ch);
     return;
   }

   /* check for possesion of key needed */
   if (world[r_room].ticket_num > 0)
   {
     for (obj = ch->carrying; obj; obj=obj->next_content)
       if (GET_OBJ_VNUM(obj) == world[r_room].ticket_num)
	{
	  has = TRUE;
	  break;
	}
   }
   else
     has = TRUE;

   if (!has && !IS_IMMORTAL(ch))
   {
     send_to_char("You need a ticket or boarding pass to enter.\n\r",ch);
     return;
   }

   if (has && obj)
   {
     act("$p disappears from your inventory!", TRUE, ch, obj, 0, TO_CHAR);
     extract_obj(obj);
     obj = NULL;
   }

   if (world[r_room].randoms[1])
      sprintf(buf1, "%s", world[r_room].randoms[1]);
   else
      sprintf(buf1, "%s", world[r_room].name);
   UNCAP(buf1);
   sprintf(buf, "You enter %s.\n\r",buf1);
   send_to_char(buf, ch);

   sprintf(buf, "$n enters %s.", buf1);
   act(buf, TRUE, ch, 0, 0, TO_ROOM);

   char_from_room(ch);
   char_to_room(ch, r_room);

   sprintf(buf, "$n enters %s.", buf1);
   act(buf, TRUE, ch, 0, 0, TO_ROOM);

   do_look_at_room(ch, 0, 0);
   check_room_affects(ch, ch->in_room);
}

/* in ch->in_room going to r_room */
void do_leave_transport(chdata *ch, int r_room)
{
   if (world[r_room].max_contains >= 0)
   if ((world[r_room].num_present + get_size(ch)) > world[r_room].max_contains)
   {
     send_to_char("There isn't enough room for you!\n\r",ch);
     return;
   }

   if (*world[ch->in_room].randoms[1])
      sprintf(buf1, "%s", world[ch->in_room].randoms[1]);
   else
      sprintf(buf1, "%s", world[ch->in_room].name);

   sprintf(buf, "You leave %s.\n\r",buf1);
   send_to_char(buf, ch);
   sprintf(buf, "$n leaves %s.", buf1);
   act(buf, TRUE, ch, 0, 0, TO_ROOM);

   char_from_room(ch);
   char_to_room(ch, r_room);

   sprintf(buf, "$n enters from %s.", buf1);
   act(buf, TRUE, ch, 0, 0, TO_ROOM);

   do_look_at_room(ch, 0, 0);
   check_room_affects(ch, ch->in_room);
}

ACMD(do_enter)
{
   int	door, rm;

   if (IN_NOWHERE(ch)) return;

   one_argument(argument, buf);

   if (world[ch->in_room].trans_present > 0 && (rm = real_room(world[ch->in_room].trans_present)) >= 0)
   {
     do_enter_transport(ch, rm);
     return;
   }

   if (*buf)  /* an argument was supplied, search for door keyword */ 
   {
      for (door = 0; door < NUM_OF_DIRS; door++)
	 if (EXIT(ch, door) && !EXIT_FLAGGED(EXIT(ch, door), EX_SECRET))
	    if (EXIT(ch, door)->keyword)
	       if (!str_cmp(EXIT(ch, door)->keyword, buf)) 
               {
		  do_move(ch, "", ++door, 0);
		  return;
	       }
      sprintf(buf2, "There is no %s here.\n\r", buf);
      send_to_char(buf2, ch);
   } 
   else 
     if (world[ch->in_room].terrain_type == TERRAIN_INSIDE)
      send_to_char("You are already inside.\n\r", ch);
   else 
   {
      /* try to locate an entrance */
      for (door = 0; door < NUM_OF_DIRS; door++)
	if (CAN_GO(ch, door) && !EXIT_FLAGGED(EXIT(ch, door), EX_SECRET) &&
	    world[EXIT(ch, door)->to_room].terrain_type == TERRAIN_INSIDE)
	{
	  do_move(ch, "", ++door, 0);
	  return;
	}
      send_to_char("You can't seem to find anything to enter.\n\r", ch);
   }
}

ACMD(do_leave)
{
   int	door, rm;

   if (IN_NOWHERE(ch)) return;
   if (ROOM_FLAGGED2(ch->in_room, TRANSPORT) && world[ch->in_room].location > 0 &&
       (rm = real_room(world[ch->in_room].location)) >= 0)
   {
     do_leave_transport(ch, rm);
     return;
   }

   if (OUTSIDE(ch))
      send_to_char("You are outside.. where do you want to go?\n\r", ch);
   else 
   {
      for(door = 0; door < NUM_OF_DIRS; door++)
	if(CAN_GO(ch, door) && !EXIT_FLAGGED(EXIT(ch, door), EX_SECRET) &&
	   world[EXIT(ch, door)->to_room].terrain_type != TERRAIN_INSIDE) 
	{
	  do_move(ch, "", ++door, 0);
	  return;
	}
      send_to_char("There are no obvious exits to the outside.\n\r", ch);
   }
}

ACMD(do_float)
{

   if (IS_NPC(ch)) return;

   if (GET_RACE(ch) != RACE_PIXIE) {
      send_to_char("You try to float like a pixie, but you cannot.\n\r",ch);
      act("$n attempts to imitate a pixie.", TRUE, ch, 0, 0, TO_ROOM);
      return;
   }
   switch (GET_POS(ch)) {
   case POS_MOUNTED:
      send_to_char("You must dismount first.\n\r", ch);
      break;
   case POS_FLOATING :
      act("You are already hovering above the ground.", FALSE, ch, 0, 0, TO_CHAR);
      break;
   case POS_FIGHTING :
      act("You are fighting!!", FALSE, ch, 0, 0, TO_CHAR);
      break;
   case POS_STANDING :
      send_to_char("You start to hover above the ground.\n\r",ch);
      act("$n starts to float.", TRUE, ch, 0, 0, TO_ROOM);
      GET_POS(ch) = POS_FLOATING;
      break;
   default :
      act("You start to float.", FALSE, ch, 0, 0, TO_CHAR);
      act("$n starts to float.", TRUE, ch, 0, 0, TO_ROOM);
      GET_POS(ch) = POS_FLOATING;
      break;
   }
}

ACMD(do_dragon_flight)
{

   if (IS_NPC(ch)) return;

   if (GET_RACE(ch) != RACE_DRAGON) {
      send_to_char("You try to fly like a drakyn, but you cannot.\n\r",ch);
      act("$n attempts to imitate a drakyn.", TRUE, ch, 0, 0, TO_ROOM);
      return;
   }

   if (GET_LEVEL(ch) < 20 && GET_AGE(ch) < 22) {  /* could use age here ? */
      send_to_char("Your wings are not fully developed for flight yet.\n\r",ch);
      act("$n trys to take flight, but is too young.", TRUE, ch, 0, 0, TO_ROOM);
      return;
   }

   switch (GET_POS(ch)) {
   case POS_MOUNTED:
      send_to_char("You must dismount first.\n\r", ch);
      break;
   case POS_FLOATING :
      act("You are already in flight.", FALSE, ch, 0, 0, TO_CHAR);
      break;
   case POS_FIGHTING :
      act("You are fighting!!", FALSE, ch, 0, 0, TO_CHAR);
      break;
   case POS_STANDING :
      send_to_char("You take flight and soar above the ground.\n\r",ch);
      act("$n starts to soar above the ground.", TRUE, ch, 0, 0, TO_ROOM);
      GET_POS(ch) = POS_FLOATING;
      break;
   default :
      act("You start to soar above the ground.", FALSE, ch, 0, 0, TO_CHAR);
      act("$n starts soar above the ground.", TRUE, ch, 0, 0, TO_ROOM);
      GET_POS(ch) = POS_FLOATING;
      break;
   }
}

ACMD(do_stand)
{
   switch (GET_POS(ch)) {
   case POS_MOUNTED:
      send_to_char("You must dismount to stand.\n\r", ch);
      break;
   case POS_FLOATING:
      act("You put your feet on the ground.", FALSE, ch, 0, 0, TO_CHAR);
      act("$n stops floating.", TRUE, ch, 0, 0, TO_ROOM);
      GET_POS(ch) = POS_STANDING;
      break;
   case POS_STANDING:
      act("You are already standing.", FALSE, ch, 0, 0, TO_CHAR);
      break;
   case POS_SITTING:
      act("You stand up.", FALSE, ch, 0, 0, TO_CHAR);

      // Added checks for different messages depending on wounds.
      // 08/13/98 -callahan
      if (Hits(ch) > (MaxHits(ch) / 2))
        act("$n clambers to $s feet.", TRUE, ch, 0, 0, TO_ROOM);
      else if (Hits(ch) > (MaxHits(ch) / 6))
        act("$n slowly stands up.", TRUE, ch, 0, 0, TO_ROOM);
      else
        act("$n gets to $s feet very slowly.", TRUE, ch, 0, 0, TO_ROOM);

      GET_POS(ch) = POS_STANDING;
      break;
   case POS_RESTING:
      act("You stop resting, and stand up.", FALSE, ch, 0, 0, TO_CHAR);
      act("$n stops resting, and clambers on $s feet.", TRUE, ch, 0, 0, TO_ROOM);
      GET_POS(ch) = POS_STANDING;
      break;
   case POS_SLEEPING:
      act("You have to wake up first!", FALSE, ch, 0, 0, TO_CHAR);
      break;
   case POS_FIGHTING:
      act("Of course you're already standing!", FALSE, ch, 0, 0, TO_CHAR);
      break;
   default :
      act("You stop floating around, and put your feet on the ground.",
          FALSE, ch, 0, 0, TO_CHAR);
      act("$n stops floating around, and puts $s feet on the ground.",
          TRUE, ch, 0, 0, TO_ROOM);
      break;
   }
}


ACMD(do_sit)
{
   switch (GET_POS(ch)) {
   case POS_MOUNTED:
      send_to_char("You must dismount first.\n\r", ch);
      break;
   case POS_FLOATING :
      act("You stop floating and sit down.", FALSE, ch, 0, 0, TO_CHAR);
      act("$n stops floating and sits down.", FALSE, ch, 0, 0, TO_ROOM);
      GET_POS(ch) = POS_SITTING;
      break;
   case POS_STANDING :
      act("You sit down.", FALSE, ch, 0, 0, TO_CHAR);
      act("$n sits down.", FALSE, ch, 0, 0, TO_ROOM);
      GET_POS(ch) = POS_SITTING;
      break;
   case POS_SITTING	:
      send_to_char("You're sitting already.\n\r", ch);
      break;
   case POS_RESTING	:
      act("You stop resting, and sit up.", FALSE, ch, 0, 0, TO_CHAR);
      act("$n stops resting.", TRUE, ch, 0, 0, TO_ROOM);
      GET_POS(ch) = POS_SITTING;
      break;
   case POS_SLEEPING :
      act("You have to wake up first.", FALSE, ch, 0, 0, TO_CHAR);
      break;
   case POS_FIGHTING :
      act("Sit down while fighting? are you MAD?", FALSE, ch, 0, 0, TO_CHAR);
      break;
   default :
      act("You stop floating around, and sit down.", FALSE, ch, 0, 0, TO_CHAR);
      act("$n stops floating around, and sits down.", TRUE, ch, 0, 0, TO_ROOM);
      GET_POS(ch) = POS_SITTING;
      break;
   }
}




ACMD(do_rest)
{
   switch (GET_POS(ch)) {
   case POS_MOUNTED:
      send_to_char("You must dismount first.\n\r", ch);
      break;
   case POS_FLOATING :
      act("You stop floating and rest your tired bones.", FALSE, ch, 0, 0, TO_CHAR);
      act("$n stops floating and rests.", TRUE, ch, 0, 0, TO_ROOM);
      GET_POS(ch) = POS_RESTING;
      break;
   case POS_STANDING :
      act("You sit down and rest your tired bones.", FALSE, ch, 0, 0, TO_CHAR);
      act("$n sits down and rests.", TRUE, ch, 0, 0, TO_ROOM);
      GET_POS(ch) = POS_RESTING;
      break;
   case POS_SITTING :
      act("You rest your tired bones.", FALSE, ch, 0, 0, TO_CHAR);
      act("$n rests.", TRUE, ch, 0, 0, TO_ROOM);
      GET_POS(ch) = POS_RESTING;
      break;
   case POS_RESTING :
      act("You are already resting.", FALSE, ch, 0, 0, TO_CHAR);
      break;
   case POS_SLEEPING :
      act("You have to wake up first.", FALSE, ch, 0, 0, TO_CHAR);
      break;
   case POS_FIGHTING :
      act("Rest while fighting?  Are you MAD?", FALSE, ch, 0, 0, TO_CHAR);
      break;
   default :
      act("You stop floating around, and stop to rest your tired bones.",
          FALSE, ch, 0, 0, TO_CHAR);
      act("$n stops floating around, and rests.", FALSE, ch, 0, 0, TO_ROOM);
      GET_POS(ch) = POS_SITTING;
      break;
   }
}


ACMD(do_sleep)
{
   switch (GET_POS(ch)) {
   case POS_MOUNTED:
      send_to_char("You must dismount first.\n\r", ch);
      break;
   case POS_STANDING :
   case POS_SITTING  :
   case POS_RESTING  :
      send_to_char("You go to sleep.\n\r", ch);
      act("$n lies down and falls asleep.", TRUE, ch, 0, 0, TO_ROOM);
      GET_POS(ch) = POS_SLEEPING;
      if (IS_DROW(ch) && !IS_AFFECTED(ch, AFF_INFRARED)) {
         send_to_char("You regain your drow infravision.\n\r",ch);
         SET_BIT(ch->specials.affected_by, AFF_INFRARED);
      }
      if (IS_NIXIE(ch) && !IS_AFFECTED(ch, AFF_GILLS)) {
         send_to_char("You regain your Nixie waterbreath ability.\n\r",ch);
         SET_BIT(ch->specials.affected_by, AFF_GILLS);
      }
      break;
   case POS_SLEEPING :
      send_to_char("You are already sound asleep.\n\r", ch);
      break;
   case POS_FIGHTING :
      send_to_char("Sleep while fighting?  Are you MAD?\n\r", ch);
      break;
   case POS_FLOATING :
   default :
      act("You stop floating around, and lie down to sleep.",
          FALSE, ch, 0, 0, TO_CHAR);
      act("$n stops floating around, and lies down to sleep.",
          TRUE, ch, 0, 0, TO_ROOM);
      GET_POS(ch) = POS_SLEEPING;
      break;
   }
}


ACMD(do_wake)
{
   chdata *tmp_char;

   one_argument(argument, arg);
   if (*arg) {
      if (GET_POS(ch) == POS_SLEEPING) {
	 act("You can't wake people up if you are asleep yourself!",
	     FALSE, ch, 0, 0, TO_CHAR);
      } else {
	 tmp_char = get_char_room_vis(ch, arg);
	 if (tmp_char) {
	    if (tmp_char == ch) {
	       act("If you want to wake yourself up, just type 'wake'",
	           FALSE, ch, 0, 0, TO_CHAR);
	    } else {
	       if (GET_POS(tmp_char) == POS_SLEEPING) {
		  if (IS_AFFECTED(tmp_char, AFF_SLEEP)) {
		     act("You can not wake $M up!", FALSE, ch, 0, tmp_char, TO_CHAR);
		  } else {
		     act("You wake $M up.", FALSE, ch, 0, tmp_char, TO_CHAR);
		     GET_POS(tmp_char) = POS_SITTING;
		     act("You are awakened by $n.", FALSE, ch, 0, tmp_char, TO_VICT);
		  }
	       } else {
		  act("$N is already awake.", FALSE, ch, 0, tmp_char, TO_CHAR);
	       }
	    }
	 } else {
	    send_to_char("You do not see that person here.\n\r", ch);
	 }
      }
   } else {
      if (IS_AFFECTED(ch, AFF_SLEEP)) {
	 send_to_char("You can't wake up!\n\r", ch);
      } else {
	 if (GET_POS(ch) > POS_SLEEPING)
	    send_to_char("You are already awake...\n\r", ch);
	 else {
	    send_to_char("You wake, and sit up.\n\r", ch);
	    act("$n awakens.", TRUE, ch, 0, 0, TO_ROOM);
	    GET_POS(ch) = POS_SITTING;
	 }
      }
   }
}

ACMD(do_follow)
{
   chdata *leader;

   one_argument(argument, buf);

   if (*buf) {
      if (!str_cmp(buf, "self"))
	 leader = ch;
      else if (!(leader = get_char_room_vis(ch, buf))) {
	 send_to_char("I see no person by that name here!\n\r", ch);
	 return;
      }
   } else {
      send_to_char("Whom do you wish to follow?\n\r", ch);
      return;
   }

   if (ch->master == leader) {
      sprintf(buf, "You are already following %s.\n\r", HMHR(leader));
      send_to_char(buf, ch);
      return;
   }

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

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

	 REMOVE_BIT(ch->specials.affected_by, AFF_GROUP);
	 add_follower(ch, leader);
      }
   }
}