swr2.0/
swr2.0/area/
swr2.0/boards/
swr2.0/clans/
swr2.0/doc/
swr2.0/planets/
swr2.0/spacecraft/
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include "mud.h"

extern int top_exit;

bool char_cloned( CHAR_DATA *ch );

/**
 * Gets if a character has a clone.
 *
 * @param ch Character to check if it has a clone.
 *
 * @returns If the character has a clone.
 */
bool char_cloned( CHAR_DATA *ch )
{
   char buf[MAX_STRING_LENGTH];
   struct stat fst;

   sprintf( buf, "%s%c/%s.clone", PLAYER_DIR,
      LOWER( ch->name[0] ), capitalize( ch->name ) );

   return ( stat( buf, &fst ) != -1 );
}

/**
 * Command that lets a character create a clone.
 *
 * @param ch       Character that initiated the command.
 * @param argument Argument passed to the command by the player.
 */
void do_clone( CHAR_DATA * ch, char *argument )
{
   long credits;
   CLAN_DATA *clan;

   if( IS_NPC( ch ) )
   {
      ch_printf( ch, "Only real people can do that!\n\r" );
      return;
   }

   if( !ch->in_room || !IS_SET( ch->in_room->room_flags, ROOM_MEDICAL ) )
   {
      send_to_char( "You must be in a medical clinic to do that!\n\r", ch );
      return;
   }

   if ( char_cloned( ch ) )
   {
      if ( ch->gold < CLONE_PRICE )
      {
         ch_printf( ch, "It cost %d credits to update your clone. You do not have enough.\n\r", CLONE_PRICE );
         return;
      }

      ch->gold -= CLONE_PRICE;
      ch_printf( ch, "&WYou pay %ld credits to update your clone.\n\r\n\r", CLONE_PRICE );
   }

   // Clones don't get to keep credits
   credits = ch->gold;
   ch->gold = 0;

   // No clan either
   clan = ch->pcdata->clan;
   ch->pcdata->clan = NULL;

   save_clone( ch );

   ch->pcdata->clan = clan;
   ch->gold = credits;
   
   ch_printf( ch, "&WA small tissue sample is taken from your arm.\n\r" );
   ch_printf( ch, "&ROuch!\n\r\n\r" );
   ch_printf( ch, "&WYou have been succesfully cloned.\n\r" );

   ch->hit--;
}

void do_buyhome( CHAR_DATA * ch, char *argument )
{
  ROOM_INDEX_DATA *room;
  char filename[256];

  if( !ch->in_room )
    return;

  if( !ch->in_room->area )
  {
    send_to_char( "&RThis is not a safe place to live.\r\n&w", ch );
    return;
  }


  if( IS_NPC( ch ) || !ch->pcdata )
    return;

  if( ch->plr_home != NULL )
  {
    send_to_char( "&RYou already have a home!\r\n&w", ch );
    return;
  }

  room = ch->in_room;

  if( !IS_SET( room->room_flags, ROOM_EMPTY_HOME ) )
  {
    send_to_char( "&RThis room isn't for sale!\r\n&w", ch );
    return;
  }

  if( ch->gold < 100000 )
  {
    send_to_char
      ( "&RThis room costs 100000 credits you don't have enough!\r\n&w",
	ch );
    return;
  }

  if( argument[0] == '\0' )
  {
    send_to_char
      ( "Set the room name.  A very brief single line room description.\r\n",
	ch );
    send_to_char( "Usage: Buyhome <Room Name>\r\n", ch );
    return;
  }

  STRFREE( room->name );
  room->name = STRALLOC( argument );

  ch->gold -= 100000;

  REMOVE_BIT( room->room_flags, ROOM_EMPTY_HOME );
  SET_BIT( room->room_flags, ROOM_PLR_HOME );
  SET_BIT( room->room_flags, ROOM_HOTEL );
  sprintf( filename, "%s%s", AREA_DIR, room->area->filename );
  fold_area( room->area, filename, FALSE );

  ch->plr_home = room;
  do_save( ch, STRLIT_EMPTY );

}

void do_ammo( CHAR_DATA * ch, char *argument )
{
  OBJ_DATA *wield;
  OBJ_DATA *obj;
  bool checkammo = FALSE;
  int charge = 0;

  obj = NULL;
  wield = get_eq_char( ch, WEAR_WIELD );
  if( wield )
  {
    obj = get_eq_char( ch, WEAR_DUAL_WIELD );
    if( !obj )
      obj = get_eq_char( ch, WEAR_HOLD );
  }
  else
  {
    wield = get_eq_char( ch, WEAR_HOLD );
    obj = NULL;
  }

  if( !wield || wield->item_type != ITEM_WEAPON )
  {
    send_to_char( "&RYou don't seem to be holding a weapon.\r\n&w", ch );
    return;
  }

  if( wield->value[3] == WEAPON_BLASTER )
  {

    if( obj && obj->item_type != ITEM_AMMO )
    {
      send_to_char
	( "&RYour hands are too full to reload your blaster.\r\n&w", ch );
      return;
    }

    if( obj )
    {
      if( obj->value[0] > wield->value[5] )
      {
	send_to_char( "That cartridge is too big for your blaster.",
	    ch );
	return;
      }
      unequip_char( ch, obj );
      checkammo = TRUE;
      charge = obj->value[0];
      separate_obj( obj );
      extract_obj( obj );
    }
    else
    {
      for( obj = ch->last_carrying; obj; obj = obj->prev_content )
      {
	if( obj->item_type == ITEM_AMMO )
	{
	  if( obj->value[0] > wield->value[5] )
	  {
	    send_to_char
	      ( "That cartridge is too big for your blaster.", ch );
	    continue;
	  }
	  checkammo = TRUE;
	  charge = obj->value[0];
	  separate_obj( obj );
	  extract_obj( obj );
	  break;
	}
      }
    }

    if( !checkammo )
    {
      send_to_char
	( "&RYou don't seem to have any ammo to reload your blaster with.\r\n&w",
	  ch );
      return;
    }

    ch_printf( ch,
	"You replace your ammunition cartridge.\r\nYour blaster is charged with %d shots at high power to %d shots on low.\r\n",
	charge / 5, charge );
    act( AT_PLAIN, "$n replaces the ammunition cell in $p.", ch, wield,
	NULL, TO_ROOM );

  }
  else
  {
    if( obj && obj->item_type != ITEM_BATTERY )
    {
      send_to_char
	( "&RYour hands are too full to replace the power cell.\r\n&w",
	  ch );
      return;
    }

    if( obj )
    {
      unequip_char( ch, obj );
      checkammo = TRUE;
      charge = obj->value[0];
      separate_obj( obj );
      extract_obj( obj );
    }
    else if( ( obj = get_obj_type_char( ch, ITEM_BATTERY ) ) )
      {
	checkammo = TRUE;
	charge = obj->value[0];
	separate_obj( obj );
	extract_obj( obj );
      }

    if( !checkammo )
    {
      send_to_char( "&RYou don't seem to have a power cell.\r\n&w", ch );
      return;
    }

    if( wield->value[3] == WEAPON_LIGHTSABER )
    {
      ch_printf( ch,
	  "You replace your power cell.\r\nYour lightsaber is charged to %d/%d units.\r\n",
	  charge, charge );
      act( AT_PLAIN, "$n replaces the power cell in $p.", ch, wield, NULL,
	  TO_ROOM );
      act( AT_PLAIN, "$p ignites with a bright glow.", ch, wield, NULL,
	  TO_ROOM );
    }
    else if( wield->value[3] == WEAPON_VIBRO_BLADE )
    {
      ch_printf( ch,
	  "You replace your power cell.\r\nYour vibro-blade is charged to %d/%d units.\r\n",
	  charge, charge );
      act( AT_PLAIN, "$n replaces the power cell in $p.", ch, wield, NULL,
	  TO_ROOM );
    }
    else
    {
      ch_printf( ch, "You feel very foolish.\r\n" );
      act( AT_PLAIN, "$n tries to jam a power cell into $p.", ch, wield,
	  NULL, TO_ROOM );
    }
  }

  wield->value[4] = charge;
}

void do_setblaster( CHAR_DATA * ch, char *argument )
{
  OBJ_DATA *wield;
  OBJ_DATA *wield2;

  wield = get_eq_char( ch, WEAR_WIELD );
  if( wield
      && !( wield->item_type == ITEM_WEAPON
	&& wield->value[3] == WEAPON_BLASTER ) )
    wield = NULL;
  wield2 = get_eq_char( ch, WEAR_DUAL_WIELD );
  if( wield2
      && !( wield2->item_type == ITEM_WEAPON
	&& wield2->value[3] == WEAPON_BLASTER ) )
    wield2 = NULL;

  if( !wield && !wield2 )
  {
    send_to_char( "&RYou don't seem to be wielding a blaster.\r\n&w", ch );
    return;
  }

  if( argument[0] == '\0' )
  {
    send_to_char
      ( "&RUsage: setblaster <full|high|normal|half|low|stun>\r\n&w", ch );
    return;
  }

  if( wield )
    act( AT_PLAIN, "$n adjusts the settings on $p.", ch, wield, NULL,
	TO_ROOM );

  if( wield2 )
    act( AT_PLAIN, "$n adjusts the settings on $p.", ch, wield2, NULL,
	TO_ROOM );

  if( !str_cmp( argument, "full" ) )
  {
    if( wield )
    {
      wield->blaster_setting = BLASTER_FULL;
      send_to_char( "&YWielded blaster set to FULL Power\r\n&w", ch );
    }
    if( wield2 )
    {
      wield2->blaster_setting = BLASTER_FULL;
      send_to_char( "&YDual wielded blaster set to FULL Power\r\n&w",
	  ch );
    }
    return;
  }
  if( !str_cmp( argument, "high" ) )
  {
    if( wield )
    {
      wield->blaster_setting = BLASTER_HIGH;
      send_to_char( "&YWielded blaster set to HIGH Power\r\n&w", ch );
    }
    if( wield2 )
    {
      wield2->blaster_setting = BLASTER_HIGH;
      send_to_char( "&YDual wielded blaster set to HIGH Power\r\n&w",
	  ch );
    }
    return;
  }
  if( !str_cmp( argument, "normal" ) )
  {
    if( wield )
    {
      wield->blaster_setting = BLASTER_NORMAL;
      send_to_char( "&YWielded blaster set to NORMAL Power\r\n&w", ch );
    }
    if( wield2 )
    {
      wield2->blaster_setting = BLASTER_NORMAL;
      send_to_char( "&YDual wielded blaster set to NORMAL Power\r\n&w",
	  ch );
    }
    return;
  }
  if( !str_cmp( argument, "half" ) )
  {
    if( wield )
    {
      wield->blaster_setting = BLASTER_HALF;
      send_to_char( "&YWielded blaster set to HALF Power\r\n&w", ch );
    }
    if( wield2 )
    {
      wield2->blaster_setting = BLASTER_HALF;
      send_to_char( "&YDual wielded blaster set to HALF Power\r\n&w",
	  ch );
    }
    return;
  }
  if( !str_cmp( argument, "low" ) )
  {
    if( wield )
    {
      wield->blaster_setting = BLASTER_LOW;
      send_to_char( "&YWielded blaster set to LOW Power\r\n&w", ch );
    }
    if( wield2 )
    {
      wield2->blaster_setting = BLASTER_LOW;
      send_to_char( "&YDual wielded blaster set to LOW Power\r\n&w", ch );
    }
    return;
  }
  if( !str_cmp( argument, "stun" ) )
  {
    if( wield )
    {
      wield->blaster_setting = BLASTER_STUN;
      send_to_char( "&YWielded blaster set to STUN\r\n&w", ch );
    }
    if( wield2 )
    {
      wield2->blaster_setting = BLASTER_STUN;
      send_to_char( "&YDual wielded blaster set to STUN\r\n&w", ch );
    }
    return;
  }
  else
    do_setblaster( ch, STRLIT_EMPTY );

}

void do_use( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];
  char argd[MAX_INPUT_LENGTH];
  CHAR_DATA *victim;
  OBJ_DATA *device;
  OBJ_DATA *obj;
  ch_ret retcode;

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

  if( !str_cmp( arg, "on" ) )
    argument = one_argument( argument, arg );

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

  if( ( device = get_eq_char( ch, WEAR_HOLD ) ) == NULL ||
      !nifty_is_name( argd, device->name ) )
  {
    return;
  }

  if( device->item_type != ITEM_DEVICE )
  {
    send_to_char
      ( "You can't figure out what it is your supposed to do with it.\r\n",
	ch );
    return;
  }

  if( device->value[2] <= 0 )
  {
    send_to_char( "It has no more charge left.", ch );
    return;
  }

  obj = NULL;
  if( arg[0] == '\0' )
  {
    if( ch->fighting )
    {
      victim = who_fighting( ch );
    }
    else
    {
      send_to_char( "Use on whom or what?\r\n", ch );
      return;
    }
  }
  else
  {
    if( ( victim = get_char_room( ch, arg ) ) == NULL
	&& ( obj = get_obj_here( ch, arg ) ) == NULL )
    {
      send_to_char( "You can't find your target.\r\n", ch );
      return;
    }
  }

  WAIT_STATE( ch, 1 * PULSE_VIOLENCE );

  if( device->value[2] > 0 )
  {
    device->value[2]--;
    if( victim )
    {
      if( !oprog_use_trigger( ch, device, victim, NULL, NULL ) )
      {
	act( AT_MAGIC, "$n uses $p on $N.", ch, device, victim,
	    TO_ROOM );
	act( AT_MAGIC, "You use $p on $N.", ch, device, victim,
	    TO_CHAR );
      }
    }
    else
    {
      if( !oprog_use_trigger( ch, device, NULL, obj, NULL ) )
      {
	act( AT_MAGIC, "$n uses $p on $P.", ch, device, obj, TO_ROOM );
	act( AT_MAGIC, "You use $p on $P.", ch, device, obj, TO_CHAR );
      }
    }

    retcode =
      obj_cast_spell( device->value[3], device->value[0], ch, victim, obj );
    if( retcode == rCHAR_DIED || retcode == rBOTH_DIED )
    {
      bug( "do_use: char died", 0 );
      return;
    }
  }


  return;
}

/*
 * Fill a container
 * Many enhancements added by Thoric (ie: filling non-drink containers)
 */
void do_fill( CHAR_DATA * ch, char *argument )
{
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];
  OBJ_DATA *obj = NULL;
  OBJ_DATA *source = NULL;
  short dest_item = 0, src_item1 = 0, src_item2 = 0, src_item3 =
    0, src_item4 = 0;
  int diff = 0;
  bool all = FALSE;

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

  /* munch optional words */
  if( ( !str_cmp( arg2, "from" ) || !str_cmp( arg2, "with" ) )
      && argument[0] != '\0' )
    argument = one_argument( argument, arg2 );

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

  if( ms_find_obj( ch ) )
    return;

  if( ( obj = get_obj_carry( ch, arg1 ) ) == NULL )
  {
    send_to_char( "You do not have that item.\r\n", ch );
    return;
  }
  else
    dest_item = obj->item_type;

  src_item1 = src_item2 = src_item3 = src_item4 = -1;
  switch ( dest_item )
  {
    default:
      act( AT_ACTION, "$n tries to fill $p... (Don't ask me how)", ch, obj,
	  NULL, TO_ROOM );
      send_to_char( "You cannot fill that.\r\n", ch );
      return;
      /* place all fillable item types here */
    case ITEM_DRINK_CON:
      src_item1 = ITEM_FOUNTAIN;
    case ITEM_CONTAINER:
      src_item1 = ITEM_CONTAINER;
      src_item2 = ITEM_CORPSE_NPC;
      src_item3 = ITEM_CORPSE_PC;
      src_item4 = ITEM_CORPSE_NPC;
      break;
  }

  if( dest_item == ITEM_CONTAINER )
  {
    if( IS_SET( obj->value[1], CONT_CLOSED ) )
    {
      act( AT_PLAIN, "The $d is closed.", ch, NULL, obj->name, TO_CHAR );
      return;
    }
    if( get_obj_weight( obj ) / obj->count >= obj->value[0] )
    {
      send_to_char( "It's already full as it can be.\r\n", ch );
      return;
    }
  }
  else
  {
    diff = obj->value[0] - obj->value[1];
    if( diff < 1 || obj->value[1] >= obj->value[0] )
    {
      send_to_char( "It's already full as it can be.\r\n", ch );
      return;
    }
  }

  if( arg2[0] != '\0' )
  {
    if( dest_item == ITEM_CONTAINER
	&& ( !str_cmp( arg2, "all" ) || !str_prefix( "all.", arg2 ) ) )
    {
      all = TRUE;
      source = NULL;
    }
    else
    {
      if( ( source = get_obj_here( ch, arg2 ) ) == NULL )
      {
	send_to_char( "You cannot find that item.\r\n", ch );
	return;
      }
    }
  }
  else
    source = NULL;

  if( !source )
  {
    bool found = FALSE;
    OBJ_DATA *src_next;

    found = FALSE;
    separate_obj( obj );
    for( source = ch->in_room->first_content; source; source = src_next )
    {
      src_next = source->next_content;
      if( dest_item == ITEM_CONTAINER )
      {
	if( !CAN_WEAR( source, ITEM_TAKE )
	    || ( IS_OBJ_STAT( source, ITEM_PROTOTYPE )
	      && !can_take_proto( ch ) )
	    || ch->carry_weight + get_obj_weight( source ) >
	    can_carry_w( ch )
	    || ( get_obj_weight( source ) +
	      get_obj_weight( obj ) / obj->count ) > obj->value[0] )
	  continue;
	if( all && arg2[3] == '.'
	    && !nifty_is_name( &arg2[4], source->name ) )
	  continue;
	obj_from_room( source );
	if( source->item_type == ITEM_MONEY )
	{
	  ch->gold += source->value[0];
	  extract_obj( source );
	}
	else
	  obj_to_obj( source, obj );
	found = TRUE;
      }
      else
	if( source->item_type == src_item1
	    || source->item_type == src_item2
	    || source->item_type == src_item3
	    || source->item_type == src_item4 )
	{
	  found = TRUE;
	  break;
	}
    }
    if( !found )
    {
      switch ( src_item1 )
      {
	default:
	  send_to_char( "There is nothing appropriate here!\r\n", ch );
	  return;
	case ITEM_FOUNTAIN:
	  send_to_char( "There is no fountain or pool here!\r\n", ch );
	  return;
      }
    }
    if( dest_item == ITEM_CONTAINER )
    {
      act( AT_ACTION, "You fill $p.", ch, obj, NULL, TO_CHAR );
      act( AT_ACTION, "$n fills $p.", ch, obj, NULL, TO_ROOM );
      return;
    }
  }

  if( dest_item == ITEM_CONTAINER )
  {
    OBJ_DATA *otmp, *otmp_next;
    char name[MAX_INPUT_LENGTH];
    CHAR_DATA *gch;
    char *pd;
    bool found = FALSE;

    if( source == obj )
    {
      send_to_char( "You can't fill something with itself!\r\n", ch );
      return;
    }

    switch ( source->item_type )
    {
      default:		/* put something in container */
	if( !source->in_room	/* disallow inventory items */
	    || !CAN_WEAR( source, ITEM_TAKE )
	    || ( IS_OBJ_STAT( source, ITEM_PROTOTYPE )
	      && !can_take_proto( ch ) )
	    || ch->carry_weight + get_obj_weight( source ) >
	    can_carry_w( ch )
	    || ( get_obj_weight( source ) +
	      get_obj_weight( obj ) / obj->count ) > obj->value[0] )
	{
	  send_to_char( "You can't do that.\r\n", ch );
	  return;
	}
	separate_obj( obj );
	act( AT_ACTION, "You take $P and put it inside $p.", ch, obj,
	    source, TO_CHAR );
	act( AT_ACTION, "$n takes $P and puts it inside $p.", ch, obj,
	    source, TO_ROOM );
	obj_from_room( source );
	obj_to_obj( source, obj );
	break;
      case ITEM_MONEY:
	send_to_char( "You can't do that... yet.\r\n", ch );
	break;
      case ITEM_CORPSE_PC:
	if( IS_NPC( ch ) )
	{
	  send_to_char( "You can't do that.\r\n", ch );
	  return;
	}

	pd = source->short_descr;
	pd = one_argument( pd, name );
	pd = one_argument( pd, name );
	pd = one_argument( pd, name );
	pd = one_argument( pd, name );

	if( str_cmp( name, ch->name ) && !IS_IMMORTAL( ch ) )
	{
	  bool fGroup;

	  fGroup = FALSE;
	  for( gch = first_char; gch; gch = gch->next )
	  {
	    if( !IS_NPC( gch )
		&& is_same_group( ch, gch )
		&& !str_cmp( name, gch->name ) )
	    {
	      fGroup = TRUE;
	      break;
	    }
	  }
	  if( !fGroup )
	  {
	    send_to_char( "That's someone else's corpse.\r\n", ch );
	    return;
	  }
	}

      case ITEM_CONTAINER:
	if( source->item_type == ITEM_CONTAINER	/* don't remove */
	    && IS_SET( source->value[1], CONT_CLOSED ) )
	{
	  act( AT_PLAIN, "The $d is closed.", ch, NULL, source->name,
	      TO_CHAR );
	  return;
	}
      case ITEM_DROID_CORPSE:
      case ITEM_CORPSE_NPC:
	if( ( otmp = source->first_content ) == NULL )
	{
	  send_to_char( "It's empty.\r\n", ch );
	  return;
	}
	separate_obj( obj );
	for( ; otmp; otmp = otmp_next )
	{
	  otmp_next = otmp->next_content;

	  if( !CAN_WEAR( otmp, ITEM_TAKE )
	      || ( IS_OBJ_STAT( otmp, ITEM_PROTOTYPE )
		&& !can_take_proto( ch ) )
	      || ch->carry_number + otmp->count > can_carry_n( ch )
	      || ch->carry_weight + get_obj_weight( otmp ) >
	      can_carry_w( ch )
	      || ( get_obj_weight( source ) +
		get_obj_weight( obj ) / obj->count ) > obj->value[0] )
	    continue;
	  obj_from_obj( otmp );
	  obj_to_obj( otmp, obj );
	  found = TRUE;
	}
	if( found )
	{
	  act( AT_ACTION, "You fill $p from $P.", ch, obj, source,
	      TO_CHAR );
	  act( AT_ACTION, "$n fills $p from $P.", ch, obj, source,
	      TO_ROOM );
	}
	else
	  send_to_char( "There is nothing appropriate in there.\r\n", ch );
	break;
    }
    return;
  }

  if( source->value[1] < 1 )
  {
    send_to_char( "There's none left!\r\n", ch );
    return;
  }
  if( source->count > 1 && source->item_type != ITEM_FOUNTAIN )
    separate_obj( source );
  separate_obj( obj );

  switch ( source->item_type )
  {
    default:
      bug( "do_fill: got bad item type: %d", source->item_type );
      send_to_char( "Something went wrong...\r\n", ch );
      return;
    case ITEM_FOUNTAIN:
      if( obj->value[1] != 0 && obj->value[2] != 0 )
      {
	send_to_char( "There is already another liquid in it.\r\n", ch );
	return;
      }
      obj->value[2] = 0;
      obj->value[1] = obj->value[0];
      act( AT_ACTION, "You fill $p from $P.", ch, obj, source, TO_CHAR );
      act( AT_ACTION, "$n fills $p from $P.", ch, obj, source, TO_ROOM );
      return;
    case ITEM_DRINK_CON:
      if( obj->value[1] != 0 && obj->value[2] != source->value[2] )
      {
	send_to_char( "There is already another liquid in it.\r\n", ch );
	return;
      }
      obj->value[2] = source->value[2];
      if( source->value[1] < diff )
	diff = source->value[1];
      obj->value[1] += diff;
      source->value[1] -= diff;
      act( AT_ACTION, "You fill $p from $P.", ch, obj, source, TO_CHAR );
      act( AT_ACTION, "$n fills $p from $P.", ch, obj, source, TO_ROOM );
      return;
  }
}

void do_drink( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];
  OBJ_DATA *obj;
  int amount;
  int liquid;

  argument = one_argument( argument, arg );
  /* munch optional words */
  if( !str_cmp( arg, "from" ) && argument[0] != '\0' )
    argument = one_argument( argument, arg );

  if( arg[0] == '\0' )
  {
    for( obj = ch->in_room->first_content; obj; obj = obj->next_content )
      if( ( obj->item_type == ITEM_FOUNTAIN ) )
	break;

    if( !obj )
    {
      send_to_char( "Drink what?\r\n", ch );
      return;
    }
  }
  else
  {
    if( ( obj = get_obj_here( ch, arg ) ) == NULL )
    {
      send_to_char( "You can't find it.\r\n", ch );
      return;
    }
  }

  if( obj->count > 1 && obj->item_type != ITEM_FOUNTAIN )
    separate_obj( obj );

  if( !IS_NPC( ch ) && ch->pcdata->condition[COND_DRUNK] > 40 )
  {
    send_to_char( "You fail to reach your mouth.  *Hic*\r\n", ch );
    return;
  }

  switch ( obj->item_type )
  {
    default:
      if( obj->carried_by == ch )
      {
	act( AT_ACTION,
	    "$n lifts $p up to $s mouth and tries to drink from it...", ch,
	    obj, NULL, TO_ROOM );
	act( AT_ACTION,
	    "You bring $p up to your mouth and try to drink from it...",
	    ch, obj, NULL, TO_CHAR );
      }
      else
      {
	act( AT_ACTION,
	    "$n gets down and tries to drink from $p... (Is $e feeling ok?)",
	    ch, obj, NULL, TO_ROOM );
	act( AT_ACTION,
	    "You get down on the ground and try to drink from $p...", ch,
	    obj, NULL, TO_CHAR );
      }
      break;

    case ITEM_FOUNTAIN:
      if( !oprog_use_trigger( ch, obj, NULL, NULL, NULL ) )
      {
	act( AT_ACTION, "$n drinks from the fountain.", ch, NULL, NULL,
	    TO_ROOM );
	send_to_char( "You take a long thirst quenching drink.\r\n", ch );
      }

      if( !IS_NPC( ch ) )
	ch->pcdata->condition[COND_THIRST] = 40;
      break;

    case ITEM_DRINK_CON:
      if( obj->value[1] <= 0 )
      {
	send_to_char( "It is already empty.\r\n", ch );
	return;
      }

      if( ( liquid = obj->value[2] ) >= LIQ_MAX )
      {
	bug( "Do_drink: bad liquid number %d.", liquid );
	liquid = obj->value[2] = 0;
      }

      if( !oprog_use_trigger( ch, obj, NULL, NULL, NULL ) )
      {
	act( AT_ACTION, "$n drinks $T from $p.",
	    ch, obj, liq_table[liquid].liq_name, TO_ROOM );
	act( AT_ACTION, "You drink $T from $p.",
	    ch, obj, liq_table[liquid].liq_name, TO_CHAR );
      }

      amount = 1;		/* UMIN(amount, obj->value[1]); */
      /* what was this? concentrated drinks?  concentrated water
	 too I suppose... sheesh! */

      gain_condition( ch, COND_DRUNK,
	  amount * liq_table[liquid].liq_affect[COND_DRUNK] );
      gain_condition( ch, COND_FULL,
	  amount * liq_table[liquid].liq_affect[COND_FULL] );
      gain_condition( ch, COND_THIRST,
	  amount * liq_table[liquid].liq_affect[COND_THIRST] );

      if( !IS_NPC( ch ) )
      {
	if( ch->pcdata->condition[COND_DRUNK] > 24 )
	  send_to_char( "You feel quite sloshed.\r\n", ch );
	else if( ch->pcdata->condition[COND_DRUNK] > 18 )
	  send_to_char( "You feel very drunk.\r\n", ch );
	else if( ch->pcdata->condition[COND_DRUNK] > 12 )
	  send_to_char( "You feel drunk.\r\n", ch );
	else if( ch->pcdata->condition[COND_DRUNK] > 8 )
	  send_to_char( "You feel a little drunk.\r\n", ch );
	else if( ch->pcdata->condition[COND_DRUNK] > 5 )
	  send_to_char( "You feel light headed.\r\n", ch );

	if( ch->pcdata->condition[COND_FULL] > 40 )
	  send_to_char( "You are full.\r\n", ch );

	if( ch->pcdata->condition[COND_THIRST] > 40 )
	  send_to_char( "You feel bloated.\r\n", ch );
	else if( ch->pcdata->condition[COND_THIRST] > 36 )
	  send_to_char( "Your stomach is sloshing around.\r\n", ch );
	else if( ch->pcdata->condition[COND_THIRST] > 30 )
	  send_to_char( "You do not feel thirsty.\r\n", ch );
      }

      if( obj->value[3] )
      {
	/* The drink was poisoned! */
	AFFECT_DATA af;

	act( AT_POISON, "$n sputters and gags.", ch, NULL, NULL, TO_ROOM );
	act( AT_POISON, "You sputter and gag.", ch, NULL, NULL, TO_CHAR );
	ch->mental_state = URANGE( 20, ch->mental_state + 5, 100 );
	af.type = gsn_poison;
	af.duration = 3 * obj->value[3];
	af.location = APPLY_NONE;
	af.modifier = 0;
	af.bitvector = AFF_POISON;
	affect_join( ch, &af );
      }

      obj->value[1] -= amount;
      break;
  }
  WAIT_STATE( ch, PULSE_PER_SECOND );
  return;
}

void do_eat( CHAR_DATA * ch, char *argument )
{
  OBJ_DATA *obj;
  int foodcond;

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

  if( IS_NPC( ch ) || ch->pcdata->condition[COND_FULL] > 5 )
    if( ms_find_obj( ch ) )
      return;

  if( ( obj = find_obj( ch, argument, TRUE ) ) == NULL )
    return;

  if( !IS_IMMORTAL( ch ) )
  {
    if( obj->item_type != ITEM_FOOD )
    {
      act( AT_ACTION,
	  "$n starts to nibble on $p... ($e must really be hungry)", ch,
	  obj, NULL, TO_ROOM );
      act( AT_ACTION, "You try to nibble on $p...", ch, obj, NULL,
	  TO_CHAR );
      return;
    }

    if( !IS_NPC( ch ) && ch->pcdata->condition[COND_FULL] > 40 )
    {
      send_to_char( "You are too full to eat more.\r\n", ch );
      return;
    }
  }

  /* required due to object grouping */
  separate_obj( obj );

  WAIT_STATE( ch, PULSE_PER_SECOND / 2 );

  if( obj->in_obj )
  {
    act( AT_PLAIN, "You take $p from $P.", ch, obj, obj->in_obj, TO_CHAR );
    act( AT_PLAIN, "$n takes $p from $P.", ch, obj, obj->in_obj, TO_ROOM );
  }
  if( !oprog_use_trigger( ch, obj, NULL, NULL, NULL ) )
  {
    if( !obj->action_desc || obj->action_desc[0] == '\0' )
    {
      act( AT_ACTION, "$n eats $p.", ch, obj, NULL, TO_ROOM );
      act( AT_ACTION, "You eat $p.", ch, obj, NULL, TO_CHAR );
    }
    else
      actiondesc( ch, obj, NULL );
  }

  switch ( obj->item_type )
  {

    case ITEM_FOOD:
      if( obj->timer > 0 && obj->value[1] > 0 )
	foodcond = ( obj->timer * 10 ) / obj->value[1];
      else
	foodcond = 10;

      if( !IS_NPC( ch ) )
      {
	int condition = ch->pcdata->condition[COND_FULL];
	gain_condition( ch, COND_FULL, ( obj->value[0] * foodcond ) / 10 );
	if( condition <= 1 && ch->pcdata->condition[COND_FULL] > 1 )
	  send_to_char( "You are no longer hungry.\r\n", ch );
	else if( ch->pcdata->condition[COND_FULL] > 40 )
	  send_to_char( "You are full.\r\n", ch );
      }

      if( obj->value[3] != 0
	  || ( foodcond < 4 && number_range( 0, foodcond + 1 ) == 0 ) )
      {
	/* The food was poisoned! */
	AFFECT_DATA af;

	if( obj->value[3] != 0 )
	{
	  act( AT_POISON, "$n chokes and gags.", ch, NULL, NULL,
	      TO_ROOM );
	  act( AT_POISON, "You choke and gag.", ch, NULL, NULL, TO_CHAR );
	  ch->mental_state = URANGE( 20, ch->mental_state + 5, 100 );
	}
	else
	{
	  act( AT_POISON, "$n gags on $p.", ch, obj, NULL, TO_ROOM );
	  act( AT_POISON, "You gag on $p.", ch, obj, NULL, TO_CHAR );
	  ch->mental_state = URANGE( 15, ch->mental_state + 5, 100 );
	}

	af.type = gsn_poison;
	af.duration = 2 * obj->value[0]
	  * ( obj->value[3] > 0 ? obj->value[3] : 1 );
	af.location = APPLY_NONE;
	af.modifier = 0;
	af.bitvector = AFF_POISON;
	affect_join( ch, &af );
      }
      break;

  }

  if( obj->serial == cur_obj )
    global_objcode = rOBJ_EATEN;
  extract_obj( obj );
  return;
}

void do_empty( CHAR_DATA * ch, char *argument )
{
  OBJ_DATA *obj;
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];

  argument = one_argument( argument, arg1 );
  argument = one_argument( argument, arg2 );
  if( !str_cmp( arg2, "into" ) && argument[0] != '\0' )
    argument = one_argument( argument, arg2 );

  if( arg1[0] == '\0' )
  {
    send_to_char( "Empty what?\r\n", ch );
    return;
  }
  if( ms_find_obj( ch ) )
    return;

  if( ( obj = get_obj_carry( ch, arg1 ) ) == NULL )
  {
    send_to_char( "You aren't carrying that.\r\n", ch );
    return;
  }
  if( obj->count > 1 )
    separate_obj( obj );

  switch ( obj->item_type )
  {
    default:
      act( AT_ACTION, "You shake $p in an attempt to empty it...", ch, obj,
	  NULL, TO_CHAR );
      act( AT_ACTION, "$n begins to shake $p in an attempt to empty it...",
	  ch, obj, NULL, TO_ROOM );
      return;
    case ITEM_DRINK_CON:
      if( obj->value[1] < 1 )
      {
	send_to_char( "It's already empty.\r\n", ch );
	return;
      }
      act( AT_ACTION, "You empty $p.", ch, obj, NULL, TO_CHAR );
      act( AT_ACTION, "$n empties $p.", ch, obj, NULL, TO_ROOM );
      obj->value[1] = 0;
      return;
    case ITEM_CONTAINER:
      if( IS_SET( obj->value[1], CONT_CLOSED ) )
      {
	act( AT_PLAIN, "The $d is closed.", ch, NULL, obj->name, TO_CHAR );
	return;
      }
      if( !obj->first_content )
      {
	send_to_char( "It's already empty.\r\n", ch );
	return;
      }
      if( arg2[0] == '\0' )
      {
	if( !IS_NPC( ch ) && IS_SET( ch->act, PLR_LITTERBUG ) )
	{
	  set_char_color( AT_MAGIC, ch );
	  send_to_char( "A magical force stops you!\r\n", ch );
	  set_char_color( AT_TELL, ch );
	  send_to_char( "Someone tells you, 'No littering here!'\r\n",
	      ch );
	  return;
	}
	if( empty_obj( obj, NULL, ch->in_room ) )
	{
	  act( AT_ACTION, "You empty $p.", ch, obj, NULL, TO_CHAR );
	  act( AT_ACTION, "$n empties $p.", ch, obj, NULL, TO_ROOM );
	  if( IS_SET( sysdata.save_flags, SV_DROP ) )
	    save_char_obj( ch );
	}
	else
	  send_to_char( "Hmmm... didn't work.\r\n", ch );
      }
      else
      {
	OBJ_DATA *dest = get_obj_here( ch, arg2 );

	if( !dest )
	{
	  send_to_char( "You can't find it.\r\n", ch );
	  return;
	}
	if( dest == obj )
	{
	  send_to_char( "You can't empty something into itself!\r\n",
	      ch );
	  return;
	}
	if( dest->item_type != ITEM_CONTAINER )
	{
	  send_to_char( "That's not a container!\r\n", ch );
	  return;
	}
	if( IS_SET( dest->value[1], CONT_CLOSED ) )
	{
	  act( AT_PLAIN, "The $d is closed.", ch, NULL, dest->name,
	      TO_CHAR );
	  return;
	}
	separate_obj( dest );
	if( empty_obj( obj, dest, NULL ) )
	{
	  act( AT_ACTION, "You empty $p into $P.", ch, obj, dest,
	      TO_CHAR );
	  act( AT_ACTION, "$n empties $p into $P.", ch, obj, dest,
	      TO_ROOM );
	  if( !dest->carried_by && IS_SET( sysdata.save_flags, SV_PUT ) )
	    save_char_obj( ch );
	}
	else
	  act( AT_ACTION, "$P is too full.", ch, obj, dest, TO_CHAR );
      }
      return;
  }
}

void actiondesc( CHAR_DATA * ch, const OBJ_DATA * obj, void *vo )
{
  char charbuf[MAX_STRING_LENGTH];
  char roombuf[MAX_STRING_LENGTH];
  char *srcptr = obj->action_desc;
  char *charptr = charbuf;
  char *roomptr = roombuf;
  const char *ichar = NULL;
  const char *iroom = NULL;

  while( *srcptr != '\0' )
  {
    if( *srcptr == '$' )
    {
      srcptr++;
      switch ( *srcptr )
      {
	case 'e':
	  ichar = "you";
	  iroom = "$e";
	  break;

	case 'm':
	  ichar = "you";
	  iroom = "$m";
	  break;

	case 'n':
	  ichar = "you";
	  iroom = "$n";
	  break;

	case 's':
	  ichar = "your";
	  iroom = "$s";
	  break;

	  /*case 'q':
	    iroom = "s";
	    break; */

	default:
	  srcptr--;
	  *charptr++ = *srcptr;
	  *roomptr++ = *srcptr;
	  break;
      }
    }
    else if( *srcptr == '%' && *++srcptr == 's' )
    {
      ichar = "You";
      iroom = "$n";
    }
    else
    {
      *charptr++ = *srcptr;
      *roomptr++ = *srcptr;
      srcptr++;
      continue;
    }

    while( ( *charptr = *ichar ) != '\0' )
    {
      charptr++;
      ichar++;
    }

    while( ( *roomptr = *iroom ) != '\0' )
    {
      roomptr++;
      iroom++;
    }

    srcptr++;
  }

  *charptr = '\0';
  *roomptr = '\0';

  /*
     sprintf( buf, "Charbuf: %s", charbuf );
     log_string_plus( buf, LOG_HIGH, LEVEL_LESSER ); 
     sprintf( buf, "Roombuf: %s", roombuf );
     log_string_plus( buf, LOG_HIGH, LEVEL_LESSER ); 
     */

  switch ( obj->item_type )
  {
    case ITEM_FOUNTAIN:
      act( AT_ACTION, charbuf, ch, obj, ch, TO_CHAR );
      act( AT_ACTION, roombuf, ch, obj, ch, TO_ROOM );
      return;

    case ITEM_DRINK_CON:
      act( AT_ACTION, charbuf, ch, obj, liq_table[obj->value[2]].liq_name,
	  TO_CHAR );
      act( AT_ACTION, roombuf, ch, obj, liq_table[obj->value[2]].liq_name,
	  TO_ROOM );
      return;

    case ITEM_ARMOR:
    case ITEM_WEAPON:
    case ITEM_LIGHT:
      act( AT_ACTION, charbuf, ch, obj, ch, TO_CHAR );
      act( AT_ACTION, roombuf, ch, obj, ch, TO_ROOM );
      return;

    case ITEM_FOOD:
      act( AT_ACTION, charbuf, ch, obj, ch, TO_CHAR );
      act( AT_ACTION, roombuf, ch, obj, ch, TO_ROOM );
      return;

    default:
      return;
  }
}

void do_hail( CHAR_DATA * ch, char *argument )
{
  int vnum = 0;
  ROOM_INDEX_DATA *room = NULL;

  if( !ch->in_room )
    return;

  if( !ch->in_room->area || !ch->in_room->area->planet )
  {
    send_to_char( "There doesn't seem to be any taxis nearby!\r\n", ch );
    return;
  }

  if( ch->position < POS_FIGHTING )
  {
    send_to_char( "You might want to stop fighting first!\r\n", ch );
    return;
  }

  if( ch->position < POS_STANDING )
  {
    send_to_char( "You might want to stand up first!\r\n", ch );
    return;
  }

  if( IS_SET( ch->in_room->room_flags, ROOM_INDOORS ) )
  {
    send_to_char( "You'll have to go outside to do that!\r\n", ch );
    return;
  }

  if( IS_SET( ch->in_room->room_flags, ROOM_SPACECRAFT ) )
  {
    send_to_char( "You can't do that on spacecraft!\r\n", ch );
    return;
  }

  if( ch->gold < ( ch->top_level - 9 ) )
  {
    send_to_char( "You don't have enough credits!\r\n", ch );
    return;
  }

  vnum = ch->in_room->vnum;

  for( room = ch->in_room->area->first_room; room; room = room->next_in_area )
  {
    if( IS_SET( room->room_flags, ROOM_HOTEL )
	&& !IS_SET( room->room_flags, ROOM_PLR_HOME ) )
      break;
  }

  if( room == NULL || !IS_SET( room->room_flags, ROOM_HOTEL ) )
  {
    send_to_char( "There doesn't seem to be any taxis nearby!\r\n", ch );
    return;
  }

  ch->gold -= UMAX( ch->top_level - 9, 0 );

  act( AT_ACTION, "$n hails a speederbike, and drives off to seek shelter.",
      ch, NULL, NULL, TO_ROOM );

  char_from_room( ch );
  char_to_room( ch, room );

  send_to_char
    ( "A speederbike picks you up and drives you to a safe location.\r\nYou pay the driver 20 credits.\r\n\n\n",
      ch );
  act( AT_ACTION, "$n $T", ch, NULL,
      "arrives on a speederbike, gets off and pays the driver before it leaves.",
      TO_ROOM );

  do_look( ch, STRLIT_AUTO );
}

void do_suicide( CHAR_DATA * ch, char *argument )
{
  char logbuf[MAX_STRING_LENGTH];

  if( IS_NPC( ch ) || !ch->pcdata )
  {
    send_to_char( "Yeah right!\r\n", ch );
    return;
  }

  if( argument[0] == '\0' )
  {
    send_to_char
      ( "&RIf you really want to delete this character type suicide and your password.\r\n",
	ch );
    return;
  }

  if( strcmp( encode_string( argument ), ch->pcdata->pwd ) )
  {
    send_to_char( "Sorry wrong password.\r\n", ch );
    sprintf( logbuf, "%s attempting to commit suicide... WRONG PASSWORD!",
	ch->name );
    log_string( logbuf );
    return;
  }

  act( AT_BLOOD,
      "With a sad determination and trembling hands you slit your own throat!",
      ch, NULL, NULL, TO_CHAR );
  act( AT_BLOOD,
      "Cold shivers run down your spine as you watch $n slit $s own throat!",
      ch, NULL, NULL, TO_ROOM );

  sprintf( logbuf, "%s has committed suicide..", ch->name );
  log_string( logbuf );

  set_cur_char( ch );
  raw_kill( ch, ch );
}

void do_bank( CHAR_DATA * ch, char *argument )
{
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];
  long amount = 0;

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

  if( IS_NPC( ch ) || !ch->pcdata )
    return;

  if( !ch->in_room || !IS_SET( ch->in_room->room_flags, ROOM_BANK ) )
  {
    send_to_char( "You must be in a bank to do that!\r\n", ch );
    return;
  }

  if( arg1[0] == '\0' )
  {
    send_to_char( "Usage: BANK <deposit|withdraw|balance> [amount]\r\n",
	ch );
    return;
  }

  if( arg2[0] != '\0' )
    amount = atoi( arg2 );

  if( !str_prefix( arg1, "deposit" ) )
  {
    if( amount <= 0 )
    {
      send_to_char( "You may only deposit amounts greater than zero.\r\n",
	  ch );
      do_bank( ch, STRLIT_EMPTY );
      return;
    }

    if( ch->gold < amount )
    {
      send_to_char( "You don't have that many credits on you.\r\n", ch );
      return;
    }

    ch->gold -= amount;
    ch->pcdata->bank += amount;

    ch_printf( ch, "You deposit %ld credits into your account.\r\n",
	amount );
    return;
  }
  else if( !str_prefix( arg1, "withdraw" ) )
  {
    if( amount <= 0 )
    {
      send_to_char
	( "You may only withdraw amounts greater than zero.\r\n", ch );
      do_bank( ch, STRLIT_EMPTY );
      return;
    }

    if( ch->pcdata->bank < amount )
    {
      send_to_char
	( "You don't have that many credits in your account.\r\n", ch );
      return;
    }

    ch->gold += amount;
    ch->pcdata->bank -= amount;

    ch_printf( ch, "You withdraw %ld credits from your account.\r\n",
	amount );
    return;

  }
  else if( !str_prefix( arg1, "balance" ) )
  {
    ch_printf( ch, "You have %ld credits in your account.\r\n",
	ch->pcdata->bank );
    return;
  }
  else
  {
    do_bank( ch, STRLIT_EMPTY );
    return;
  }
}

void do_dig( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];
  OBJ_DATA *obj;
  OBJ_DATA *startobj;
  bool found, shovel;
  EXIT_DATA *pexit;

  switch ( ch->substate )
  {
    default:
      if( IS_NPC( ch ) && IS_AFFECTED( ch, AFF_CHARM ) )
      {
	send_to_char( "You can't concentrate enough for that.\r\n", ch );
	return;
      }
      if( ch->mount )
      {
	send_to_char( "You can't do that while mounted.\r\n", ch );
	return;
      }
      one_argument( argument, arg );
      if( arg[0] != '\0' )
      {
	if( ( pexit = find_door( ch, arg, TRUE ) ) == NULL
	    && get_dir( arg ) == -1 )
	{
	  send_to_char( "What direction is that?\r\n", ch );
	  return;
	}
	if( pexit )
	{
	  if( !IS_SET( pexit->exit_info, EX_DIG )
	      && !IS_SET( pexit->exit_info, EX_CLOSED ) )
	  {
	    send_to_char( "There is no need to dig out that exit.\r\n",
		ch );
	    return;
	  }
	}
      }
      else
      {
	switch ( ch->in_room->sector_type )
	{
	  case SECT_CITY:
	  case SECT_INSIDE:
	    send_to_char( "The floor is too hard to dig through.\r\n", ch );
	    return;
	  case SECT_WATER_SWIM:
	  case SECT_WATER_NOSWIM:
	  case SECT_UNDERWATER:
	    send_to_char( "You cannot dig here.\r\n", ch );
	    return;
	  case SECT_AIR:
	    send_to_char( "What?  In the air?!\r\n", ch );
	    return;
	}
      }
      add_timer( ch, TIMER_DO_FUN, 3, do_dig, 1 );
      ch->dest_buf = str_dup( arg );
      send_to_char( "You begin digging...\r\n", ch );
      act( AT_PLAIN, "$n begins digging...", ch, NULL, NULL, TO_ROOM );
      return;

    case 1:
      if( !ch->dest_buf )
      {
	send_to_char( "Your digging was interrupted!\r\n", ch );
	act( AT_PLAIN, "$n's digging was interrupted!", ch, NULL, NULL,
	    TO_ROOM );
	bug( "do_dig: dest_buf NULL", 0 );
	return;
      }
      strcpy( arg, ( const char * ) ch->dest_buf );
      DISPOSE( ch->dest_buf );
      break;

    case SUB_TIMER_DO_ABORT:
      DISPOSE( ch->dest_buf );
      ch->substate = SUB_NONE;
      send_to_char( "You stop digging...\r\n", ch );
      act( AT_PLAIN, "$n stops digging...", ch, NULL, NULL, TO_ROOM );
      return;
  }

  ch->substate = SUB_NONE;

  if( number_percent() == 23 )
  {
    send_to_char( "You feel a little bit stronger...\r\n", ch );
    ch->perm_str++;
    ch->perm_str = UMIN( ch->perm_str, 25 );
  }

  /* not having a shovel makes it harder to succeed */
  shovel = get_obj_type_char( ch, ITEM_SHOVEL ) ? TRUE : FALSE;

  /* dig out an EX_DIG exit... */
  if( arg[0] != '\0' )
  {
    if( ( pexit = find_door( ch, arg, TRUE ) ) != NULL
	&& IS_SET( pexit->exit_info, EX_DIG )
	&& IS_SET( pexit->exit_info, EX_CLOSED ) )
    {
      /* 4 times harder to dig open a passage without a shovel */
      if( ( number_percent() * ( shovel ? 1 : 4 ) ) < 80 )
      {
	REMOVE_BIT( pexit->exit_info, EX_CLOSED );
	send_to_char( "You dig open a passageway!\r\n", ch );
	act( AT_PLAIN, "$n digs open a passageway!", ch, NULL, NULL,
	    TO_ROOM );
	return;
      }
    }
    send_to_char( "Your dig did not discover any exit...\r\n", ch );
    act( AT_PLAIN, "$n's dig did not discover any exit...", ch, NULL, NULL,
	TO_ROOM );
    return;
  }

  startobj = ch->in_room->first_content;
  found = FALSE;

  for( obj = startobj; obj; obj = obj->next_content )
  {
    /* twice as hard to find something without a shovel */
    if( IS_OBJ_STAT( obj, ITEM_BURRIED )
	&& ( number_percent() * ( shovel ? 1 : 2 ) ) < 80 )
    {
      found = TRUE;
      break;
    }
  }

  if( !found )
  {
    send_to_char( "Your dig uncovered nothing.\r\n", ch );
    act( AT_PLAIN, "$n's dig uncovered nothing.", ch, NULL, NULL, TO_ROOM );
    return;
  }

  separate_obj( obj );
  REMOVE_BIT( obj->extra_flags, ITEM_BURRIED );
  act( AT_SKILL, "Your dig uncovered $p!", ch, obj, NULL, TO_CHAR );
  act( AT_SKILL, "$n's dig uncovered $p!", ch, obj, NULL, TO_ROOM );

  return;
}


void do_search( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];
  OBJ_DATA *obj;
  OBJ_DATA *container;
  OBJ_DATA *startobj;
  int percent, door;
  bool found, room;

  door = -1;
  switch ( ch->substate )
  {
    default:
      if( IS_NPC( ch ) && IS_AFFECTED( ch, AFF_CHARM ) )
      {
	send_to_char( "You can't concentrate enough for that.\r\n", ch );
	return;
      }
      if( ch->mount )
      {
	send_to_char( "You can't do that while mounted.\r\n", ch );
	return;
      }
      argument = one_argument( argument, arg );
      if( arg[0] != '\0' && ( door = get_door( arg ) ) == -1 )
      {
	container = get_obj_here( ch, arg );
	if( !container )
	{
	  send_to_char( "You can't find that here.\r\n", ch );
	  return;
	}
	if( container->item_type != ITEM_CONTAINER )
	{
	  send_to_char( "You can't search in that!\r\n", ch );
	  return;
	}
	if( IS_SET( container->value[1], CONT_CLOSED ) )
	{
	  send_to_char( "It is closed.\r\n", ch );
	  return;
	}
      }
      add_timer( ch, TIMER_DO_FUN, 3, do_search, 1 );
      send_to_char( "You begin your search...\r\n", ch );
      ch->dest_buf = str_dup( arg );
      return;

    case 1:
      if( !ch->dest_buf )
      {
	send_to_char( "Your search was interrupted!\r\n", ch );
	bug( "do_search: dest_buf NULL", 0 );
	return;
      }
      strcpy( arg, ( const char * ) ch->dest_buf );
      DISPOSE( ch->dest_buf );
      break;
    case SUB_TIMER_DO_ABORT:
      DISPOSE( ch->dest_buf );
      ch->substate = SUB_NONE;
      send_to_char( "You stop your search...\r\n", ch );
      return;
  }

  if( number_percent() == 23 )
  {
    send_to_char( "You feel a little bit wiser...\r\n", ch );
    ch->perm_wis++;
    ch->perm_wis = UMIN( ch->perm_wis, 25 );
  }

  ch->substate = SUB_NONE;
  if( arg[0] == '\0' )
  {
    room = TRUE;
    startobj = ch->in_room->first_content;
  }
  else
  {
    if( ( door = get_door( arg ) ) != -1 )
      startobj = NULL;
    else
    {
      container = get_obj_here( ch, arg );
      if( !container )
      {
	send_to_char( "You can't find that here.\r\n", ch );
	return;
      }
      startobj = container->first_content;
    }
  }

  found = FALSE;

  if( ( !startobj && door == -1 ) || IS_NPC( ch ) )
  {
    send_to_char( "You find nothing.\r\n", ch );
    return;
  }

  percent = number_percent();

  if( door != -1 )
  {
    EXIT_DATA *pexit;

    if( ( pexit = get_exit( ch->in_room, door ) ) != NULL
	&& IS_SET( pexit->exit_info, EX_SECRET )
	&& IS_SET( pexit->exit_info, EX_xSEARCHABLE ) && percent < 80 )
    {
      act( AT_SKILL, "Your search reveals the $d!", ch, NULL,
	  pexit->keyword, TO_CHAR );
      act( AT_SKILL, "$n finds the $d!", ch, NULL, pexit->keyword,
	  TO_ROOM );
      REMOVE_BIT( pexit->exit_info, EX_SECRET );
      return;
    }
  }
  else
    for( obj = startobj; obj; obj = obj->next_content )
    {
      if( IS_OBJ_STAT( obj, ITEM_HIDDEN ) && percent < 70 )
      {
	found = TRUE;
	break;
      }
    }

  if( !found )
  {
    send_to_char( "You find nothing.\r\n", ch );
    return;
  }

  separate_obj( obj );
  REMOVE_BIT( obj->extra_flags, ITEM_HIDDEN );
  act( AT_SKILL, "Your search reveals $p!", ch, obj, NULL, TO_CHAR );
  act( AT_SKILL, "$n finds $p!", ch, obj, NULL, TO_ROOM );
  return;
}

void do_shove( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];
  int exit_dir = DIR_SOMEWHERE;
  EXIT_DATA *pexit = NULL;
  CHAR_DATA *victim = NULL;
  bool nogo = FALSE;
  ROOM_INDEX_DATA *to_room = NULL;
  int chance = 0;

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

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

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

  if( victim == ch )
    {
      send_to_char( "You shove yourself around, to no avail.\r\n", ch );
      return;
    }

  if( ( victim->position ) != POS_STANDING )
    {
      act( AT_PLAIN, "$N isn't standing up.", ch, NULL, victim, TO_CHAR );
      return;
    }

  if( arg2[0] == '\0' )
    {
      send_to_char( "Shove them in which direction?\r\n", ch );
      return;
    }

  exit_dir = get_dir( arg2 );

  if( IS_SET( victim->in_room->room_flags, ROOM_SAFE )
      && get_timer( victim, TIMER_SHOVEDRAG ) <= 0 )
    {
      send_to_char( "That character cannot be shoved right now.\r\n", ch );
      return;
    }

  victim->position = POS_SHOVE;
  nogo = FALSE;

  if( ( pexit = get_exit( ch->in_room, exit_dir ) ) == NULL )
    nogo = TRUE;
  else
    if( IS_SET( pexit->exit_info, EX_CLOSED )
        && ( !IS_AFFECTED( victim, AFF_PASS_DOOR )
	     || IS_SET( pexit->exit_info, EX_NOPASSDOOR ) ) )
      nogo = TRUE;

  if( nogo )
    {
      send_to_char( "There's no exit in that direction.\r\n", ch );
      victim->position = POS_STANDING;
      return;
    }

  to_room = pexit->to_room;

  if( IS_NPC( victim ) )
    {
      send_to_char( "You can only shove player characters.\r\n", ch );
      return;
    }

  chance = 50;

  /* Add 3 points to chance for every str point above 15, subtract for
     below 15 */
  chance += ( ( get_curr_str( ch ) - 15 ) * 3 );
  chance += ( ch->top_level - victim->top_level );

  /* Debugging purposes - show percentage for testing */

  /* sprintf(buf, "Shove percentage of %s = %d", ch->name, chance);
     act( AT_ACTION, buf, ch, NULL, NULL, TO_ROOM );
  */

  if( chance < number_percent() )
    {
      send_to_char( "You failed.\r\n", ch );
      victim->position = POS_STANDING;
      return;
    }

  act( AT_ACTION, "You shove $M.", ch, NULL, victim, TO_CHAR );
  act( AT_ACTION, "$n shoves you.", ch, NULL, victim, TO_VICT );
  move_char( victim, get_exit( ch->in_room, exit_dir ), 0 );

  if( !char_died( victim ) )
    victim->position = POS_STANDING;

  WAIT_STATE( ch, 12 );

  /* Remove protection from shove/drag if char shoves -- Blodkai */
  if( IS_SET( ch->in_room->room_flags, ROOM_SAFE )
      && get_timer( ch, TIMER_SHOVEDRAG ) <= 0 )
    add_timer( ch, TIMER_SHOVEDRAG, 10, NULL, 0 );
}

void do_drag( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];
  int exit_dir = DIR_SOMEWHERE;
  CHAR_DATA *victim = NULL;
  EXIT_DATA *pexit = NULL;
  ROOM_INDEX_DATA *to_room = NULL;
  bool nogo = FALSE;
  int chance = 0;

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

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

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

  if( victim == ch )
    {
    send_to_char
      ( "You take yourself by the scruff of your neck, but go nowhere.\r\n",
        ch );
    return;
    }

  if( IS_NPC( victim ) )
    {
      send_to_char( "You can only drag player characters.\r\n", ch );
      return;
    }

  if( victim->fighting )
    {
      send_to_char( "You try, but can't get close enough.\r\n", ch );
      return;
    }

  if( arg2[0] == '\0' )
    {
      send_to_char( "Drag them in which direction?\r\n", ch );
      return;
    }

  exit_dir = get_dir( arg2 );

  if( IS_SET( victim->in_room->room_flags, ROOM_SAFE )
      && get_timer( victim, TIMER_SHOVEDRAG ) <= 0 )
    {
      send_to_char( "That character cannot be dragged right now.\r\n", ch );
      return;
    }

  nogo = FALSE;

  if( ( pexit = get_exit( ch->in_room, exit_dir ) ) == NULL )
    nogo = TRUE;
  else
    if( IS_SET( pexit->exit_info, EX_CLOSED )
        && ( !IS_AFFECTED( victim, AFF_PASS_DOOR )
	     || IS_SET( pexit->exit_info, EX_NOPASSDOOR ) ) )
      nogo = TRUE;
  if( nogo )
    {
      send_to_char( "There's no exit in that direction.\r\n", ch );
      return;
    }

  to_room = pexit->to_room;
  chance = 50;

  /*
     sprintf(buf, "Drag percentage of %s = %d", ch->name, chance);
     act( AT_ACTION, buf, ch, NULL, NULL, TO_ROOM );
  */
  if( chance < number_percent() )
    {
      send_to_char( "You failed.\r\n", ch );
      victim->position = POS_STANDING;
      return;
    }
  if( victim->position < POS_STANDING )
    {
      short temp = victim->position;
      victim->position = POS_DRAG;
      act( AT_ACTION, "You drag $M into the next room.", ch, NULL, victim,
	   TO_CHAR );
      act( AT_ACTION, "$n grabs your hair and drags you.", ch, NULL, victim,
	   TO_VICT );
      move_char( victim, get_exit( ch->in_room, exit_dir ), 0 );
      if( !char_died( victim ) )
	victim->position = temp;
      /* Move ch to the room too.. they are doing dragging - Scryn */
      move_char( ch, get_exit( ch->in_room, exit_dir ), 0 );
      WAIT_STATE( ch, 12 );
      return;
    }
  send_to_char( "You cannot do that to someone who is standing.\r\n", ch );
}