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

/*
 MurkMUD++ - A Windows compatible, C++ compatible Merc 2.2 Mud.

 \author Jon A. Lambert
 \date 01/02/2007
 \version 1.5
 \remarks
  This source code copyright (C) 2005, 2006, 2007 by Jon A. Lambert
  All rights reserved.

  Use governed by the MurkMUD++ public license found in license.murk++
*/

#include "os.hpp"
#include "config.hpp"
#include "globals.hpp"
#include "io.hpp"
#include "utils.hpp"

#include "pcdata.hpp"
#include "affect.hpp"
#include "area.hpp"
#include "room.hpp"
#include "mobproto.hpp"
#include "objproto.hpp"
#include "exit.hpp"
#include "extra.hpp"
#include "object.hpp"
#include "descriptor.hpp"
#include "ban.hpp"
#include "shop.hpp"
#include "reset.hpp"
#include "note.hpp"
#include "database.hpp"
#include "world.hpp"

// Temp externs
extern Room *find_location (Character * ch, const std::string & arg);
extern Character *find_keeper (Character * ch);
extern int get_cost (Character * keeper, Object * obj, bool fBuy);
extern std::string extra_bit_name (int extra_flags);
extern void obj_cast_spell (int sn, int level, Character * ch, Character * victim,
  Object * obj);
extern void talk_channel (Character * ch, const std::string & argument, int channel,
  const char *verb);
extern void multi_hit(Character *ch, Character *victim, int dt);
extern void say_spell (Character * ch, int sn);
extern void raw_kill (Character * victim);
extern bool is_same_group (Character * ach, Character * bch);
extern std::string affect_loc_name (int location);
extern std::string affect_bit_name (int vector);
extern void mprog_bribe_trigger (Character * mob, Character * ch, int amount);
extern void mprog_speech_trigger (const std::string & txt, Character * mob);
extern void mprog_give_trigger (Character * mob, Character * ch, Object * obj);
extern Object *create_money (int amount);
extern void note_attach (Character * ch);
extern void note_remove (Character * ch, Note * pnote);
extern void damage(Character *ch, Character *victim, int dam, int dt);
extern void check_killer (Character * ch, Character * victim);
extern std::string get_extra_descr (const std::string & name, std::list<ExtraDescription *> & ed);
extern bool is_safe (Character * ch, Character * victim);
extern void disarm (Character * ch, Character * victim);

void Character::do_areas (std::string argument)
{
  send_to_char (g_world->list_areas());
  return;
}

void Character::do_memory (std::string argument)
{
  char buf[MAX_STRING_LENGTH];

  snprintf (buf, sizeof buf, "Affects %5d\r\n", Affect::top_affect);
  send_to_char (buf);
  snprintf (buf, sizeof buf, "Areas   %5d\r\n", Area::top_area);
  send_to_char (buf);
  snprintf (buf, sizeof buf, "ExDes   %5d\r\n", ExtraDescription::top_ed);
  send_to_char (buf);
  snprintf (buf, sizeof buf, "Exits   %5d\r\n", Exit::top_exit);
  send_to_char (buf);
  snprintf (buf, sizeof buf, "Mobs    %5d\r\n", MobPrototype::top_mob);
  send_to_char (buf);
  snprintf (buf, sizeof buf, "Objs    %5d\r\n", ObjectPrototype::top_obj);
  send_to_char (buf);
  snprintf (buf, sizeof buf, "Resets  %5d\r\n", Reset::top_reset);
  send_to_char (buf);
  snprintf (buf, sizeof buf, "Rooms   %5d\r\n", Room::top_room);
  send_to_char (buf);
  snprintf (buf, sizeof buf, "Shops   %5d\r\n", Shop::top_shop);
  send_to_char (buf);
  return;
}

void Character::do_kill (std::string argument)
{
  std::string arg;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Kill whom?\r\n");
    return;
  }

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

  if (!victim->is_npc ()) {
    if (!IS_SET (victim->actflags, PLR_KILLER)
      && !IS_SET (victim->actflags, PLR_THIEF)) {
      send_to_char ("You must MURDER a player.\r\n");
      return;
    }
  } else {
    if (victim->is_affected (AFF_CHARM) && victim->master != NULL) {
      send_to_char ("You must MURDER a charmed creature.\r\n");
      return;
    }
  }

  if (victim == this) {
    send_to_char ("You hit yourself.  Ouch!\r\n");
    multi_hit (this, this, TYPE_UNDEFINED);
    return;
  }

  if (is_safe (this, victim))
    return;

  if (is_affected (AFF_CHARM) && master == victim) {
    act ("$N is your beloved master.", NULL, victim, TO_CHAR);
    return;
  }

  if (position == POS_FIGHTING) {
    send_to_char ("You do the best you can!\r\n");
    return;
  }

  wait_state (1 * PULSE_VIOLENCE);
  check_killer (this, victim);
  multi_hit (this, victim, TYPE_UNDEFINED);
  return;
}

void Character::do_murde (std::string argument)
{
  send_to_char ("If you want to MURDER, spell it out.\r\n");
  return;
}

void Character::do_murder (std::string argument)
{
  std::string arg, buf;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Murder whom?\r\n");
    return;
  }

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

  if (victim == this) {
    send_to_char ("Suicide is a mortal sin.\r\n");
    return;
  }

  if (is_safe (this, victim))
    return;

  if (is_affected (AFF_CHARM) && master == victim) {
    act ("$N is your beloved master.", NULL, victim, TO_CHAR);
    return;
  }

  if (position == POS_FIGHTING) {
    send_to_char ("You do the best you can!\r\n");
    return;
  }

  wait_state (1 * PULSE_VIOLENCE);
  buf += "Help!  I am being attacked by " + name + "!";
  victim->do_shout (buf);
  check_killer (this, victim);
  multi_hit (this, victim, TYPE_UNDEFINED);
  return;
}

void Character::do_backstab (std::string argument)
{
  std::string arg;

  if (!is_npc ()
    && level < skill_table[skill_lookup("backstab")].skill_level[klass]) {
    send_to_char ("You better leave the assassin trade to thieves.\r\n");
    return;
  }

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Backstab whom?\r\n");
    return;
  }

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

  if (victim == this) {
    send_to_char ("How can you sneak up on yourself?\r\n");
    return;
  }

  if (is_safe (this, victim))
    return;

  Object *obj;
  if ((obj = get_eq_char (WEAR_WIELD)) == NULL || obj->value[3] != 11) {
    send_to_char ("You need to wield a piercing weapon.\r\n");
    return;
  }

  if (victim->fighting != NULL) {
    send_to_char ("You can't backstab a fighting person.\r\n");
    return;
  }

  if (victim->hit < victim->max_hit) {
    act ("$N is hurt and suspicious ... you can't sneak up.",
      NULL, victim, TO_CHAR);
    return;
  }

  check_killer (this, victim);
  int bck = skill_lookup("backstab");
  wait_state (skill_table[bck].beats);
  if (!victim->is_awake ()
    || is_npc ()
    || number_percent () < pcdata->learned[bck])
    multi_hit (this, victim, bck);
  else
    damage (this, victim, 0, bck);

  return;
}

void Character::do_flee (std::string argument)
{
  if (fighting == NULL) {
    if (position == POS_FIGHTING)
      position = POS_STANDING;
    send_to_char ("You aren't fighting anyone.\r\n");
    return;
  }

  Room *now_in;
  Room* was_in = in_room;
  for (int attempt = 0; attempt < 6; attempt++) {
    Exit *pexit;
    int door;

    door = number_door ();
    if ((pexit = was_in->exit[door]) == 0
      || pexit->to_room == NULL || IS_SET (pexit->exit_info, EX_CLOSED)
      || (is_npc ()
        && (IS_SET (pexit->to_room->room_flags, ROOM_NO_MOB)
          || (IS_SET (actflags, ACT_STAY_AREA)
            && pexit->to_room->area != in_room->area))))
      continue;

    move_char (door);
    if ((now_in = in_room) == was_in)
      continue;

    in_room = was_in;
    act ("$n has fled!", NULL, NULL, TO_ROOM);
    in_room = now_in;

    if (!is_npc ()) {
      send_to_char ("You flee from combat!  You lose 25 exps.\r\n");
      gain_exp(-25);
    }

    stop_fighting(true);
    return;
  }

  send_to_char ("You failed!  You lose 10 exps.\r\n");
  gain_exp(-10);
  return;
}

void Character::do_rescue (std::string argument)
{
  if (!is_npc ()
    && level < skill_table[skill_lookup("rescue")].skill_level[klass]) {
    send_to_char ("You better leave the heroic acts to warriors.\r\n");
    return;
  }

  std::string arg;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Rescue whom?\r\n");
    return;
  }

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

  if (victim == this) {
    send_to_char ("What about fleeing instead?\r\n");
    return;
  }

  if (!is_npc () && victim->is_npc ()) {
    send_to_char ("Doesn't need your help!\r\n");
    return;
  }

  if (fighting == victim) {
    send_to_char ("Too late.\r\n");
    return;
  }

  Character *fch;
  if ((fch = victim->fighting) == NULL) {
    send_to_char ("That person is not fighting right now.\r\n");
    return;
  }

  int rsc = skill_lookup("rescue");
  wait_state (skill_table[rsc].beats);
  if (!is_npc () && number_percent () > pcdata->learned[rsc]) {
    send_to_char ("You fail the rescue.\r\n");
    return;
  }

  act ("You rescue $N!", NULL, victim, TO_CHAR);
  act ("$n rescues you!", NULL, victim, TO_VICT);
  act ("$n rescues $N!", NULL, victim, TO_NOTVICT);

  fch->stop_fighting(false);
  victim->stop_fighting(false);

  check_killer (this, fch);
  set_fighting(fch);
  fch->set_fighting(this);
  return;
}

void Character::do_kick (std::string argument)
{
  if (!is_npc ()
    && level < skill_table[skill_lookup("kick")].skill_level[klass]) {
    send_to_char ("You better leave the martial arts to fighters.\r\n");
    return;
  }

  Character *victim;

  if ((victim = fighting) == NULL) {
    send_to_char ("You aren't fighting anyone.\r\n");
    return;
  }

  int kck = skill_lookup("kick");
  wait_state (skill_table[kck].beats);
  if (is_npc () || number_percent () < pcdata->learned[kck])
    damage (this, victim, number_range (1, level), kck);
  else
    damage (this, victim, 0, kck);

  return;
}

void Character::do_disarm (std::string argument)
{
  if (!is_npc ()
    && level < skill_table[skill_lookup("disarm")].skill_level[klass]) {
    send_to_char ("You don't know how to disarm opponents.\r\n");
    return;
  }

  if (get_eq_char (WEAR_WIELD) == NULL) {
    send_to_char ("You must wield a weapon to disarm.\r\n");
    return;
  }

  Character *victim;

  if ((victim = fighting) == NULL) {
    send_to_char ("You aren't fighting anyone.\r\n");
    return;
  }

  if (victim->get_eq_char (WEAR_WIELD) == NULL) {
    send_to_char ("Your opponent is not wielding a weapon.\r\n");
    return;
  }

  wait_state (skill_table[skill_lookup("disarm")].beats);
  int percent = number_percent () + victim->level - level;
  if (is_npc () || percent < pcdata->learned[skill_lookup("disarm")] * 2 / 3)
    disarm (this, victim);
  else
    send_to_char ("You failed.\r\n");
  return;
}

void Character::do_sla (std::string argument)
{
  send_to_char ("If you want to SLAY, spell it out.\r\n");
  return;
}

void Character::do_slay (std::string argument)
{
  std::string arg;

  one_argument (argument, arg);
  if (arg.empty()) {
    send_to_char ("Slay whom?\r\n");
    return;
  }

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

  if (this == victim) {
    send_to_char ("Suicide is a mortal sin.\r\n");
    return;
  }

  if (!victim->is_npc () && victim->level >= level) {
    send_to_char ("You failed.\r\n");
    return;
  }

  act ("You slay $M in cold blood!", NULL, victim, TO_CHAR);
  act ("$n slays you in cold blood!", NULL, victim, TO_VICT);
  act ("$n slays $N in cold blood!", NULL, victim, TO_NOTVICT);
  raw_kill (victim);
  return;
}

void Character::do_cast (std::string argument)
{
  /*
   * Only MOBprogrammed mobs not charmed can cast spells
   * like PC's
   */
  if (is_npc ()
    && (!pIndexData->progtypes || is_affected (AFF_CHARM)))
    return;

  std::string arg1, arg2;

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

  if (arg1.empty()) {
    send_to_char ("Cast which what where?\r\n");
    return;
  }

  int sn;
  if ((sn = skill_lookup (arg1)) < 0
    || (!is_npc () && level < skill_table[sn].skill_level[klass])) {
    send_to_char ("You can't do that.\r\n");
    return;
  }

  if (position < skill_table[sn].minimum_position) {
    send_to_char ("You can't concentrate enough.\r\n");
    return;
  }

  int mn = mana_cost (sn);
  /*
   * Locate targets.
   */
  Character *victim = NULL;
  Object *obj;
  void *vo = NULL;

  switch (skill_table[sn].target) {
  default:
    bug_printf ("Do_cast: bad target for sn %d.", sn);
    return;

  case TAR_IGNORE:
    break;

  case TAR_CHAR_OFFENSIVE:
    if (arg2.empty()) {
      if ((victim = fighting) == NULL) {
        send_to_char ("Cast the spell on whom?\r\n");
        return;
      }
    } else {
      if ((victim = get_char_room (arg2)) == NULL) {
        send_to_char ("They aren't here.\r\n");
        return;
      }
    }
    vo = (void *) victim;
    break;

  case TAR_CHAR_DEFENSIVE:
    if (arg2.empty()) {
      victim = this;
    } else {
      if ((victim = get_char_room (arg2)) == NULL) {
        send_to_char ("They aren't here.\r\n");
        return;
      }
    }

    vo = (void *) victim;
    break;

  case TAR_CHAR_SELF:
    if (!arg2.empty() && !is_name (arg2, name)) {
      send_to_char ("You cannot cast this spell on another.\r\n");
      return;
    }

    vo = (void *) this;
    break;

  case TAR_OBJ_INV:
    if (arg2.empty()) {
      send_to_char ("What should the spell be cast upon?\r\n");
      return;
    }

    if ((obj = get_obj_carry (arg2)) == NULL) {
      send_to_char ("You are not carrying that.\r\n");
      return;
    }

    vo = (void *) obj;
    break;
  }

  if (!is_npc () && mana < mn) {
    send_to_char ("You don't have enough mana.\r\n");
    return;
  }

  if (str_cmp (skill_table[sn].name, "ventriloquate"))
    say_spell (this, sn);

  wait_state (skill_table[sn].beats);

  if (!is_npc () && number_percent () > pcdata->learned[sn]) {
    send_to_char ("You lost your concentration.\r\n");
    mana -= mn / 2;
  } else {
    mana -= mn;
    (this->*(skill_table[sn].spell_fun)) (sn, level, vo);
  }

  if (skill_table[sn].target == TAR_CHAR_OFFENSIVE
    && victim->master != this && victim != this) {
    Character *vch;

    CharIter rch, next;
    for (rch = in_room->people.begin(); rch != in_room->people.end(); rch = next) {
      vch = *rch;
      next = ++rch;
      if (victim == vch && victim->fighting == NULL) {
        multi_hit (victim, this, TYPE_UNDEFINED);
        break;
      }
    }
  }

  return;
}

/* Date stamp idea comes from Alander of ROM */
void Character::do_note (std::string argument)
{
  if (is_npc ())
    return;

  std::string arg, buf1;

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

  if (arg.empty()) {
    do_note ("read");
    return;
  }

  char buf[MAX_STRING_LENGTH];
  int vnum;
  int anum;
  if (!str_cmp (arg, "list")) {
    vnum = 0;
    for (std::list<Note*>::iterator p = note_list.begin();
      p != note_list.end(); p++) {
      if ((*p)->is_note_to (this)) {
        snprintf (buf, sizeof buf, "[%3d%s] %s: %s\r\n",
          vnum,
          ((*p)->date_stamp > last_note
            && str_cmp ((*p)->sender, name)) ? "N" : " ",
          (*p)->sender.c_str(), (*p)->subject.c_str());
        buf1.append(buf);
        vnum++;
      }
    }
    send_to_char (buf1);
    return;
  }

  if (!str_cmp (arg, "read")) {
    bool fAll;

    if (!str_cmp (argument, "all")) {
      fAll = true;
      anum = 0;
    } else if (argument.empty() || !str_prefix (argument, "next"))
      /* read next unread note */
    {
      vnum = 0;
      for (std::list<Note*>::iterator p = note_list.begin();
        p != note_list.end(); p++) {
        if ((*p)->is_note_to (this)
          && str_cmp (name, (*p)->sender)
          && last_note < (*p)->date_stamp) {
          snprintf (buf, sizeof buf, "[%3d] %s: %s\r\n%s\r\nTo: %s\r\n",
            vnum, (*p)->sender.c_str(), (*p)->subject.c_str(),
            (*p)->date.c_str(), (*p)->to_list.c_str());
          buf1.append(buf);
          buf1.append((*p)->text);
          last_note = std::max (last_note, (*p)->date_stamp);
          send_to_char (buf1);
          return;
        } else
          vnum++;
      }
      send_to_char ("You have no unread notes.\r\n");
      return;
    } else if (is_number (argument)) {
      fAll = false;
      anum = std::atoi (argument.c_str());
    } else {
      send_to_char ("Note read which number?\r\n");
      return;
    }

    vnum = 0;
    for (std::list<Note*>::iterator p = note_list.begin();
      p != note_list.end(); p++) {
      if ((*p)->is_note_to (this) && (vnum++ == anum || fAll)) {
        snprintf (buf, sizeof buf, "[%3d] %s: %s\r\n%s\r\nTo: %s\r\n",
          vnum - 1,
          (*p)->sender.c_str(), (*p)->subject.c_str(),
          (*p)->date.c_str(), (*p)->to_list.c_str());
        buf1.append(buf);
        buf1.append((*p)->text);
        send_to_char (buf1);
        last_note = std::max (last_note, (*p)->date_stamp);
        return;
      }
    }

    send_to_char ("No such note.\r\n");
    return;
  }

  if (!str_cmp (arg, "+")) {
    note_attach (this);
    strncpy (buf, pnote->text.c_str(), sizeof buf);
    if (strlen (buf) + argument.size() >= MAX_STRING_LENGTH - 200) {
      send_to_char ("Note too long.\r\n");
      return;
    }

    strncat (buf, argument.c_str(), sizeof buf - argument.size());
    strncat (buf, "\r\n", sizeof buf - strlen("\r\n"));
    pnote->text = buf;
    send_to_char ("Ok.\r\n");
    return;
  }

  if (!str_cmp (arg, "subject")) {
    note_attach (this);
    pnote->subject = argument;
    send_to_char ("Ok.\r\n");
    return;
  }

  if (!str_cmp (arg, "to")) {
    note_attach (this);
    pnote->to_list = argument;
    send_to_char ("Ok.\r\n");
    return;
  }

  if (!str_cmp (arg, "clear")) {
    if (pnote != NULL) {
      delete pnote;
      pnote = NULL;
    }

    send_to_char ("Ok.\r\n");
    return;
  }

  if (!str_cmp (arg, "show")) {
    if (pnote == NULL) {
      send_to_char ("You have no note in progress.\r\n");
      return;
    }

    snprintf (buf, sizeof buf, "%s: %s\r\nTo: %s\r\n",
      pnote->sender.c_str(), pnote->subject.c_str(), pnote->to_list.c_str());
    send_to_char (buf);
    send_to_char (pnote->text);
    return;
  }

  if (!str_cmp (arg, "post") || !str_prefix (arg, "send")) {
    if (pnote == NULL) {
      send_to_char ("You have no note in progress.\r\n");
      return;
    }

    if (!str_cmp (pnote->to_list, "")) {
      send_to_char
        ("You need to provide a recipient (name, all, or immortal).\r\n");
      return;
    }

    if (!str_cmp (pnote->subject, "")) {
      send_to_char ("You need to provide a subject.\r\n");
      return;
    }

    pnote->date = g_world->get_time_text();
    pnote->date_stamp = g_world->get_current_time();

    note_list.push_back(pnote);

    std::ofstream notefile;

    notefile.open (NOTE_FILE, std::ofstream::out | std::ofstream::app | std::ofstream::binary);
    if (!notefile.is_open()) {
      std::perror (NOTE_FILE);
    } else {
      notefile << "Sender  " << pnote->sender << "~\n";
      notefile << "Date    " << pnote->date << "~\n";
      notefile << "Stamp   " << pnote->date_stamp << "\n";
      notefile << "To      " << pnote->to_list << "~\n";
      notefile << "Subject " << pnote->subject << "~\n";
      notefile << "Text\n" << pnote->text << "~\n\n";
      notefile.close();
    }

    pnote = NULL;
    send_to_char ("Ok.\r\n");
    return;
  }

  if (!str_cmp (arg, "remove")) {
    if (!is_number (argument)) {
      send_to_char ("Note remove which number?\r\n");
      return;
    }

    anum = std::atoi (argument.c_str());
    vnum = 0;
    std::list<Note*>::iterator next;
    for (std::list<Note*>::iterator p = note_list.begin();
      p != note_list.end(); p = next) {
      Note* curr = *p;
      next = ++p;
      if (curr->is_note_to (this) && vnum++ == anum) {
        note_remove (this, curr);
        send_to_char ("Ok.\r\n");
        return;
      }
    }

    send_to_char ("No such note.\r\n");
    return;
  }

  send_to_char ("Huh?  Type 'help note' for usage.\r\n");
  return;
}

void Character::do_auction (std::string argument)
{
  talk_channel (this, argument, CHANNEL_AUCTION, "auction");
  return;
}

void Character::do_chat (std::string argument)
{
  talk_channel (this, argument, CHANNEL_CHAT, "chat");
  return;
}

/*
 * Alander's new channels.
 */
void Character::do_music (std::string argument)
{
  talk_channel (this, argument, CHANNEL_MUSIC, "music");
  return;
}

void Character::do_question (std::string argument)
{
  talk_channel (this, argument, CHANNEL_QUESTION, "question");
  return;
}

void Character::do_answer (std::string argument)
{
  talk_channel (this, argument, CHANNEL_QUESTION, "answer");
  return;
}

void Character::do_shout (std::string argument)
{
  talk_channel (this, argument, CHANNEL_SHOUT, "shout");
  wait_state (12);
  return;
}

void Character::do_yell (std::string argument)
{
  talk_channel (this, argument, CHANNEL_YELL, "yell");
  return;
}

void Character::do_immtalk (std::string argument)
{
  talk_channel (this, argument, CHANNEL_IMMTALK, "immtalk");
  return;
}

void Character::do_say (std::string argument)
{
  if (argument.empty()) {
    send_to_char ("Say what?\r\n");
    return;
  }

  act ("$n says '$T'.", NULL, argument.c_str(), TO_ROOM);
  act ("You say '$T'.", NULL, argument.c_str(), TO_CHAR);
  mprog_speech_trigger (argument, this);
  return;
}

void Character::do_tell (std::string argument)
{
  if (!is_npc () && IS_SET (actflags, PLR_SILENCE)) {
    send_to_char ("Your message didn't get through.\r\n");
    return;
  }

  std::string arg;
  argument = one_argument (argument, arg);

  if (arg.empty() || argument.empty()) {
    send_to_char ("Tell whom what?\r\n");
    return;
  }

  /*
   * Can tell to PC's anywhere, but NPC's only in same room.
   * -- Furey
   */
  Character *victim;
  if ((victim = get_char_world (arg)) == NULL
    || (victim->is_npc () && victim->in_room != in_room)) {
    send_to_char ("They aren't here.\r\n");
    return;
  }

  if (!is_immortal() && !victim->is_awake ()) {
    act ("$E can't hear you.", 0, victim, TO_CHAR);
    return;
  }

  act ("You tell $N '$t'.", argument.c_str(), victim, TO_CHAR);
  int savepos = victim->position;
  victim->position = POS_STANDING;
  act ("$n tells you '$t'.", argument.c_str(), victim, TO_VICT);
  victim->position = savepos;
  victim->reply = this;

  return;
}

void Character::do_reply (std::string argument)
{
  if (!is_npc () && IS_SET (actflags, PLR_SILENCE)) {
    send_to_char ("Your message didn't get through.\r\n");
    return;
  }

  Character *victim;

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

  if (!is_immortal() && !victim->is_awake ()) {
    act ("$E can't hear you.", 0, victim, TO_CHAR);
    return;
  }

  act ("You tell $N '$t'.", argument.c_str(), victim, TO_CHAR);
  int savepos = victim->position;
  victim->position = POS_STANDING;
  act ("$n tells you '$t'.", argument.c_str(), victim, TO_VICT);
  victim->position = savepos;
  victim->reply = this;

  return;
}

void Character::do_emote (std::string argument)
{
  if (!is_npc () && IS_SET (actflags, PLR_NO_EMOTE)) {
    send_to_char ("You can't show your emotions.\r\n");
    return;
  }

  if (argument.empty()) {
    send_to_char ("Emote what?\r\n");
    return;
  }

  if (isalpha(argument[argument.size()-1]))
    argument += ".";

  act ("$n $T", NULL, argument.c_str(), TO_ROOM);
  act ("$n $T", NULL, argument.c_str(), TO_CHAR);
  return;
}

void Character::do_bug (std::string argument)
{
  append_file (BUG_FILE, argument);
  send_to_char ("Ok.  Thanks.\r\n");
  return;
}

void Character::do_idea (std::string argument)
{
  append_file (IDEA_FILE, argument);
  send_to_char ("Ok.  Thanks.\r\n");
  return;
}

void Character::do_typo (std::string argument)
{
  append_file (TYPO_FILE, argument);
  send_to_char ("Ok.  Thanks.\r\n");
  return;
}

void Character::do_rent (std::string argument)
{
  send_to_char ("There is no rent here.  Just save and quit.\r\n");
  return;
}

void Character::do_qui (std::string argument)
{
  send_to_char ("If you want to QUIT, you have to spell it out.\r\n");
  return;
}

void Character::do_quit (std::string argument)
{
  if (is_npc ())
    return;

  if (position == POS_FIGHTING) {
    send_to_char ("No way! You are fighting.\r\n");
    return;
  }

  if (position < POS_STUNNED) {
    send_to_char ("You're not DEAD yet.\r\n");
    return;
  }

  send_to_char
    ("Had I but time--as this fell sergeant, Death,\r\nIs strict in his arrest--O, I could tell you--\r\nBut let it be.\r\n");
  act ("$n has left the game.", NULL, NULL, TO_ROOM);
  log_printf ("%s has quit.", name.c_str());

  /*
   * After extract_char the this is no longer valid!
   */
  save_char_obj();
  Descriptor *d = desc;
  extract_char (true);
  if (d != NULL)
    d->close_socket();

  return;
}

void Character::do_save (std::string argument)
{
  if (is_npc ())
    return;

  if (level < 2) {
    send_to_char ("You must be at least second level to save.\r\n");
    return;
  }

  save_char_obj();
  send_to_char ("Ok.\r\n");
  return;
}

void Character::do_gag (std::string argument)
{
  if (is_npc ())
    return;

  std::string arg;
  one_argument (argument, arg);

  if (arg.empty()) {
    if (pcdata->gag_list.empty()) {
      send_to_char ("No gags set.\r\n");
      return;
    }
    std::string buf("Gag list\r\n");
    for (std::list<std::string>::iterator gag = pcdata->gag_list.begin();
      gag != pcdata->gag_list.end(); gag++) {
      buf.append((*gag) + "\r\n");
    }
    send_to_char (buf);
    return;
  }

  std::string gagname = capitalize(arg);
  if (!str_cmp(name, gagname)) {
    send_to_char ("You can't gag yourself.\r\n");
    return;
  }

  std::list<std::string>::iterator fnd;
  fnd = std::find(pcdata->gag_list.begin(), pcdata->gag_list.end(), gagname);
  if (fnd != pcdata->gag_list.end()) {
    pcdata->gag_list.remove(gagname);
    send_to_char ("Gag removed.\r\n");
  } else {
    pcdata->gag_list.push_back(gagname);
    send_to_char ("Gag added.\r\n");
  }
  return;
}

void Character::do_follow (std::string argument)
{
  std::string arg;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Follow whom?\r\n");
    return;
  }

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

  if (is_affected (AFF_CHARM) && master != NULL) {
    act ("But you'd rather follow $N!", NULL, master, TO_CHAR);
    return;
  }

  if (victim == this) {
    if (master == NULL) {
      send_to_char ("You already follow yourself.\r\n");
      return;
    }
    stop_follower();
    return;
  }

  if ((level - victim->level < -5 || level - victim->level > 5)
    && !is_hero()) {
    send_to_char ("You are not of the right caliber to follow.\r\n");
    return;
  }

  if (master != NULL)
    stop_follower();

  add_follower(victim);
  return;
}

void Character::do_order (std::string argument)
{
  std::string arg;

  argument = one_argument (argument, arg);

  if (arg.empty() || argument.empty()) {
    send_to_char ("Order whom to do what?\r\n");
    return;
  }

  if (is_affected (AFF_CHARM)) {
    send_to_char ("You feel like taking, not giving, orders.\r\n");
    return;
  }

  Character *victim;
  bool fAll;
  if (!str_cmp (arg, "all")) {
    fAll = true;
    victim = NULL;
  } else {
    fAll = false;
    if ((victim = get_char_room (arg)) == NULL) {
      send_to_char ("They aren't here.\r\n");
      return;
    }

    if (victim == this) {
      send_to_char ("Aye aye, right away!\r\n");
      return;
    }

    if (!victim->is_affected (AFF_CHARM) || victim->master != this) {
      send_to_char ("Do it yourself!\r\n");
      return;
    }
  }

  Character *och;
  bool found = false;
  CharIter rch, next;
  for (rch = in_room->people.begin(); rch != in_room->people.end(); rch = next) {
    och = *rch;
    next = ++rch;

    if (och->is_affected (AFF_CHARM)
      && och->master == this && (fAll || och == victim)) {
      found = true;
      act ("$n orders you to '$t'.", argument.c_str(), och, TO_VICT);
      och->interpret (argument);
    }
  }

  if (found)
    send_to_char ("Ok.\r\n");
  else
    send_to_char ("You have no followers here.\r\n");
  return;
}

void Character::do_group (std::string argument)
{
  char buf[MAX_STRING_LENGTH];
  std::string arg;

  one_argument (argument, arg);

  if (arg.empty()) {
    Character *ldr = (leader != NULL) ? leader : this;
    snprintf (buf, sizeof buf, "%s's group:\r\n", ldr->describe_to(this).c_str());
    send_to_char (buf);

    CharIter c;
    for (c = char_list.begin(); c != char_list.end(); c++) {
      if (is_same_group (*c, this)) {
        snprintf (buf, sizeof buf,
          "[%2d %s] %-16s %4d/%4d hp %4d/%4d mana %4d/%4d mv %5d xp\r\n",
          (*c)->level,
          (*c)->is_npc () ? "Mob" : class_table[(*c)->klass].who_name,
          capitalize ((*c)->describe_to(this)).c_str(),
          (*c)->hit, (*c)->max_hit,
          (*c)->mana, (*c)->max_mana, (*c)->move, (*c)->max_move, (*c)->exp);
        send_to_char (buf);
      }
    }
    return;
  }

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

  if (master != NULL || (leader != NULL && leader != this)) {
    send_to_char ("But you are following someone else!\r\n");
    return;
  }

  if (victim->master != this && this != victim) {
    act ("$N isn't following you.", NULL, victim, TO_CHAR);
    return;
  }

  if (is_same_group (victim, this) && this != victim) {
    victim->leader = NULL;
    act ("$n removes $N from $s group.", NULL, victim, TO_NOTVICT);
    act ("$n removes you from $s group.", NULL, victim, TO_VICT);
    act ("You remove $N from your group.", NULL, victim, TO_CHAR);
    return;
  }

  if (level - victim->level < -5 || level - victim->level > 5) {
    act ("$N cannot join $n's group.", NULL, victim, TO_NOTVICT);
    act ("You cannot join $n's group.", NULL, victim, TO_VICT);
    act ("$N cannot join your group.", NULL, victim, TO_CHAR);
    return;
  }

  victim->leader = this;
  act ("$N joins $n's group.", NULL, victim, TO_NOTVICT);
  act ("You join $n's group.", NULL, victim, TO_VICT);
  act ("$N joins your group.", NULL, victim, TO_CHAR);
  return;
}

/*
 * 'Split' originally by Gnort, God of Chaos.
 */
void Character::do_split (std::string argument)
{
  std::string arg;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Split how much?\r\n");
    return;
  }

  int amount = std::atoi (arg.c_str());

  if (amount < 0) {
    send_to_char ("Your group wouldn't like that.\r\n");
    return;
  }

  if (amount == 0) {
    send_to_char ("You hand out zero coins, but no one notices.\r\n");
    return;
  }

  if (gold < amount) {
    send_to_char ("You don't have that much gold.\r\n");
    return;
  }

  int members = 0;
  CharIter gch;
  for (gch = in_room->people.begin(); gch != in_room->people.end(); gch++) {
    if (is_same_group (*gch, this))
      members++;
  }

  if (members < 2) {
    send_to_char ("Just keep it all.\r\n");
    return;
  }

  int share = amount / members;
  int extra = amount % members;

  if (share == 0) {
    send_to_char ("Don't even bother, cheapskate.\r\n");
    return;
  }

  gold -= amount;
  gold += share + extra;

  char buf[MAX_STRING_LENGTH];
  snprintf (buf, sizeof buf,
    "You split %d gold coins.  Your share is %d gold coins.\r\n",
    amount, share + extra);
  send_to_char (buf);

  snprintf (buf, sizeof buf, "$n splits %d gold coins.  Your share is %d gold coins.",
    amount, share);

  for (gch = in_room->people.begin(); gch != in_room->people.end(); gch++) {
    if (*gch != this && is_same_group (*gch, this)) {
      act (buf, NULL, *gch, TO_VICT);
      (*gch)->gold += share;
    }
  }

  return;
}

void Character::do_gtell (std::string argument)
{
  char buf[MAX_STRING_LENGTH];

  if (argument.empty()) {
    send_to_char ("Tell your group what?\r\n");
    return;
  }

  if (IS_SET (actflags, PLR_NO_TELL)) {
    send_to_char ("Your message didn't get through!\r\n");
    return;
  }

  /*
   * Note use of send_to_char, so gtell works on sleepers.
   */
  snprintf (buf, sizeof buf, "%s tells the group '%s'.\r\n", name.c_str(), argument.c_str());
  for (CharIter c = char_list.begin(); c != char_list.end(); c++) {
    if (is_same_group (*c, this))
      (*c)->send_to_char (buf);
  }

  return;
}

void Character::do_look (std::string argument)
{
  if (!is_npc () && desc == NULL)
    return;

  if (position < POS_SLEEPING) {
    send_to_char ("You can't see anything but stars!\r\n");
    return;
  }

  if (position == POS_SLEEPING) {
    send_to_char ("You can't see anything, you're sleeping!\r\n");
    return;
  }

  if (!check_blind())
    return;

  if (!is_npc ()
    && !IS_SET (actflags, PLR_HOLYLIGHT)
    && in_room->is_dark()) {
    send_to_char ("It is pitch black ... \r\n");
    show_char_to_char (in_room->people);
    return;
  }

  std::string arg1, arg2;

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

  if (arg1.empty() || !str_cmp (arg1, "auto")) {
    /* 'look' or 'look auto' */
    send_to_char (in_room->name);
    send_to_char ("\r\n");

    if (!is_npc () && IS_SET (actflags, PLR_AUTOEXIT))
      do_exits ("auto");

    if (arg1.empty() || (!is_npc () && !IS_SET (actflags, PLR_BRIEF)))
      send_to_char (in_room->description);

    show_list_to_char (in_room->contents, false, false);
    show_char_to_char (in_room->people);
    return;
  }

  char buf[MAX_STRING_LENGTH];
  Object *obj;
  std::string pdesc;

  if (!str_cmp (arg1, "i") || !str_cmp (arg1, "in")) {
    /* 'look in' */
    if (arg2.empty()) {
      send_to_char ("Look in what?\r\n");
      return;
    }

    if ((obj = get_obj_here (arg2)) == NULL) {
      send_to_char ("You do not see that here.\r\n");
      return;
    }

    switch (obj->item_type) {
    default:
      send_to_char ("That is not a container.\r\n");
      break;

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

      snprintf (buf, sizeof buf, "It's %s full of a %s liquid.\r\n",
        obj->value[1] < obj->value[0] / 4
        ? "less than" :
        obj->value[1] < 3 * obj->value[0] / 4
        ? "about" : "more than", liq_table[obj->value[2]].liq_color);

      send_to_char (buf);
      break;

    case ITEM_CONTAINER:
    case ITEM_CORPSE_NPC:
    case ITEM_CORPSE_PC:
      if (IS_SET (obj->value[1], CONT_CLOSED)) {
        send_to_char ("It is closed.\r\n");
        break;
      }

      act ("$p contains:", obj, NULL, TO_CHAR);
      show_list_to_char (obj->contains, true, true);
      break;
    }
    return;
  }

  Character *victim;
  if ((victim = get_char_room (arg1)) != NULL) {
    show_char_to_char_1 (victim);
    return;
  }

  ObjIter o;
  for (o = carrying.begin(); o != carrying.end(); o++) {
    if (can_see_obj(*o)) {
      pdesc = get_extra_descr (arg1, (*o)->extra_descr);
      if (!pdesc.empty()) {
        send_to_char (pdesc);
        return;
      }

      pdesc = get_extra_descr (arg1, (*o)->pIndexData->extra_descr);
      if (!pdesc.empty()) {
        send_to_char (pdesc);
        return;
      }
    }

    if (is_name (arg1, (*o)->name)) {
      send_to_char ((*o)->description);
      return;
    }
  }

  for (o = in_room->contents.begin(); o != in_room->contents.end(); o++) {
    if (can_see_obj(*o)) {
      pdesc = get_extra_descr (arg1, (*o)->extra_descr);
      if (!pdesc.empty()) {
        send_to_char (pdesc);
        return;
      }

      pdesc = get_extra_descr (arg1, (*o)->pIndexData->extra_descr);
      if (!pdesc.empty()) {
        send_to_char (pdesc);
        return;
      }
    }

    if (is_name (arg1, (*o)->name)) {
      send_to_char ((*o)->description);
      return;
    }
  }

  pdesc = get_extra_descr (arg1, in_room->extra_descr);
  if (!pdesc.empty()) {
    send_to_char (pdesc);
    return;
  }

  int door;
  if (!str_cmp (arg1, "n") || !str_cmp (arg1, "north"))
    door = 0;
  else if (!str_cmp (arg1, "e") || !str_cmp (arg1, "east"))
    door = 1;
  else if (!str_cmp (arg1, "s") || !str_cmp (arg1, "south"))
    door = 2;
  else if (!str_cmp (arg1, "w") || !str_cmp (arg1, "west"))
    door = 3;
  else if (!str_cmp (arg1, "u") || !str_cmp (arg1, "up"))
    door = 4;
  else if (!str_cmp (arg1, "d") || !str_cmp (arg1, "down"))
    door = 5;
  else {
    send_to_char ("You do not see that here.\r\n");
    return;
  }

  /* 'look direction' */
  Exit *pexit;
  if ((pexit = in_room->exit[door]) == NULL) {
    send_to_char ("Nothing special there.\r\n");
    return;
  }

  if (!pexit->description.empty())
    send_to_char (pexit->description);
  else
    send_to_char ("Nothing special there.\r\n");

  if (!pexit->name.empty() && pexit->name[0] != ' ') {
    if (IS_SET (pexit->exit_info, EX_CLOSED)) {
      act ("The $d is closed.", NULL, pexit->name.c_str(), TO_CHAR);
    } else if (IS_SET (pexit->exit_info, EX_ISDOOR)) {
      act ("The $d is open.", NULL, pexit->name.c_str(), TO_CHAR);
    }
  }

  return;
}

void Character::do_examine (std::string argument)
{
  std::string arg;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Examine what?\r\n");
    return;
  }

  do_look (arg);

  Object *obj;
  if ((obj = get_obj_here (arg)) != NULL) {
    switch (obj->item_type) {
    default:
      break;

    case ITEM_DRINK_CON:
    case ITEM_CONTAINER:
    case ITEM_CORPSE_NPC:
    case ITEM_CORPSE_PC:
      send_to_char ("When you look inside, you see:\r\n");
      std::string buf = "in " + arg;
      do_look (buf);
    }
  }

  return;
}

/*
 * Thanks to Zrin for auto-exit part.
 */
void Character::do_exits (std::string argument)
{
  if (!check_blind())
    return;

  char buf[MAX_STRING_LENGTH];
  buf[0] = '\0';
  bool fAuto = !str_cmp (argument, "auto");
  strncpy (buf, fAuto ? "[Exits:" : "Obvious exits:\r\n", sizeof buf);

  bool found = false;
  for (int door = 0; door <= 5; door++) {
    Exit *pexit;
    if ((pexit = in_room->exit[door]) != NULL
      && pexit->to_room != NULL && !IS_SET (pexit->exit_info, EX_CLOSED)) {
      found = true;
      if (fAuto) {
        strncat (buf, " ", sizeof(buf) - strlen(" "));
        strncat (buf, dir_name[door].c_str(), sizeof(buf) - dir_name[door].size());
      } else {
        snprintf (buf + strlen(buf), sizeof(buf) - strlen(buf), "%-5s - %s\r\n",
          capitalize (dir_name[door]).c_str(), pexit->to_room->is_dark()
          ? "Too dark to tell" : pexit->to_room->name.c_str());
      }
    }
  }

  if (!found)
    strncat (buf, fAuto ? " none" : "None.\r\n", sizeof(buf) - strlen("None.\r\n"));

  if (fAuto)
    strncat (buf, "]\r\n", sizeof(buf) - strlen("]\r\n"));

  send_to_char (buf);
  return;
}

void Character::do_score (std::string argument)
{
  char buf[MAX_STRING_LENGTH];

  snprintf (buf, sizeof buf,
    "You are %s%s, level %d, %d years old (%d hours).\r\n",
    name.c_str(),
    is_npc () ? "" : pcdata->title.c_str(),
    level, get_age(), (get_age() - 17) * 2);
  send_to_char (buf);

  if (get_trust () != level) {
    snprintf (buf, sizeof buf, "You are trusted at level %d.\r\n", get_trust ());
    send_to_char (buf);
  }

  snprintf (buf, sizeof buf,
    "You have %d/%d hit, %d/%d mana, %d/%d movement, %d practices.\r\n",
    hit, max_hit,
    mana, max_mana, move, max_move, practice);
  send_to_char (buf);

  snprintf (buf, sizeof buf,
    "You are carrying %d/%d items with weight %d/%d kg.\r\n",
    carry_number, can_carry_n(), carry_weight, can_carry_w());
  send_to_char (buf);

  snprintf (buf, sizeof buf,
    "Str: %d  Int: %d  Wis: %d  Dex: %d  Con: %d.\r\n",
    get_curr_str(), get_curr_int(), get_curr_wis(),
    get_curr_dex(), get_curr_con());
  send_to_char (buf);

  snprintf (buf, sizeof buf,
    "You have scored %d exp, and have %d gold coins.\r\n", exp, gold);
  send_to_char (buf);

  snprintf (buf, sizeof buf,
    "Autoexit: %s.  Autoloot: %s.  Autosac: %s.\r\n",
    (!is_npc () && IS_SET (actflags, PLR_AUTOEXIT)) ? "yes" : "no",
    (!is_npc () && IS_SET (actflags, PLR_AUTOLOOT)) ? "yes" : "no",
    (!is_npc () && IS_SET (actflags, PLR_AUTOSAC)) ? "yes" : "no");
  send_to_char (buf);

  snprintf (buf, sizeof buf, "Wimpy set to %d hit points.\r\n", wimpy);
  send_to_char (buf);

  if (!is_npc ()) {
    snprintf (buf, sizeof buf, "Page pausing set to %d lines of text.\r\n",
      pcdata->pagelen);
    send_to_char (buf);
  }

  if (!is_npc () && pcdata->condition[COND_DRUNK] > 10)
    send_to_char ("You are drunk.\r\n");
  if (!is_npc () && pcdata->condition[COND_THIRST] == 0)
    send_to_char ("You are thirsty.\r\n");
  if (!is_npc () && pcdata->condition[COND_FULL] == 0)
    send_to_char ("You are hungry.\r\n");

  switch (position) {
  case POS_DEAD:
    send_to_char ("You are DEAD!!\r\n");
    break;
  case POS_MORTAL:
    send_to_char ("You are mortally wounded.\r\n");
    break;
  case POS_INCAP:
    send_to_char ("You are incapacitated.\r\n");
    break;
  case POS_STUNNED:
    send_to_char ("You are stunned.\r\n");
    break;
  case POS_SLEEPING:
    send_to_char ("You are sleeping.\r\n");
    break;
  case POS_RESTING:
    send_to_char ("You are resting.\r\n");
    break;
  case POS_STANDING:
    send_to_char ("You are standing.\r\n");
    break;
  case POS_FIGHTING:
    send_to_char ("You are fighting.\r\n");
    break;
  }

  if (level >= 25) {
    snprintf (buf, sizeof buf, "AC: %d.  ", get_ac());
    send_to_char (buf);
  }

  send_to_char ("You are ");
  if (get_ac() >= 101)
    send_to_char ("WORSE than naked!\r\n");
  else if (get_ac() >= 80)
    send_to_char ("naked.\r\n");
  else if (get_ac() >= 60)
    send_to_char ("wearing clothes.\r\n");
  else if (get_ac() >= 40)
    send_to_char ("slightly armored.\r\n");
  else if (get_ac() >= 20)
    send_to_char ("somewhat armored.\r\n");
  else if (get_ac() >= 0)
    send_to_char ("armored.\r\n");
  else if (get_ac() >= -20)
    send_to_char ("well armored.\r\n");
  else if (get_ac() >= -40)
    send_to_char ("strongly armored.\r\n");
  else if (get_ac() >= -60)
    send_to_char ("heavily armored.\r\n");
  else if (get_ac() >= -80)
    send_to_char ("superbly armored.\r\n");
  else if (get_ac() >= -100)
    send_to_char ("divinely armored.\r\n");
  else
    send_to_char ("invincible!\r\n");

  if (level >= 15) {
    snprintf (buf, sizeof buf, "Hitroll: %d  Damroll: %d.\r\n",
      get_hitroll(), get_damroll());
    send_to_char (buf);
  }

  if (level >= 10) {
    snprintf (buf, sizeof buf, "Alignment: %d.  ", alignment);
    send_to_char (buf);
  }

  send_to_char ("You are ");
  if (alignment > 900)
    send_to_char ("angelic.\r\n");
  else if (alignment > 700)
    send_to_char ("saintly.\r\n");
  else if (alignment > 350)
    send_to_char ("good.\r\n");
  else if (alignment > 100)
    send_to_char ("kind.\r\n");
  else if (alignment > -100)
    send_to_char ("neutral.\r\n");
  else if (alignment > -350)
    send_to_char ("mean.\r\n");
  else if (alignment > -700)
    send_to_char ("evil.\r\n");
  else if (alignment > -900)
    send_to_char ("demonic.\r\n");
  else
    send_to_char ("satanic.\r\n");

  if (!affected.empty()) {
    send_to_char ("You are affected by:\r\n");
    AffIter af;
    for (af = affected.begin(); af != affected.end(); af++) {
      snprintf (buf, sizeof buf, "Spell: '%s'", skill_table[(*af)->type].name);
      send_to_char (buf);

      if (level >= 20) {
        snprintf (buf, sizeof buf,
          " modifies %s by %d for %d hours",
          affect_loc_name ((*af)->location).c_str(), (*af)->modifier, (*af)->duration);
        send_to_char (buf);
      }

      send_to_char (".\r\n");
    }
  }

  return;
}

void Character::do_time (std::string argument)
{
  char buf[MAX_STRING_LENGTH];
  std::string wt = g_world->world_time();
  snprintf (buf, sizeof buf,
    "%sMerc started up at %s\r\nThe system time is %s\r\n",  wt.c_str(),
    str_boot_time.c_str(), g_world->get_time_text()
    );

  send_to_char (buf);
  return;
}

void Character::do_weather (std::string argument)
{
  if (!is_outside()) {
    send_to_char ("You can't see the weather indoors.\r\n");
    return;
  }

  send_to_char (g_world->world_weather());
  return;
}

void Character::do_help (std::string argument)
{
  sqlite3_stmt *stmt = NULL;

  if (argument.empty())
    argument = "summary";

  char *sql = sqlite3_mprintf("SELECT level, keyword, text FROM helps WHERE level <= %d", level);

  if (sqlite3_prepare(g_db->database, sql, -1, &stmt, 0) != SQLITE_OK) {
    bug_printf("Could not prepare statement: '%s' Error: %s", sql, sqlite3_errmsg(g_db->database));
    sqlite3_free(sql);
    return;
  }

  while (sqlite3_step(stmt) == SQLITE_ROW) {
    int lvl = sqlite3_column_int( stmt, 0 );
    std::string keyword = (const char*)sqlite3_column_text( stmt, 1 );
    std::string text = (const char*)sqlite3_column_text( stmt, 2 );

    if (is_name (argument, keyword)) {
      if (lvl >= 0 && str_cmp (argument, "imotd")) {
        send_to_char (keyword);
        send_to_char ("\r\n");
      }

      send_to_char (text);
      sqlite3_finalize(stmt);
      sqlite3_free(sql);
      return;
    }
  }

  sqlite3_finalize(stmt);
  sqlite3_free(sql);
  send_to_char ("No help on that word.\r\n");
  return;
}

/*
 * New 'who' command originally by Alander of Rivers of Mud.
 */
void Character::do_who (std::string argument)
{
  int iClass;
  bool rgfClass[CLASS_MAX];

  /*
   * Set default arguments.
   */
  int iLevelLower = 0;
  int iLevelUpper = MAX_LEVEL;
  bool fClassRestrict = false;
  bool fImmortalOnly = false;

  for (iClass = 0; iClass < CLASS_MAX; iClass++)
    rgfClass[iClass] = false;

  /*
   * Parse arguments.
   */
  int nNumber = 0;
  for (;;) {
    std::string arg;

    argument = one_argument (argument, arg);
    if (arg.empty())
      break;

    if (is_number (arg)) {
      switch (++nNumber) {
      case 1:
        iLevelLower = std::atoi (arg.c_str());
        break;
      case 2:
        iLevelUpper = std::atoi (arg.c_str());
        break;
      default:
        send_to_char ("Only two level numbers allowed.\r\n");
        return;
      }
    } else {
      if (arg.size() < 3) {
        send_to_char ("Classes must be longer than that.\r\n");
        return;
      }

      /*
       * Look for classes to turn on.
       */
      int iClass;

      arg.erase(3);
      if (!str_cmp (arg, "imm")) {
        fImmortalOnly = true;
      } else {
        fClassRestrict = true;
        for (iClass = 0; iClass < CLASS_MAX; iClass++) {
          if (!str_cmp (arg, class_table[iClass].who_name)) {
            rgfClass[iClass] = true;
            break;
          }
        }

        if (iClass == CLASS_MAX) {
          send_to_char ("That's not a class.\r\n");
          return;
        }
      }
    }
  }

  /*
   * Now show matching chars.
   */
  int nMatch = 0;
  char buf[MAX_STRING_LENGTH];
  buf[0] = '\0';
  for (DescIter d = descriptor_list.begin();
    d != descriptor_list.end(); d++) {
    Character *wch;
    char const *klass;

    /*
     * Check for match against restrictions.
     * Don't use trust as that exposes trusted mortals.
     */
    if ((*d)->connected != CON_PLAYING || !can_see((*d)->character))
      continue;

    wch = ((*d)->original != NULL) ? (*d)->original : (*d)->character;
    if (wch->level < iLevelLower
      || wch->level > iLevelUpper
      || (fImmortalOnly && wch->level < LEVEL_HERO)
      || (fClassRestrict && !rgfClass[wch->klass]))
      continue;

    nMatch++;

    /*
     * Figure out what to print for class.
     */
    klass = class_table[wch->klass].who_name;
    switch (wch->level) {
    default:
      break;
    case MAX_LEVEL - 0:
      klass = "GOD";
      break;
    case MAX_LEVEL - 1:
      klass = "SUP";
      break;
    case MAX_LEVEL - 2:
      klass = "DEI";
      break;
    case MAX_LEVEL - 3:
      klass = "ANG";
      break;
    }

    /*
     * Format it up.
     */
    snprintf (buf + strlen(buf), sizeof(buf) - strlen(buf), "[%2d %s] %s%s%s%s\r\n",
      wch->level,
      klass,
      IS_SET (wch->actflags, PLR_KILLER) ? "(KILLER) " : "",
      IS_SET (wch->actflags, PLR_THIEF) ? "(THIEF) " : "",
      wch->name.c_str(), wch->pcdata->title.c_str());
  }

  char buf2[MAX_STRING_LENGTH];
  snprintf (buf2, sizeof buf2, "You see %d player%s in the game.\r\n",
    nMatch, nMatch == 1 ? "" : "s");
  strncat (buf, buf2, sizeof(buf) - sizeof(buf2));
  send_to_char (buf);
  return;
}

void Character::do_inventory (std::string argument)
{
  send_to_char ("You are carrying:\r\n");
  show_list_to_char (carrying, true, true);
  return;
}

void Character::do_equipment (std::string argument)
{

  send_to_char ("You are using:\r\n");
  bool found = false;
  for (int iWear = 0; iWear < MAX_WEAR; iWear++) {
    Object *obj;
    if ((obj = get_eq_char (iWear)) == NULL)
      continue;

    send_to_char (where_name[iWear]);
    if (can_see_obj(obj)) {
      send_to_char (obj->format_obj_to_char (this, true));
      send_to_char ("\r\n");
    } else {
      send_to_char ("something.\r\n");
    }
    found = true;
  }

  if (!found)
    send_to_char ("Nothing.\r\n");

  return;
}

void Character::do_compare (std::string argument)
{
  std::string arg1, arg2;

  argument = one_argument (argument, arg1);
  argument = one_argument (argument, arg2);
  if (arg1.empty()) {
    send_to_char ("Compare what to what?\r\n");
    return;
  }

  Object *obj1;
  if ((obj1 = get_obj_carry (arg1)) == NULL) {
    send_to_char ("You do not have that item.\r\n");
    return;
  }

  Object *obj2 = NULL;
  if (arg2.empty()) {
    ObjIter o;
    for (o = carrying.begin(); o != carrying.end(); o++) {
      obj2 = *o;
      if (obj2->wear_loc != WEAR_NONE && can_see_obj(obj2)
        && obj1->item_type == obj2->item_type
        && (obj1->wear_flags & obj2->wear_flags & ~ITEM_TAKE) != 0)
        break;
    }

    if (obj2 == NULL) {
      send_to_char ("You aren't wearing anything comparable.\r\n");
      return;
    }
  } else {
    if ((obj2 = get_obj_carry (arg2)) == NULL) {
      send_to_char ("You do not have that item.\r\n");
      return;
    }
  }

  const char* msg = NULL;
  int value1 = 0;
  int value2 = 0;

  if (obj1 == obj2) {
    msg = "You compare $p to itself.  It looks about the same.";
  } else if (obj1->item_type != obj2->item_type) {
    msg = "You can't compare $p and $P.";
  } else {
    switch (obj1->item_type) {
    default:
      msg = "You can't compare $p and $P.";
      break;

    case ITEM_ARMOR:
      value1 = obj1->value[0];
      value2 = obj2->value[0];
      break;

    case ITEM_WEAPON:
      value1 = obj1->value[1] + obj1->value[2];
      value2 = obj2->value[1] + obj2->value[2];
      break;
    }
  }

  if (msg == NULL) {
    if (value1 == value2)
      msg = "$p and $P look about the same.";
    else if (value1 > value2)
      msg = "$p looks better than $P.";
    else
      msg = "$p looks worse than $P.";
  }

  act (msg, obj1, obj2, TO_CHAR);
  return;
}

void Character::do_credits (std::string argument)
{
  do_help ("diku");
  return;
}

void Character::do_where (std::string argument)
{
  std::string arg;
  char buf[MAX_STRING_LENGTH];
  Character *victim;
  bool found = false;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Players near you:\r\n");
    for (DescIter d = descriptor_list.begin();
      d != descriptor_list.end(); d++) {
      if ((*d)->connected == CON_PLAYING
        && (victim = (*d)->character) != NULL && !victim->is_npc ()
        && victim->in_room != NULL
        && victim->in_room->area == in_room->area
        && can_see(victim)) {
        found = true;
        snprintf (buf, sizeof buf, "%-28s %s\r\n", victim->name.c_str(), victim->in_room->name.c_str());
        send_to_char (buf);
      }
    }
    if (!found)
      send_to_char ("None\r\n");
  } else {
    for (CharIter c = char_list.begin(); c != char_list.end(); c++) {
      victim = *c;
      if (victim->in_room != NULL
        && victim->in_room->area == in_room->area
        && !victim->is_affected (AFF_HIDE)
        && !victim->is_affected (AFF_SNEAK)
        && can_see(victim)
        && is_name (arg, victim->name)) {
        found = true;
        snprintf (buf, sizeof buf, "%-28s %s\r\n",
          victim->describe_to(this).c_str(), victim->in_room->name.c_str());
        send_to_char (buf);
        break;
      }
    }
    if (!found)
      act ("You didn't find any $T.", NULL, arg.c_str(), TO_CHAR);
  }

  return;
}

void Character::do_consider (std::string argument)
{
  std::string arg, msg, buf;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Consider killing whom?\r\n");
    return;
  }

  Character *victim;
  if ((victim = get_char_room (arg)) == NULL) {
    send_to_char ("They're not here.\r\n");
    return;
  }

  if (!victim->is_npc ()) {
    send_to_char ("The gods do not accept this type of sacrafice.\r\n");
    return;
  }

  int diff = victim->level - level;

  if (diff <= -10)
    msg = "You can kill $N naked and weaponless.";
  else if (diff <= -5)
    msg = "$N is no match for you.";
  else if (diff <= -2)
    msg = "$N looks like an easy kill.";
  else if (diff <= 1)
    msg = "The perfect match!";
  else if (diff <= 4)
    msg = "$N says 'Do you feel lucky, punk?'.";
  else if (diff <= 9)
    msg = "$N laughs at you mercilessly.";
  else
    msg = "Death will thank you for your gift.";

  act (msg, NULL, victim, TO_CHAR);

  /* additions by king@tinuviel.cs.wcu.edu */
  int hpdiff = (hit - victim->hit);

  if (((diff >= 0) && (hpdiff <= 0))
    || ((diff <= 0) && (hpdiff >= 0))) {
    send_to_char ("Also,");
  } else {
    send_to_char ("However,");
  }

  if (hpdiff >= 101)
    buf = " you are currently much healthier than $E.";
  if (hpdiff <= 100)
    buf = " you are currently healthier than $E.";
  if (hpdiff <= 50)
    buf = " you are currently slightly healthier than $E.";
  if (hpdiff <= 25)
    buf = " you are a teensy bit healthier than $E.";
  if (hpdiff <= 0)
    buf = " $E is a teensy bit healthier than you.";
  if (hpdiff <= -25)
    buf = " $E is slightly healthier than you.";
  if (hpdiff <= -50)
    buf = " $E is healthier than you.";
  if (hpdiff <= -100)
    buf = " $E is much healthier than you.";

  act (buf, NULL, victim, TO_CHAR);
  return;
}

void Character::do_title (std::string argument)
{
  if (is_npc ())
    return;

  if (argument.empty()) {
    send_to_char ("Change your title to what?\r\n");
    return;
  }

  if (argument.size() > 50)
    argument.erase(50);

  smash_tilde (argument);
  set_title(argument);
  send_to_char ("Ok.\r\n");
}

void Character::do_description (std::string argument)
{
  if (!argument.empty()) {
    std::string buf;
    smash_tilde (argument);
    if (argument[0] == '+') {
      if (!description.empty())
        buf = description;
      argument.erase(0,1);
      argument.erase(0, argument.find_first_not_of(" "));
    }

    if (buf.size() + argument.size() >= MAX_STRING_LENGTH - 2) {
      send_to_char ("Description too long.\r\n");
      return;
    }

    buf.append(argument);
    buf.append("\r\n");
    description = buf;
  }

  send_to_char ("Your description is:\r\n");
  send_to_char (!description.empty() ? description.c_str() : "(None).\r\n");
  return;
}

void Character::do_report (std::string argument)
{
  char buf[MAX_INPUT_LENGTH];

  snprintf (buf, sizeof buf,
    "You report: %d/%d hp %d/%d mana %d/%d mv %d xp.\r\n",
    hit, max_hit,
    mana, max_mana, move, max_move, exp);

  send_to_char (buf);

  snprintf (buf, sizeof buf, "$n reports: %d/%d hp %d/%d mana %d/%d mv %d xp.",
    hit, max_hit,
    mana, max_mana, move, max_move, exp);

  act (buf, NULL, NULL, TO_ROOM);

  return;
}

void Character::do_practice (std::string argument)
{
  if (is_npc ())
    return;

  if (level < 3) {
    send_to_char
      ("You must be third level to practice.  Go train instead!\r\n");
    return;
  }

  char buf[MAX_STRING_LENGTH];
  std::string buf1;
  int sn;

  if (argument.empty()) {
    int col;

    col = 0;
    for (sn = 0; sn < MAX_SKILL; sn++) {
      if (skill_table[sn].name == NULL)
        break;
      if (level < skill_table[sn].skill_level[klass])
        continue;

      snprintf (buf, sizeof buf, "%18s %3d%%  ",
        skill_table[sn].name, pcdata->learned[sn]);
      buf1.append(buf);
      if (++col % 3 == 0)
        buf1.append("\r\n");
    }

    if (col % 3 != 0)
      buf1.append("\r\n");

    snprintf (buf, sizeof buf, "You have %d practice sessions left.\r\n", practice);
    buf1.append(buf);
    send_to_char (buf1);
  } else {
    int adept;

    if (!is_awake ()) {
      send_to_char ("In your dreams, or what?\r\n");
      return;
    }

    CharIter mob;
    for (mob = in_room->people.begin(); mob != in_room->people.end(); mob++) {
      if ((*mob)->is_npc () && IS_SET ((*mob)->actflags, ACT_PRACTICE))
        break;
    }

    if (mob == in_room->people.end()) {
      send_to_char ("You can't do that here.\r\n");
      return;
    }

    if (practice <= 0) {
      send_to_char ("You have no practice sessions left.\r\n");
      return;
    }

    if ((sn = skill_lookup (argument)) < 0 || (!is_npc ()
        && level < skill_table[sn].skill_level[klass])) {
      send_to_char ("You can't practice that.\r\n");
      return;
    }

    adept = is_npc () ? 100 : class_table[klass].skill_adept;

    if (pcdata->learned[sn] >= adept) {
      snprintf (buf, sizeof buf, "You are already an adept of %s.\r\n",
        skill_table[sn].name);
      send_to_char (buf);
    } else {
      practice--;
      pcdata->learned[sn] += int_app[get_curr_int()].learn;
      if (pcdata->learned[sn] < adept) {
        act ("You practice $T.", NULL, skill_table[sn].name, TO_CHAR);
        act ("$n practices $T.", NULL, skill_table[sn].name, TO_ROOM);
      } else {
        pcdata->learned[sn] = adept;
        act ("You are now an adept of $T.",
          NULL, skill_table[sn].name, TO_CHAR);
        act ("$n is now an adept of $T.",
          NULL, skill_table[sn].name, TO_ROOM);
      }
    }
  }
  return;
}

/*
 * 'Wimpy' originally by Dionysos.
 */
void Character::do_wimpy (std::string argument)
{
  std::string arg;
  int wpy;

  one_argument (argument, arg);

  if (arg.empty())
    wpy = max_hit / 5;
  else
    wpy = std::atoi (arg.c_str());

  if (wpy < 0) {
    send_to_char ("Your courage exceeds your wisdom.\r\n");
    return;
  }

  if (wpy > max_hit) {
    send_to_char ("Such cowardice ill becomes you.\r\n");
    return;
  }

  wimpy = wpy;
  char buf[MAX_STRING_LENGTH];
  snprintf (buf, sizeof buf, "Wimpy set to %d hit points.\r\n", wimpy);
  send_to_char (buf);
  return;
}

void Character::do_password (std::string argument)
{
  if (is_npc ())
    return;

  std::string arg1;
  std::string arg2;
  char *pwdnew;
  char *p;
  char cEnd;

  /*
   * Can't use one_argument here because it smashes case.
   * So we just steal all its code.  Bleagh.
   */
  std::string::iterator argp = argument.begin();

  arg1.erase();
  while (argp != argument.end() && isspace (*argp))
    argp++;

  cEnd = ' ';
  if (*argp == '\'' || *argp == '"')
    cEnd = *argp++;

  while (argp != argument.end()) {
    if (*argp == cEnd) {
      break;
    }
    arg1.append(1, *argp);
    argp++;
  }

  argp = argument.begin();

  arg2.erase();
  while (argp != argument.end() && isspace (*argp))
    argp++;

  cEnd = ' ';
  if (*argp == '\'' || *argp == '"')
    cEnd = *argp++;

  while (argp != argument.end()) {
    if (*argp == cEnd) {
      break;
    }
    arg2.append(1, *argp);
    argp++;
  }

  if (arg1.empty() || arg2.empty()) {
    send_to_char ("Syntax: password <old> <new>.\r\n");
    return;
  }

  char buf[MAX_STRING_LENGTH];  // Needed for Windows crypt
  strncpy(buf,arg1.c_str(), sizeof buf);
  if (strcmp (crypt (buf, pcdata->pwd.c_str()), pcdata->pwd.c_str())) {
    wait_state (40);
    send_to_char ("Wrong password.  Wait 10 seconds.\r\n");
    return;
  }

  if (arg2.size() < 5) {
    send_to_char ("New password must be at least five characters long.\r\n");
    return;
  }

  /*
   * No tilde allowed because of player file format.
   */
  strncpy(buf,arg2.c_str(), sizeof buf);
  pwdnew = crypt (buf, name.c_str());
  for (p = pwdnew; *p != '\0'; p++) {
    if (*p == '~') {
      send_to_char ("New password not acceptable, try again.\r\n");
      return;
    }
  }

  pcdata->pwd = pwdnew;
  save_char_obj();
  send_to_char ("Ok.\r\n");
  return;
}

void Character::do_socials (std::string argument)
{
  char buf[MAX_STRING_LENGTH];
  int col = 0;
  sqlite3_stmt *stmt = NULL;

  if (sqlite3_prepare(g_db->database,
      "SELECT name FROM socials ORDER BY name ASC",
      -1, &stmt, 0) != SQLITE_OK) {
    bug_printf("Could not prepare statement: %s", sqlite3_errmsg(g_db->database));
    return;
  }

  while (sqlite3_step(stmt) == SQLITE_ROW) {
    snprintf (buf, sizeof buf, "%-12s", sqlite3_column_text( stmt, 0 ));
    send_to_char (buf);
    if (++col % 6 == 0)
      send_to_char ("\r\n");
  }

  if (col % 6 != 0)
    send_to_char ("\r\n");
  sqlite3_finalize(stmt);
  return;
}

/*
 * Contributed by Alander.
 */
void Character::do_commands (std::string argument)
{
  char buf[MAX_STRING_LENGTH];
  std::string buf1;
  int cmd;
  int col;

  col = 0;
  for (cmd = 0; cmd_table[cmd].name[0] != '\0'; cmd++) {
    if (cmd_table[cmd].level < LEVEL_HERO
      && cmd_table[cmd].level <= get_trust ()) {
      snprintf (buf, sizeof buf, "%-12s", cmd_table[cmd].name);
      buf1.append(buf);
      if (++col % 6 == 0)
        buf1.append("\r\n");
    }
  }

  if (col % 6 != 0)
    buf1.append("\r\n");

  send_to_char (buf1);
  return;
}

void Character::do_channels (std::string argument)
{
  std::string arg;

  one_argument (argument, arg);

  if (arg.empty()) {
    if (!is_npc () && IS_SET (actflags, PLR_SILENCE)) {
      send_to_char ("You are silenced.\r\n");
      return;
    }

    send_to_char ("Channels:");

    send_to_char (!IS_SET (deaf, CHANNEL_AUCTION)
      ? " +AUCTION" : " -auction");

    send_to_char (!IS_SET (deaf, CHANNEL_CHAT)
      ? " +CHAT" : " -chat");

    if (is_hero()) {
      send_to_char (!IS_SET (deaf, CHANNEL_IMMTALK)
        ? " +IMMTALK" : " -immtalk");
    }

    send_to_char (!IS_SET (deaf, CHANNEL_MUSIC)
      ? " +MUSIC" : " -music");

    send_to_char (!IS_SET (deaf, CHANNEL_QUESTION)
      ? " +QUESTION" : " -question");

    send_to_char (!IS_SET (deaf, CHANNEL_SHOUT)
      ? " +SHOUT" : " -shout");

    send_to_char (!IS_SET (deaf, CHANNEL_YELL)
      ? " +YELL" : " -yell");

    send_to_char (".\r\n");
  } else {
    bool fClear;
    int bit;

    if (arg[0] == '+')
      fClear = true;
    else if (arg[0] == '-')
      fClear = false;
    else {
      send_to_char ("Channels -channel or +channel?\r\n");
      return;
    }

    if (!str_cmp (arg.substr(1), "auction"))
      bit = CHANNEL_AUCTION;
    else if (!str_cmp (arg.substr(1), "chat"))
      bit = CHANNEL_CHAT;
    else if (!str_cmp (arg.substr(1), "immtalk"))
      bit = CHANNEL_IMMTALK;
    else if (!str_cmp (arg.substr(1), "music"))
      bit = CHANNEL_MUSIC;
    else if (!str_cmp (arg.substr(1), "question"))
      bit = CHANNEL_QUESTION;
    else if (!str_cmp (arg.substr(1), "shout"))
      bit = CHANNEL_SHOUT;
    else if (!str_cmp (arg.substr(1), "yell"))
      bit = CHANNEL_YELL;
    else {
      send_to_char ("Set or clear which channel?\r\n");
      return;
    }

    if (fClear)
      REMOVE_BIT (deaf, bit);
    else
      SET_BIT (deaf, bit);

    send_to_char ("Ok.\r\n");
  }

  return;
}

/*
 * Contributed by Grodyn.
 */
void Character::do_config (std::string argument)
{
  if (is_npc ())
    return;

  std::string arg;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("[ Keyword  ] Option\r\n");

    send_to_char (IS_SET (actflags, PLR_AUTOEXIT)
      ? "[+AUTOEXIT ] You automatically see exits.\r\n"
      : "[-autoexit ] You don't automatically see exits.\r\n");

    send_to_char (IS_SET (actflags, PLR_AUTOLOOT)
      ? "[+AUTOLOOT ] You automatically loot corpses.\r\n"
      : "[-autoloot ] You don't automatically loot corpses.\r\n");

    send_to_char (IS_SET (actflags, PLR_AUTOSAC)
      ? "[+AUTOSAC  ] You automatically sacrifice corpses.\r\n"
      : "[-autosac  ] You don't automatically sacrifice corpses.\r\n");

    send_to_char (IS_SET (actflags, PLR_BLANK)
      ? "[+BLANK    ] You have a blank line before your prompt.\r\n"
      : "[-blank    ] You have no blank line before your prompt.\r\n");

    send_to_char (IS_SET (actflags, PLR_BRIEF)
      ? "[+BRIEF    ] You see brief descriptions.\r\n"
      : "[-brief    ] You see long descriptions.\r\n");

    send_to_char (IS_SET (actflags, PLR_COMBINE)
      ? "[+COMBINE  ] You see object lists in combined format.\r\n"
      : "[-combine  ] You see object lists in single format.\r\n");

    send_to_char (IS_SET (actflags, PLR_PROMPT)
      ? "[+PROMPT   ] You have a prompt.\r\n"
      : "[-prompt   ] You don't have a prompt.\r\n");

    send_to_char (IS_SET (actflags, PLR_TELNET_GA)
      ? "[+TELNETGA ] You receive a telnet GA sequence.\r\n"
      : "[-telnetga ] You don't receive a telnet GA sequence.\r\n");

    send_to_char (IS_SET (actflags, PLR_SILENCE)
      ? "[+SILENCE  ] You are silenced.\r\n" : "");

    send_to_char (!IS_SET (actflags, PLR_NO_EMOTE)
      ? "" : "[-emote    ] You can't emote.\r\n");

    send_to_char (!IS_SET (actflags, PLR_NO_TELL)
      ? "" : "[-tell     ] You can't use 'tell'.\r\n");
  } else {
    bool fSet;
    int bit;

    if (arg[0] == '+')
      fSet = true;
    else if (arg[0] == '-')
      fSet = false;
    else {
      send_to_char ("Config -option or +option?\r\n");
      return;
    }

    if (!str_cmp (arg.substr(1), "autoexit"))
      bit = PLR_AUTOEXIT;
    else if (!str_cmp (arg.substr(1), "autoloot"))
      bit = PLR_AUTOLOOT;
    else if (!str_cmp (arg.substr(1), "autosac"))
      bit = PLR_AUTOSAC;
    else if (!str_cmp (arg.substr(1), "blank"))
      bit = PLR_BLANK;
    else if (!str_cmp (arg.substr(1), "brief"))
      bit = PLR_BRIEF;
    else if (!str_cmp (arg.substr(1), "combine"))
      bit = PLR_COMBINE;
    else if (!str_cmp (arg.substr(1), "prompt"))
      bit = PLR_PROMPT;
    else if (!str_cmp (arg.substr(1), "telnetga"))
      bit = PLR_TELNET_GA;
    else {
      send_to_char ("Config which option?\r\n");
      return;
    }

    if (fSet)
      SET_BIT (actflags, bit);
    else
      REMOVE_BIT (actflags, bit);

    send_to_char ("Ok.\r\n");
  }

  return;
}

void Character::do_wizlist (std::string argument)
{
  do_help ("wizlist");
  return;
}

void Character::do_spells (std::string argument)
{
  char buf[MAX_STRING_LENGTH];
  std::string buf1;

  if ((!is_npc () && !class_table[klass].fMana)
    || is_npc ()) {
    send_to_char ("You do not know how to cast spells!\r\n");
    return;
  }

  int col = 0;
  for (int sn = 0; sn < MAX_SKILL; sn++) {
    if (skill_table[sn].name == NULL)
      break;
    if ((level < skill_table[sn].skill_level[klass])
      || (skill_table[sn].skill_level[klass] > LEVEL_HERO))
      continue;

    snprintf (buf, sizeof buf, "%18s %3dpts ", skill_table[sn].name, mana_cost (sn));
    buf1.append(buf);
    if (++col % 3 == 0)
      buf1.append("\r\n");
  }

  if (col % 3 != 0)
    buf1.append("\r\n");

  send_to_char (buf1);
  return;

}

void Character::do_slist (std::string argument)
{
  if ((!is_npc () && !class_table[klass].fMana) || is_npc ()) {
    send_to_char ("You do not need any stinking spells!\r\n");
    return;
  }

  std::string buf1;
  buf1.append("ALL Spells available for your class.\r\n\r\n");
  buf1.append("Lv          Spells\r\n\r\n");

  for (int lvl = 1; lvl < LEVEL_IMMORTAL; lvl++) {
    int col = 0;
    bool pSpell = true;
    char buf[MAX_STRING_LENGTH];

    for (int sn = 0; sn < MAX_SKILL; sn++) {
      if (skill_table[sn].name == NULL)
        break;
      if (skill_table[sn].skill_level[klass] != lvl)
        continue;

      if (pSpell) {
        snprintf (buf, sizeof buf, "%2d:", level);
        buf1.append(buf);
        pSpell = false;
      }

      if (++col % 5 == 0)
        buf1.append("   ");

      snprintf (buf, sizeof buf, "%18s", skill_table[sn].name);
      buf1.append(buf);

      if (col % 4 == 0)
        buf1.append("\r\n");

    }

    if (col % 4 != 0)
      buf1.append("\r\n");
  }
  send_to_char (buf1);
  return;
}

/* by passing the conf command - Kahn */
void Character::do_autoexit (std::string argument)
{
  (IS_SET (actflags, PLR_AUTOEXIT)
    ? do_config ("-autoexit")
    : do_config ("+autoexit"));
}

void Character::do_autoloot (std::string argument)
{
  (IS_SET (actflags, PLR_AUTOLOOT)
    ? do_config ("-autoloot")
    : do_config ("+autoloot"));
}

void Character::do_autosac (std::string argument)
{
  (IS_SET (actflags, PLR_AUTOSAC)
    ? do_config ("-autosac")
    : do_config ("+autosac"));
}

void Character::do_blank (std::string argument)
{
  (IS_SET (actflags, PLR_BLANK)
    ? do_config ("-blank")
    : do_config ("+blank"));
}

void Character::do_brief (std::string argument)
{
  (IS_SET (actflags, PLR_BRIEF)
    ? do_config ("-brief")
    : do_config ("+brief"));
}

void Character::do_combine (std::string argument)
{
  (IS_SET (actflags, PLR_COMBINE)
    ? do_config ("-combine")
    : do_config ("+combine"));
}

void Character::do_pagelen (std::string argument)
{
  char buf[MAX_STRING_LENGTH];
  std::string arg;
  int lines;

  one_argument (argument, arg);

  if (arg.empty())
    lines = 20;
  else
    lines = std::atoi (arg.c_str());

  if (lines < 1) {
    send_to_char
      ("Negative or Zero values for a page pause is not legal.\r\n");
    return;
  }

  pcdata->pagelen = lines;
  snprintf (buf, sizeof buf, "Page pause set to %d lines.\r\n", lines);
  send_to_char (buf);
  return;
}

/* Do_prompt from Morgenes from Aldara Mud */
void Character::do_prompt (std::string argument)
{
  if (argument.empty()) {
    (IS_SET (actflags, PLR_PROMPT)
      ? do_config ("-prompt")
      : do_config ("+prompt"));
    return;
  }

  std::string buf;

  if (!strcmp (argument.c_str(), "all"))
    buf = "<%hhp %mm %vmv> ";
  else {
    smash_tilde (argument);
    if (argument.size() > 50)
      argument.erase(50);
    buf = argument;
  }

  prompt = buf;
  send_to_char ("Ok.\r\n");
  return;
}

void Character::do_auto (std::string argument)
{
  do_config ("");
  return;
}

void Character::do_north (std::string argument)
{
  move_char (DIR_NORTH);
  return;
}

void Character::do_east (std::string argument)
{
  move_char (DIR_EAST);
  return;
}

void Character::do_south (std::string argument)
{
  move_char (DIR_SOUTH);
  return;
}

void Character::do_west (std::string argument)
{
  move_char (DIR_WEST);
  return;
}

void Character::do_up (std::string argument)
{
  move_char (DIR_UP);
  return;
}

void Character::do_down (std::string argument)
{
  move_char (DIR_DOWN);
  return;
}

void Character::do_open (std::string argument)
{
  std::string arg;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Open what?\r\n");
    return;
  }

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

    REMOVE_BIT (obj->value[1], CONT_CLOSED);
    send_to_char ("Ok.\r\n");
    act ("$n opens $p.", obj, NULL, TO_ROOM);
    return;
  }

  int door;
  if ((door = find_door (arg)) >= 0) {
    /* 'open door' */
    Room *to_room;
    Exit *pexit;
    Exit *pexit_rev;

    pexit = in_room->exit[door];
    if (!IS_SET (pexit->exit_info, EX_CLOSED)) {
      send_to_char ("It's already open.\r\n");
      return;
    }
    if (IS_SET (pexit->exit_info, EX_LOCKED)) {
      send_to_char ("It's locked.\r\n");
      return;
    }

    REMOVE_BIT (pexit->exit_info, EX_CLOSED);
    act ("$n opens the $d.", NULL, pexit->name.c_str(), TO_ROOM);
    send_to_char ("Ok.\r\n");

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

      REMOVE_BIT (pexit_rev->exit_info, EX_CLOSED);
      CharIter rch;
      for (rch = to_room->people.begin(); rch != to_room->people.end(); rch++)
        (*rch)->act ("The $d opens.", NULL, pexit_rev->name.c_str(), TO_CHAR);
    }
  }

  return;
}

void Character::do_close (std::string argument)
{
  std::string arg;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Close what?\r\n");
    return;
  }

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

    SET_BIT (obj->value[1], CONT_CLOSED);
    send_to_char ("Ok.\r\n");
    act ("$n closes $p.", obj, NULL, TO_ROOM);
    return;
  }

  int door;
  if ((door = find_door (arg)) >= 0) {
    /* 'close door' */
    Room *to_room;
    Exit *pexit;
    Exit *pexit_rev;

    pexit = in_room->exit[door];
    if (IS_SET (pexit->exit_info, EX_CLOSED)) {
      send_to_char ("It's already closed.\r\n");
      return;
    }

    SET_BIT (pexit->exit_info, EX_CLOSED);
    act ("$n closes the $d.", NULL, pexit->name.c_str(), TO_ROOM);
    send_to_char ("Ok.\r\n");

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

      SET_BIT (pexit_rev->exit_info, EX_CLOSED);
      CharIter rch;
      for (rch = to_room->people.begin(); rch != to_room->people.end(); rch++)
        (*rch)->act ("The $d closes.", NULL, pexit_rev->name.c_str(), TO_CHAR);
    }
  }

  return;
}

void Character::do_lock (std::string argument)
{
  std::string arg;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Lock what?\r\n");
    return;
  }

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

    SET_BIT (obj->value[1], CONT_LOCKED);
    send_to_char ("*Click*\r\n");
    act ("$n locks $p.", obj, NULL, TO_ROOM);
    return;
  }

  int door;
  if ((door = find_door (arg)) >= 0) {
    /* 'lock door' */
    Room *to_room;
    Exit *pexit;
    Exit *pexit_rev;

    pexit = in_room->exit[door];
    if (!IS_SET (pexit->exit_info, EX_CLOSED)) {
      send_to_char ("It's not closed.\r\n");
      return;
    }
    if (pexit->key < 0) {
      send_to_char ("It can't be locked.\r\n");
      return;
    }
    if (!has_key(pexit->key)) {
      send_to_char ("You lack the key.\r\n");
      return;
    }
    if (IS_SET (pexit->exit_info, EX_LOCKED)) {
      send_to_char ("It's already locked.\r\n");
      return;
    }

    SET_BIT (pexit->exit_info, EX_LOCKED);
    send_to_char ("*Click*\r\n");
    act ("$n locks the $d.", NULL, pexit->name.c_str(), TO_ROOM);

    /* lock the other side */
    if ((to_room = pexit->to_room) != NULL
      && (pexit_rev = to_room->exit[rev_dir[door]]) != 0
      && pexit_rev->to_room == in_room) {
      SET_BIT (pexit_rev->exit_info, EX_LOCKED);
    }
  }

  return;
}

void Character::do_unlock (std::string argument)
{
  std::string arg;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Unlock what?\r\n");
    return;
  }

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

    REMOVE_BIT (obj->value[1], CONT_LOCKED);
    send_to_char ("*Click*\r\n");
    act ("$n unlocks $p.", obj, NULL, TO_ROOM);
    return;
  }

  int door;
  if ((door = find_door (arg)) >= 0) {
    /* 'unlock door' */
    Room *to_room;
    Exit *pexit;
    Exit *pexit_rev;

    pexit = in_room->exit[door];
    if (!IS_SET (pexit->exit_info, EX_CLOSED)) {
      send_to_char ("It's not closed.\r\n");
      return;
    }
    if (pexit->key < 0) {
      send_to_char ("It can't be unlocked.\r\n");
      return;
    }
    if (!has_key(pexit->key)) {
      send_to_char ("You lack the key.\r\n");
      return;
    }
    if (!IS_SET (pexit->exit_info, EX_LOCKED)) {
      send_to_char ("It's already unlocked.\r\n");
      return;
    }

    REMOVE_BIT (pexit->exit_info, EX_LOCKED);
    send_to_char ("*Click*\r\n");
    act ("$n unlocks the $d.", NULL, pexit->name.c_str(), TO_ROOM);

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

  return;
}

void Character::do_pick (std::string argument)
{
  std::string arg;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Pick what?\r\n");
    return;
  }

  wait_state (skill_table[skill_lookup("pick lock")].beats);

  /* look for guards */
  CharIter rch;
  for (rch = in_room->people.begin(); rch != in_room->people.end(); rch++) {
    if ((*rch)->is_npc () && (*rch)->is_awake () && level + 5 < (*rch)->level) {
      act ("$N is standing too close to the lock.", NULL, *rch, TO_CHAR);
      return;
    }
  }

  if (!is_npc () && number_percent () > pcdata->learned[skill_lookup("pick lock")]) {
    send_to_char ("You failed.\r\n");
    return;
  }

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

    REMOVE_BIT (obj->value[1], CONT_LOCKED);
    send_to_char ("*Click*\r\n");
    act ("$n picks $p.", obj, NULL, TO_ROOM);
    return;
  }

  int door;
  if ((door = find_door (arg)) >= 0) {
    /* 'pick door' */
    Room *to_room;
    Exit *pexit;
    Exit *pexit_rev;

    pexit = in_room->exit[door];
    if (!IS_SET (pexit->exit_info, EX_CLOSED)) {
      send_to_char ("It's not closed.\r\n");
      return;
    }
    if (pexit->key < 0) {
      send_to_char ("It can't be picked.\r\n");
      return;
    }
    if (!IS_SET (pexit->exit_info, EX_LOCKED)) {
      send_to_char ("It's already unlocked.\r\n");
      return;
    }
    if (IS_SET (pexit->exit_info, EX_PICKPROOF)) {
      send_to_char ("You failed.\r\n");
      return;
    }

    REMOVE_BIT (pexit->exit_info, EX_LOCKED);
    send_to_char ("*Click*\r\n");
    act ("$n picks the $d.", NULL, pexit->name.c_str(), TO_ROOM);

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

  return;
}

void Character::do_stand (std::string argument)
{
  switch (position) {
  case POS_SLEEPING:
    if (is_affected (AFF_SLEEP)) {
      send_to_char ("You can't wake up!\r\n");
      return;
    }

    send_to_char ("You wake and stand up.\r\n");
    act ("$n wakes and stands up.", NULL, NULL, TO_ROOM);
    position = POS_STANDING;
    break;

  case POS_RESTING:
    send_to_char ("You stand up.\r\n");
    act ("$n stands up.", NULL, NULL, TO_ROOM);
    position = POS_STANDING;
    break;

  case POS_STANDING:
    send_to_char ("You are already standing.\r\n");
    break;

  case POS_FIGHTING:
    send_to_char ("You are already fighting!\r\n");
    break;
  }

  return;
}

void Character::do_rest (std::string argument)
{
  switch (position) {
  case POS_SLEEPING:
    send_to_char ("You are already sleeping.\r\n");
    break;

  case POS_RESTING:
    send_to_char ("You are already resting.\r\n");
    break;

  case POS_STANDING:
    send_to_char ("You rest.\r\n");
    act ("$n rests.", NULL, NULL, TO_ROOM);
    position = POS_RESTING;
    break;

  case POS_FIGHTING:
    send_to_char ("You are already fighting!\r\n");
    break;
  }

  return;
}

void Character::do_sleep (std::string argument)
{
  switch (position) {
  case POS_SLEEPING:
    send_to_char ("You are already sleeping.\r\n");
    break;

  case POS_RESTING:
  case POS_STANDING:
    send_to_char ("You sleep.\r\n");
    act ("$n sleeps.", NULL, NULL, TO_ROOM);
    position = POS_SLEEPING;
    break;

  case POS_FIGHTING:
    send_to_char ("You are already fighting!\r\n");
    break;
  }

  return;
}

void Character::do_wake (std::string argument)
{
  std::string arg;

  one_argument (argument, arg);
  if (arg.empty()) {
    do_stand (argument);
    return;
  }

  if (!is_awake ()) {
    send_to_char ("You are asleep yourself!\r\n");
    return;
  }

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

  if (victim->is_awake ()) {
    act ("$N is already awake.", NULL, victim, TO_CHAR);
    return;
  }

  if (victim->is_affected (AFF_SLEEP)) {
    act ("You can't wake $M!", NULL, victim, TO_CHAR);
    return;
  }

  act ("You wake $M.", NULL, victim, TO_CHAR);
  act ("$n wakes you.", NULL, victim, TO_VICT);
  victim->position = POS_STANDING;
  return;
}

void Character::do_sneak (std::string argument)
{
  Affect af;
  int snk = skill_lookup("sneak");

  send_to_char ("You attempt to move silently.\r\n");
  affect_strip (snk);

  if (is_npc () || number_percent () < pcdata->learned[snk]) {
    af.type = snk;
    af.duration = level;
    af.location = APPLY_NONE;
    af.modifier = 0;
    af.bitvector = AFF_SNEAK;
    affect_to_char(&af);
  }

  return;
}

void Character::do_hide (std::string argument)
{
  send_to_char ("You attempt to hide.\r\n");

  if (is_affected (AFF_HIDE))
    REMOVE_BIT (affected_by, AFF_HIDE);

  if (is_npc () || number_percent () < pcdata->learned[skill_lookup("hide")])
    SET_BIT (affected_by, AFF_HIDE);

  return;
}

/*
 * Contributed by Alander.
 */
void Character::do_visible (std::string argument)
{
  affect_strip (skill_lookup("invis"));
  affect_strip (skill_lookup("mass invis"));
  affect_strip (skill_lookup("sneak"));
  REMOVE_BIT (affected_by, AFF_HIDE);
  REMOVE_BIT (affected_by, AFF_INVISIBLE);
  REMOVE_BIT (affected_by, AFF_SNEAK);
  send_to_char ("Ok.\r\n");
  return;
}

void Character::do_recall (std::string argument)
{
  char buf[MAX_STRING_LENGTH];
  Room *location;

  act ("$n prays for transportation!", 0, 0, TO_ROOM);

  if ((location = get_room_index (ROOM_VNUM_TEMPLE)) == NULL) {
    send_to_char ("You are completely lost.\r\n");
    return;
  }

  if (in_room == location)
    return;

  if (IS_SET (in_room->room_flags, ROOM_NO_RECALL)
    || is_affected (AFF_CURSE)) {
    send_to_char ("God has forsaken you.\r\n");
    return;
  }

  if (fighting != NULL) {
    int lose;

    if (number_percent() <= 50) {
      wait_state (4);
      lose = (desc != NULL) ? 50 : 100;
      gain_exp(0 - lose);
      snprintf (buf, sizeof buf, "You failed!  You lose %d exps.\r\n", lose);
      send_to_char (buf);
      return;
    }

    lose = (desc != NULL) ? 100 : 200;
    gain_exp(0 - lose);
    snprintf (buf, sizeof buf, "You recall from combat!  You lose %d exps.\r\n", lose);
    send_to_char (buf);
    stop_fighting(true);
  }

  move /= 2;
  act ("$n disappears.", NULL, NULL, TO_ROOM);
  char_from_room();
  char_to_room(location);
  act ("$n appears in the room.", NULL, NULL, TO_ROOM);
  do_look ("auto");

  return;
}

void Character::do_train (std::string argument)
{
  if (is_npc ())
    return;

  std::string buf;
  sh_int ability;
  const char *pOutput;

  /*
   * Check for trainer.
   */
  CharIter mob;
  for (mob = in_room->people.begin(); mob != in_room->people.end(); mob++) {
    if ((*mob)->is_npc () && IS_SET ((*mob)->actflags, ACT_TRAIN))
      break;
  }

  if (mob == in_room->people.end()) {
    send_to_char ("You can't do that here.\r\n");
    return;
  }

  if (argument.empty()) {
    buf = "You have " + itoa(practice, 10) + " practice sessions.\r\n";
    send_to_char (buf);
    argument = "foo";
  }

  int cost = 5;

  if (!str_cmp (argument, "str")) {
    if (class_table[klass].attr_prime == APPLY_STR)
      cost = 3;
    ability = pcdata->get_perm(argument);
    pOutput = "strength";
  } else if (!str_cmp (argument, "int")) {
    if (class_table[klass].attr_prime == APPLY_INT)
      cost = 3;
    ability = pcdata->get_perm(argument);
    pOutput = "intelligence";
  } else if (!str_cmp (argument, "wis")) {
    if (class_table[klass].attr_prime == APPLY_WIS)
      cost = 3;
    ability = pcdata->get_perm(argument);
    pOutput = "wisdom";
  } else if (!str_cmp (argument, "dex")) {
    if (class_table[klass].attr_prime == APPLY_DEX)
      cost = 3;
    ability = pcdata->get_perm(argument);
    pOutput = "dexterity";
  } else if (!str_cmp (argument, "con")) {
    if (class_table[klass].attr_prime == APPLY_CON)
      cost = 3;
    ability = pcdata->get_perm(argument);
    pOutput = "constitution";
  } else {
    buf = "You can train:";
    buf.append(pcdata->trainable_list());

    if (buf[buf.size() - 1] != ':') {
      buf.append(".\r\n");
      send_to_char (buf);
    } else {
      /*
       * This message dedicated to Jordan ... you big stud!
       */
      act ("You have nothing left to train, you $T!",
        NULL,
        sex == SEX_MALE ? "big stud" :
        sex == SEX_FEMALE ? "hot babe" : "wild thing", TO_CHAR);
    }

    return;
  }

  if (ability >= 18) {
    act ("Your $T is already at maximum.", NULL, pOutput, TO_CHAR);
    return;
  }

  if (cost > practice) {
    send_to_char ("You don't have enough practices.\r\n");
    return;
  }

  practice -= cost;
  pcdata->set_perm(argument, ability+1);
  act ("Your $T increases!", NULL, pOutput, TO_CHAR);
  act ("$n's $T increases!", NULL, pOutput, TO_ROOM);
  return;
}

void Character::do_get (std::string argument)
{
  std::string arg1;
  std::string arg2;

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

  /* Get type. */
  if (arg1.empty()) {
    send_to_char ("Get what?\r\n");
    return;
  }

  Object *obj;
  Object *container;
  bool found;
  if (arg2.empty()) {
    if (str_cmp (arg1, "all") && str_prefix ("all.", arg1)) {
      /* 'get obj' */
      obj = get_obj_list (arg1, in_room->contents);
      if (obj == NULL) {
        act ("I see no $T here.", NULL, arg1.c_str(), TO_CHAR);
        return;
      }

      get_obj (obj, NULL);
    } else {
      /* 'get all' or 'get all.obj' */
      found = false;
      ObjIter o, onext;
      for (o = in_room->contents.begin(); o != in_room->contents.end(); o = onext) {
        obj = *o;
        onext = ++o;
        if ((arg1[3] == '\0' || is_name (&arg1[4], obj->name))
          && can_see_obj(obj)) {
          found = true;
          get_obj (obj, NULL);
        }
      }

      if (!found) {
        if (arg1[3] == '\0')
          send_to_char ("I see nothing here.\r\n");
        else
          act ("I see no $T here.", NULL, &arg1[4], TO_CHAR);
      }
    }
  } else {
    /* 'get ... container' */
    if (!str_cmp (arg2, "all") || !str_prefix ("all.", arg2)) {
      send_to_char ("You can't do that.\r\n");
      return;
    }

    if ((container = get_obj_here (arg2)) == NULL) {
      act ("I see no $T here.", NULL, arg2.c_str(), TO_CHAR);
      return;
    }

    switch (container->item_type) {
    default:
      send_to_char ("That's not a container.\r\n");
      return;

    case ITEM_CONTAINER:
    case ITEM_CORPSE_NPC:
      break;

    case ITEM_CORPSE_PC:
      {
        std::string nm;
        std::string pd;

        if (is_npc ()) {
          send_to_char ("You can't do that.\r\n");
          return;
        }

        pd = container->short_descr;
        pd = one_argument (pd, nm);
        pd = one_argument (pd, nm);
        pd = one_argument (pd, nm);

        if (str_cmp (nm, name) && !is_immortal()) {
          bool fGroup;

          fGroup = false;
          CharIter c;
          for (c = char_list.begin(); c != char_list.end(); c++) {
            if (!(*c)->is_npc ()
              && is_same_group (this, *c)
              && !str_cmp (nm, (*c)->name)) {
              fGroup = true;
              break;
            }
          }

          if (!fGroup) {
            send_to_char ("You can't do that.\r\n");
            return;
          }
        }
      }
    }

    if (IS_SET (container->value[1], CONT_CLOSED)) {
      act ("The $d is closed.", NULL, container->name.c_str(), TO_CHAR);
      return;
    }

    if (str_cmp (arg1, "all") && str_prefix ("all.", arg1)) {
      /* 'get obj container' */
      obj = get_obj_list (arg1, container->contains);
      if (obj == NULL) {
        act ("I see nothing like that in the $T.", NULL, arg2.c_str(), TO_CHAR);
        return;
      }
      get_obj (obj, container);
    } else {
      /* 'get all container' or 'get all.obj container' */
      found = false;
      ObjIter o, onext;
      for (o = container->contains.begin(); o != container->contains.end(); o = onext) {
        obj = *o;
        onext = ++o;
        if ((arg1[3] == '\0' || is_name (&arg1[4], obj->name))
          && can_see_obj(obj)) {
          found = true;
          get_obj (obj, container);
        }
      }

      if (!found) {
        if (arg1[3] == '\0')
          act ("I see nothing in the $T.", NULL, arg2.c_str(), TO_CHAR);
        else
          act ("I see nothing like that in the $T.", NULL, arg2.c_str(), TO_CHAR);
      }
    }
  }

  return;
}

void Character::do_put (std::string argument)
{
  std::string arg1;
  std::string arg2;

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

  if (arg1.empty() || arg2.empty()) {
    send_to_char ("Put what in what?\r\n");
    return;
  }

  if (!str_cmp (arg2, "all") || !str_prefix ("all.", arg2)) {
    send_to_char ("You can't do that.\r\n");
    return;
  }

  Object *container;
  if ((container = get_obj_here (arg2)) == NULL) {
    act ("I see no $T here.", NULL, arg2.c_str(), TO_CHAR);
    return;
  }

  if (container->item_type != ITEM_CONTAINER) {
    send_to_char ("That's not a container.\r\n");
    return;
  }

  if (IS_SET (container->value[1], CONT_CLOSED)) {
    act ("The $d is closed.", NULL, container->name.c_str(), TO_CHAR);
    return;
  }

  Object *obj;
  if (str_cmp (arg1, "all") && str_prefix ("all.", arg1)) {
    /* 'put obj container' */
    if ((obj = get_obj_carry (arg1)) == NULL) {
      send_to_char ("You do not have that item.\r\n");
      return;
    }

    if (obj == container) {
      send_to_char ("You can't fold it into itself.\r\n");
      return;
    }

    if (!can_drop_obj (obj)) {
      send_to_char ("You can't let go of it.\r\n");
      return;
    }

    if (obj->get_obj_weight() + container->get_obj_weight()
      > container->value[0]) {
      send_to_char ("It won't fit.\r\n");
      return;
    }

    obj->obj_from_char();
    obj->obj_to_obj (container);
    act ("$n puts $p in $P.", obj, container, TO_ROOM);
    act ("You put $p in $P.", obj, container, TO_CHAR);
  } else {
    /* 'put all container' or 'put all.obj container' */
    ObjIter o, onext;
    for (o = carrying.begin(); o != carrying.end(); o = onext) {
      obj = *o;
      onext = ++o;

      if ((arg1[3] == '\0' || is_name (&arg1[4], obj->name))
        && can_see_obj(obj)
        && obj->wear_loc == WEAR_NONE
        && obj != container && can_drop_obj (obj)
        && obj->get_obj_weight() + container->get_obj_weight()
        <= container->value[0]) {
        obj->obj_from_char ();
        obj->obj_to_obj (container);
        act ("$n puts $p in $P.", obj, container, TO_ROOM);
        act ("You put $p in $P.", obj, container, TO_CHAR);
      }
    }
  }

  return;
}

void Character::do_drop (std::string argument)
{
  std::string arg;

  argument = one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Drop what?\r\n");
    return;
  }

  Object *obj;
  bool found;
  if (is_number (arg)) {
    /* 'drop NNNN coins' */
    int amount;

    amount = std::atoi (arg.c_str());
    argument = one_argument (argument, arg);
    if (amount <= 0 || (str_cmp (arg, "coins") && str_cmp (arg, "coin"))) {
      send_to_char ("Sorry, you can't do that.\r\n");
      return;
    }

    if (gold < amount) {
      send_to_char ("You haven't got that many coins.\r\n");
      return;
    }

    gold -= amount;

    ObjIter o, onext;
    for (o = in_room->contents.begin(); o != in_room->contents.end(); o = onext) {
      obj = *o;
      onext = ++o;

      switch (obj->pIndexData->vnum) {
      case OBJ_VNUM_MONEY_ONE:
        amount += 1;
        obj->extract_obj ();
        break;

      case OBJ_VNUM_MONEY_SOME:
        amount += obj->value[0];
        obj->extract_obj ();
        break;
      }
    }

    create_money (amount)->obj_to_room (in_room);
    act ("$n drops some gold.", NULL, NULL, TO_ROOM);
    send_to_char ("OK.\r\n");
    return;
  }

  if (str_cmp (arg, "all") && str_prefix ("all.", arg)) {
    /* 'drop obj' */
    if ((obj = get_obj_carry (arg)) == NULL) {
      send_to_char ("You do not have that item.\r\n");
      return;
    }

    if (!can_drop_obj (obj)) {
      send_to_char ("You can't let go of it.\r\n");
      return;
    }

    obj->obj_from_char();
    obj->obj_to_room (in_room);
    act ("$n drops $p.", obj, NULL, TO_ROOM);
    act ("You drop $p.", obj, NULL, TO_CHAR);
  } else {
    /* 'drop all' or 'drop all.obj' */
    found = false;
    ObjIter o, onext;
    for (o = carrying.begin(); o != carrying.end(); o = onext) {
      obj = *o;
      onext = ++o;

      if ((arg[3] == '\0' || is_name (&arg[4], obj->name))
        && can_see_obj(obj)
        && obj->wear_loc == WEAR_NONE && can_drop_obj (obj)) {
        found = true;
        obj->obj_from_char();
        obj->obj_to_room(in_room);
        act ("$n drops $p.", obj, NULL, TO_ROOM);
        act ("You drop $p.", obj, NULL, TO_CHAR);
      }
    }

    if (!found) {
      if (arg[3] == '\0')
        act ("You are not carrying anything.", NULL, arg.c_str(), TO_CHAR);
      else
        act ("You are not carrying any $T.", NULL, &arg[4], TO_CHAR);
    }
  }

  return;
}

void Character::do_give (std::string argument)
{
  std::string arg1;
  std::string arg2;

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

  if (arg1.empty() || arg2.empty()) {
    send_to_char ("Give what to whom?\r\n");
    return;
  }

  Character *victim;
  Object *obj;
  if (is_number (arg1)) {
    /* 'give NNNN coins victim' */
    int amount;

    amount = std::atoi (arg1.c_str());
    if (amount <= 0 || (str_cmp (arg2, "coins") && str_cmp (arg2, "coin"))) {
      send_to_char ("Sorry, you can't do that.\r\n");
      return;
    }

    argument = one_argument (argument, arg2);
    if (arg2.empty()) {
      send_to_char ("Give what to whom?\r\n");
      return;
    }

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

    if (gold < amount) {
      send_to_char ("You haven't got that much gold.\r\n");
      return;
    }

    gold -= amount;
    victim->gold += amount;
    act ("$n gives you some gold.", NULL, victim, TO_VICT);
    act ("$n gives $N some gold.", NULL, victim, TO_NOTVICT);
    act ("You give $N some gold.", NULL, victim, TO_CHAR);
    send_to_char ("OK.\r\n");
    mprog_bribe_trigger (victim, this, amount);
    return;
  }

  if ((obj = get_obj_carry (arg1)) == NULL) {
    send_to_char ("You do not have that item.\r\n");
    return;
  }

  if (obj->wear_loc != WEAR_NONE) {
    send_to_char ("You must remove it first.\r\n");
    return;
  }

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

  if (!can_drop_obj (obj)) {
    send_to_char ("You can't let go of it.\r\n");
    return;
  }

  if (victim->carry_number + obj->get_obj_number() > victim->can_carry_n()) {
    act ("$N has $S hands full.", NULL, victim, TO_CHAR);
    return;
  }

  if (victim->carry_weight + obj->get_obj_weight() > victim->can_carry_w()) {
    act ("$N can't carry that much weight.", NULL, victim, TO_CHAR);
    return;
  }

  if (!victim->can_see_obj(obj)) {
    act ("$N can't see it.", NULL, victim, TO_CHAR);
    return;
  }

  obj->obj_from_char ();
  obj->obj_to_char (victim);
  MOBtrigger = false;
  act ("$n gives $p to $N.", obj, victim, TO_NOTVICT);
  act ("$n gives you $p.", obj, victim, TO_VICT);
  act ("You give $p to $N.", obj, victim, TO_CHAR);
  mprog_give_trigger (victim, this, obj);
  return;
}

void Character::do_fill (std::string argument)
{
  std::string arg;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Fill what?\r\n");
    return;
  }

  Object *obj;
  if ((obj = get_obj_carry (arg)) == NULL) {
    send_to_char ("You do not have that item.\r\n");
    return;
  }

  bool found = false;
  ObjIter o;
  for (o = in_room->contents.begin(); o != in_room->contents.end(); o++) {
    if ((*o)->item_type == ITEM_FOUNTAIN) {
      found = true;
      break;
    }
  }

  if (!found) {
    send_to_char ("There is no fountain here!\r\n");
    return;
  }

  if (obj->item_type != ITEM_DRINK_CON) {
    send_to_char ("You can't fill that.\r\n");
    return;
  }

  if (obj->value[1] != 0 && obj->value[2] != 0) {
    send_to_char ("There is already another liquid in it.\r\n");
    return;
  }

  if (obj->value[1] >= obj->value[0]) {
    send_to_char ("Your container is full.\r\n");
    return;
  }

  act ("You fill $p.", obj, NULL, TO_CHAR);
  obj->value[2] = 0;
  obj->value[1] = obj->value[0];
  return;
}

void Character::do_drink (std::string argument)
{
  std::string arg;
  Object *obj = NULL;
  int amount;
  int liquid;

  one_argument (argument, arg);

  if (arg.empty()) {
    ObjIter o;
    for (o = in_room->contents.begin(); o != in_room->contents.end(); o++) {
      obj = *o;
      if (obj->item_type == ITEM_FOUNTAIN)
        break;
    }

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

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

  switch (obj->item_type) {
  default:
    send_to_char ("You can't drink from that.\r\n");
    break;

  case ITEM_FOUNTAIN:
    if (!is_npc ())
      pcdata->condition[COND_THIRST] = 48;
    act ("$n drinks from the fountain.", NULL, NULL, TO_ROOM);
    send_to_char ("You are not thirsty.\r\n");
    break;

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

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

    act ("$n drinks $T from $p.",
      obj, liq_table[liquid].liq_name, TO_ROOM);
    act ("You drink $T from $p.",
      obj, liq_table[liquid].liq_name, TO_CHAR);

    amount = number_range (3, 10);
    amount = std::min (amount, obj->value[1]);

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

    if (!is_npc () && pcdata->condition[COND_DRUNK] > 10)
      send_to_char ("You feel drunk.\r\n");
    if (!is_npc () && pcdata->condition[COND_FULL] > 40)
      send_to_char ("You are full.\r\n");
    if (!is_npc () && pcdata->condition[COND_THIRST] > 40)
      send_to_char ("You do not feel thirsty.\r\n");

    if (obj->value[3] != 0) {
      /* The shit was poisoned ! */
      Affect af;

      act ("$n chokes and gags.", NULL, NULL, TO_ROOM);
      send_to_char ("You choke and gag.\r\n");
      af.type = skill_lookup("poison");
      af.duration = 3 * amount;
      af.location = APPLY_NONE;
      af.modifier = 0;
      af.bitvector = AFF_POISON;
      affect_join (&af);
    }

    obj->value[1] -= amount;
    if (obj->value[1] <= 0) {
      send_to_char ("The empty container vanishes.\r\n");
      obj->extract_obj ();
    }
    break;
  }

  return;
}

void Character::do_eat (std::string argument)
{
  std::string arg;

  one_argument (argument, arg);
  if (arg.empty()) {
    send_to_char ("Eat what?\r\n");
    return;
  }

  Object *obj;
  if ((obj = get_obj_carry (arg)) == NULL) {
    send_to_char ("You do not have that item.\r\n");
    return;
  }

  if (!is_immortal()) {
    if (obj->item_type != ITEM_FOOD && obj->item_type != ITEM_PILL) {
      send_to_char ("That's not edible.\r\n");
      return;
    }

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

  act ("$n eats $p.", obj, NULL, TO_ROOM);
  act ("You eat $p.", obj, NULL, TO_CHAR);

  switch (obj->item_type) {

  case ITEM_FOOD:
    if (!is_npc ()) {
      int condition;

      condition = pcdata->condition[COND_FULL];
      gain_condition (COND_FULL, obj->value[0]);
      if (condition == 0 && pcdata->condition[COND_FULL] > 0)
        send_to_char ("You are no longer hungry.\r\n");
      else if (pcdata->condition[COND_FULL] > 40)
        send_to_char ("You are full.\r\n");
    }

    if (obj->value[3] != 0) {
      /* The shit was poisoned! */
      Affect af;

      act ("$n chokes and gags.", 0, 0, TO_ROOM);
      send_to_char ("You choke and gag.\r\n");

      af.type = skill_lookup("poison");
      af.duration = 2 * obj->value[0];
      af.location = APPLY_NONE;
      af.modifier = 0;
      af.bitvector = AFF_POISON;
      affect_join (&af);
    }
    break;

  case ITEM_PILL:
    obj_cast_spell (obj->value[1], obj->value[0], this, this, NULL);
    obj_cast_spell (obj->value[2], obj->value[0], this, this, NULL);
    obj_cast_spell (obj->value[3], obj->value[0], this, this, NULL);
    break;
  }

  obj->extract_obj ();
  return;
}

void Character::do_wear (std::string argument)
{
  std::string arg;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Wear, wield, or hold what?\r\n");
    return;
  }

  Object *obj;
  if (!str_cmp (arg, "all")) {
    ObjIter o, onext;
    for (o = carrying.begin(); o != carrying.end(); o = onext) {
      obj = *o;
      onext = ++o;
      if (obj->wear_loc == WEAR_NONE && can_see_obj(obj))
        wear_obj (obj, false);
    }
    return;
  } else {
    if ((obj = get_obj_carry (arg)) == NULL) {
      send_to_char ("You do not have that item.\r\n");
      return;
    }

    wear_obj (obj, true);
  }

  return;
}

void Character::do_remove (std::string argument)
{
  std::string arg;
  Object *obj;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Remove what?\r\n");
    return;
  }

  if ((obj = get_obj_wear (arg)) == NULL) {
    send_to_char ("You do not have that item.\r\n");
    return;
  }

  remove_obj (obj->wear_loc, true);
  return;
}

void Character::do_sacrifice (std::string argument)
{
  std::string arg;

  one_argument (argument, arg);

  if (arg.empty() || !str_cmp (arg, name)) {
    act ("$n offers $mself to God, who graciously declines.",
      NULL, NULL, TO_ROOM);
    send_to_char ("God appreciates your offer and may accept it later.");
    return;
  }

  Object* obj = get_obj_list (arg, in_room->contents);
  if (obj == NULL) {
    send_to_char ("You can't find it.\r\n");
    return;
  }

  if (!obj->can_wear(ITEM_TAKE)) {
    act ("$p is not an acceptable sacrifice.", obj, 0, TO_CHAR);
    return;
  }

  send_to_char ("God gives you one gold coin for your sacrifice.\r\n");
  gold += 1;

  act ("$n sacrifices $p to God.", obj, NULL, TO_ROOM);
  obj->extract_obj ();
  return;
}

void Character::do_quaff (std::string argument)
{
  std::string arg;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Quaff what?\r\n");
    return;
  }

  Object *obj;
  if ((obj = get_obj_carry (arg)) == NULL) {
    send_to_char ("You do not have that potion.\r\n");
    return;
  }

  if (obj->item_type != ITEM_POTION) {
    send_to_char ("You can quaff only potions.\r\n");
    return;
  }

  act ("$n quaffs $p.", obj, NULL, TO_ROOM);
  act ("You quaff $p.", obj, NULL, TO_CHAR);

  obj_cast_spell (obj->value[1], obj->value[0], this, this, NULL);
  obj_cast_spell (obj->value[2], obj->value[0], this, this, NULL);
  obj_cast_spell (obj->value[3], obj->value[0], this, this, NULL);

  obj->extract_obj ();
  return;
}

void Character::do_recite (std::string argument)
{
  std::string arg1;
  std::string arg2;

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

  Object *scroll;
  if ((scroll = get_obj_carry (arg1)) == NULL) {
    send_to_char ("You do not have that scroll.\r\n");
    return;
  }

  if (scroll->item_type != ITEM_SCROLL) {
    send_to_char ("You can recite only scrolls.\r\n");
    return;
  }

  Character *victim;
  Object *obj = NULL;
  if (arg2.empty()) {
    victim = this;
  } else {
    if ((victim = get_char_room (arg2)) == NULL
      && (obj = get_obj_here (arg2)) == NULL) {
      send_to_char ("You can't find it.\r\n");
      return;
    }
  }

  act ("$n recites $p.", scroll, NULL, TO_ROOM);
  act ("You recite $p.", scroll, NULL, TO_CHAR);

  obj_cast_spell (scroll->value[1], scroll->value[0], this, victim, obj);
  obj_cast_spell (scroll->value[2], scroll->value[0], this, victim, obj);
  obj_cast_spell (scroll->value[3], scroll->value[0], this, victim, obj);

  scroll->extract_obj ();
  return;
}

void Character::do_brandish (std::string argument)
{
  Character *vch;
  Object *staff;

  if ((staff = get_eq_char (WEAR_HOLD)) == NULL) {
    send_to_char ("You hold nothing in your hand.\r\n");
    return;
  }

  if (staff->item_type != ITEM_STAFF) {
    send_to_char ("You can brandish only with a staff.\r\n");
    return;
  }

  int sn;
  if ((sn = staff->value[3]) < 0
    || sn >= MAX_SKILL || skill_table[sn].spell_fun == NULL) {
    bug_printf ("Do_brandish: bad sn %d.", sn);
    return;
  }

  wait_state (2 * PULSE_VIOLENCE);

  if (staff->value[2] > 0) {
    act ("$n brandishes $p.", staff, NULL, TO_ROOM);
    act ("You brandish $p.", staff, NULL, TO_CHAR);
    CharIter rch, next;
    for (rch = in_room->people.begin(); rch != in_room->people.end(); rch = next) {
      vch = *rch;
      next = ++rch;

      switch (skill_table[sn].target) {
      default:
        bug_printf ("Do_brandish: bad target for sn %d.", sn);
        return;

      case TAR_IGNORE:
        if (vch != this)
          continue;
        break;

      case TAR_CHAR_OFFENSIVE:
        if (is_npc () ? vch->is_npc () : !vch->is_npc ())
          continue;
        break;

      case TAR_CHAR_DEFENSIVE:
        if (is_npc () ? !vch->is_npc () : vch->is_npc ())
          continue;
        break;

      case TAR_CHAR_SELF:
        if (vch != this)
          continue;
        break;
      }

      obj_cast_spell (staff->value[3], staff->value[0], this, vch, NULL);
    }
  }

  if (--staff->value[2] <= 0) {
    act ("$n's $p blazes bright and is gone.", staff, NULL, TO_ROOM);
    act ("Your $p blazes bright and is gone.", staff, NULL, TO_CHAR);
    staff->extract_obj ();
  }

  return;
}

void Character::do_zap (std::string argument)
{
  std::string arg;

  one_argument (argument, arg);
  if (arg.empty() && fighting == NULL) {
    send_to_char ("Zap whom or what?\r\n");
    return;
  }

  Object *wand;
  if ((wand = get_eq_char (WEAR_HOLD)) == NULL) {
    send_to_char ("You hold nothing in your hand.\r\n");
    return;
  }

  if (wand->item_type != ITEM_WAND) {
    send_to_char ("You can zap only with a wand.\r\n");
    return;
  }

  Character *victim;
  Object *obj = NULL;
  if (arg.empty()) {
    if (fighting != NULL) {
      victim = fighting;
    } else {
      send_to_char ("Zap whom or what?\r\n");
      return;
    }
  } else {
    if ((victim = get_char_room (arg)) == NULL
      && (obj = get_obj_here (arg)) == NULL) {
      send_to_char ("You can't find it.\r\n");
      return;
    }
  }

  wait_state (2 * PULSE_VIOLENCE);

  if (wand->value[2] > 0) {
    if (victim != NULL) {
      act ("$n zaps $N with $p.", wand, victim, TO_ROOM);
      act ("You zap $N with $p.", wand, victim, TO_CHAR);
    } else {
      act ("$n zaps $P with $p.", wand, obj, TO_ROOM);
      act ("You zap $P with $p.", wand, obj, TO_CHAR);
    }

    obj_cast_spell (wand->value[3], wand->value[0], this, victim, obj);
  }

  if (--wand->value[2] <= 0) {
    act ("$n's $p explodes into fragments.", wand, NULL, TO_ROOM);
    act ("Your $p explodes into fragments.", wand, NULL, TO_CHAR);
    wand->extract_obj ();
  }

  return;
}

void Character::do_steal (std::string argument)
{
  std::string arg1, arg2, buf;
  Character *victim;
  Object *obj;
  int percent;

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

  if (arg1.empty() || arg2.empty()) {
    send_to_char ("Steal what from whom?\r\n");
    return;
  }

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

  if (victim == this) {
    send_to_char ("That's pointless.\r\n");
    return;
  }

  wait_state (skill_table[skill_lookup("steal")].beats);
  percent = number_percent () + (victim->is_awake () ? 10 : -50);

  if (level + 5 < victim->level
    || victim->position == POS_FIGHTING || !victim->is_npc ()
    || (!is_npc () && percent > pcdata->learned[skill_lookup("steal")])) {
    /*
     * Failure.
     */
    send_to_char ("Oops.\r\n");
    act ("$n tried to steal from you.\r\n", NULL, victim, TO_VICT);
    act ("$n tried to steal from $N.\r\n", NULL, victim, TO_NOTVICT);
    buf = name + " is a bloody thief!";
    victim->do_shout (buf);
    if (!is_npc ()) {
      if (victim->is_npc ()) {
        multi_hit (victim, this, TYPE_UNDEFINED);
      } else {
        log_printf (buf.c_str());
        if (!IS_SET (actflags, PLR_THIEF)) {
          SET_BIT (actflags, PLR_THIEF);
          send_to_char ("*** You are now a THIEF!! ***\r\n");
          save_char_obj();
        }
      }
    }

    return;
  }

  if (!str_cmp (arg1, "coin")
    || !str_cmp (arg1, "coins")
    || !str_cmp (arg1, "gold")) {
    int amount;

    amount = victim->gold * number_range (1, 10) / 100;
    if (amount <= 0) {
      send_to_char ("You couldn't get any gold.\r\n");
      return;
    }

    gold += amount;
    victim->gold -= amount;
    buf = "Bingo!  You got " + itoa(amount, 10) + " gold coins.\r\n";
    send_to_char (buf);
    return;
  }

  if ((obj = victim->get_obj_carry (arg1)) == NULL) {
    send_to_char ("You can't find it.\r\n");
    return;
  }

  if (!can_drop_obj (obj)
    || IS_SET (obj->extra_flags, ITEM_INVENTORY)
    || obj->level > level) {
    send_to_char ("You can't pry it away.\r\n");
    return;
  }

  if (carry_number + obj->get_obj_number() > can_carry_n()) {
    send_to_char ("You have your hands full.\r\n");
    return;
  }

  if (carry_weight + obj->get_obj_weight() > can_carry_w()) {
    send_to_char ("You can't carry that much weight.\r\n");
    return;
  }

  obj->obj_from_char ();
  obj->obj_to_char (this);
  send_to_char ("Ok.\r\n");
  return;
}

void Character::do_buy (std::string argument)
{
  std::string arg;

  argument = one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Buy what?\r\n");
    return;
  }

  if (IS_SET (in_room->room_flags, ROOM_PET_SHOP)) {
    std::string buf;
    Character *pet;
    Room *pRoomIndexNext;
    Room *in_rm;

    if (is_npc ())
      return;

    pRoomIndexNext = get_room_index (in_room->vnum + 1);
    if (pRoomIndexNext == NULL) {
      bug_printf ("Do_buy: bad pet shop at vnum %d.", in_room->vnum);
      send_to_char ("Sorry, you can't buy that here.\r\n");
      return;
    }

    in_rm = in_room;
    in_room = pRoomIndexNext;
    pet = get_char_room (arg);
    in_room = in_rm;

    if (pet == NULL || !IS_SET (pet->actflags, ACT_PET)) {
      send_to_char ("Sorry, you can't buy that here.\r\n");
      return;
    }

    if (IS_SET (actflags, PLR_BOUGHT_PET)) {
      send_to_char ("You already bought one pet this level.\r\n");
      return;
    }

    if (gold < 10 * pet->level * pet->level) {
      send_to_char ("You can't afford it.\r\n");
      return;
    }

    if (level < pet->level) {
      send_to_char ("You're not ready for this pet.\r\n");
      return;
    }

    gold -= 10 * pet->level * pet->level;
    pet = pet->pIndexData->create_mobile();
    SET_BIT (actflags, PLR_BOUGHT_PET);
    SET_BIT (pet->actflags, ACT_PET);
    SET_BIT (pet->affected_by, AFF_CHARM);

    argument = one_argument (argument, arg);
    if (!arg.empty()) {
      buf = pet->name + " " + arg;
      pet->name = buf;
    }

    buf = pet->description + "A neck tag says 'I belong to " + name + "'.\r\n";
    pet->description = buf;

    pet->char_to_room(in_room);
    pet->add_follower(this);
    send_to_char ("Enjoy your pet.\r\n");
    act ("$n bought $N as a pet.", NULL, pet, TO_ROOM);
    return;
  } else {
    Character *keeper;
    Object *obj;
    int cost;

    if ((keeper = find_keeper (this)) == NULL)
      return;

    obj = keeper->get_obj_carry (arg);
    cost = get_cost (keeper, obj, true);

    if (cost <= 0 || !can_see_obj(obj)) {
      keeper->act ("$n tells you 'I don't sell that -- try 'list''.",
        NULL, this, TO_VICT);
      reply = keeper;
      return;
    }

    if (gold < cost) {
      keeper->act ("$n tells you 'You can't afford to buy $p'.",
        obj, this, TO_VICT);
      reply = keeper;
      return;
    }

    if (obj->level > level) {
      keeper->act ("$n tells you 'You can't use $p yet'.", obj, this, TO_VICT);
      reply = keeper;
      return;
    }

    if (carry_number + obj->get_obj_number() > can_carry_n()) {
      send_to_char ("You can't carry that many items.\r\n");
      return;
    }

    if (carry_weight + obj->get_obj_weight() > can_carry_w()) {
      send_to_char ("You can't carry that much weight.\r\n");
      return;
    }

    act ("$n buys $p.", obj, NULL, TO_ROOM);
    act ("You buy $p.", obj, NULL, TO_CHAR);
    gold -= cost;
    keeper->gold += cost;

    if (IS_SET (obj->extra_flags, ITEM_INVENTORY))
      obj = obj->pIndexData->create_object(obj->level);
    else
      obj->obj_from_char ();

    obj->obj_to_char (this);
    return;
  }
}

void Character::do_list (std::string argument)
{
  char buf[MAX_STRING_LENGTH];
  std::string buf1;

  if (IS_SET (in_room->room_flags, ROOM_PET_SHOP)) {
    Room *pRoomIndexNext;
    bool found;

    pRoomIndexNext = get_room_index (in_room->vnum + 1);
    if (pRoomIndexNext == NULL) {
      bug_printf ("Do_list: bad pet shop at vnum %d.", in_room->vnum);
      send_to_char ("You can't do that here.\r\n");
      return;
    }

    found = false;
    CharIter pet;
    for (pet = pRoomIndexNext->people.begin(); pet != pRoomIndexNext->people.end(); pet++) {
      if (IS_SET ((*pet)->actflags, ACT_PET)) {
        if (!found) {
          found = true;
          buf1.append("Pets for sale:\r\n");
        }
        snprintf (buf, sizeof buf, "[%2d] %8d - %s\r\n",
          (*pet)->level, 10 * (*pet)->level * (*pet)->level, (*pet)->short_descr.c_str());
        buf1.append(buf);
      }
    }
    if (!found)
      send_to_char ("Sorry, we're out of pets right now.\r\n");

    send_to_char (buf1);
    return;
  } else {
    std::string arg;
    Character *keeper;
    Object *obj;
    int cost = 0;
    bool found;

    one_argument (argument, arg);

    if ((keeper = find_keeper (this)) == NULL)
      return;

    found = false;
    ObjIter o;
    for (o = keeper->carrying.begin(); o != keeper->carrying.end(); o++) {
      obj = *o;
      if (obj->wear_loc == WEAR_NONE && can_see_obj(obj)
        && (cost = get_cost (keeper, obj, true)) > 0
        && (arg.empty() || is_name (arg, obj->name))) {
        if (!found) {
          found = true;
          buf1.append("[Lv Price] Item\r\n");
        }

        snprintf (buf, sizeof buf, "[%2d %5d] %s.\r\n",
          obj->level, cost, capitalize (obj->short_descr).c_str());
        buf1.append(buf);
      }
    }

    if (!found) {
      if (arg.empty())
        send_to_char ("You can't buy anything here.\r\n");
      else
        send_to_char ("You can't buy that here.\r\n");
      return;
    }

    send_to_char (buf1);
    return;
  }
}

void Character::do_sell (std::string argument)
{
  std::string arg;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Sell what?\r\n");
    return;
  }

  Character *keeper;
  if ((keeper = find_keeper (this)) == NULL)
    return;

  Object *obj;
  if ((obj = get_obj_carry (arg)) == NULL) {
    keeper->act ("$n tells you 'You don't have that item'.",
      NULL, this, TO_VICT);
    reply = keeper;
    return;
  }

  if (!can_drop_obj (obj)) {
    send_to_char ("You can't let go of it.\r\n");
    return;
  }

  int cost;
  if ((cost = get_cost (keeper, obj, false)) <= 0) {
    keeper->act ("$n looks uninterested in $p.", obj, this, TO_VICT);
    return;
  }

  char buf[MAX_STRING_LENGTH];
  act ("$n sells $p.", obj, NULL, TO_ROOM);
  snprintf (buf, sizeof buf, "You sell $p for %d gold piece%s.",
    cost, cost == 1 ? "" : "s");
  act (buf, obj, NULL, TO_CHAR);
  gold += cost;
  keeper->gold -= cost;
  if (keeper->gold < 0)
    keeper->gold = 0;

  if (obj->item_type == ITEM_TRASH) {
    obj->extract_obj ();
  } else {
    obj->obj_from_char ();
    obj->obj_to_char (keeper);
  }

  return;
}

void Character::do_value (std::string argument)
{
  std::string arg;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Value what?\r\n");
    return;
  }

  Character *keeper;
  if ((keeper = find_keeper (this)) == NULL)
    return;

  Object *obj;
  if ((obj = get_obj_carry (arg)) == NULL) {
    keeper->act ("$n tells you 'You don't have that item'.",
      NULL, this, TO_VICT);
    reply = keeper;
    return;
  }

  if (!can_drop_obj (obj)) {
    send_to_char ("You can't let go of it.\r\n");
    return;
  }

  int cost;
  if ((cost = get_cost (keeper, obj, false)) <= 0) {
    keeper->act ("$n looks uninterested in $p.", obj, this, TO_VICT);
    return;
  }

  char buf[MAX_STRING_LENGTH];
  snprintf (buf, sizeof buf, "$n tells you 'I'll give you %d gold coins for $p'.", cost);
  keeper->act (buf, obj, this, TO_VICT);
  reply = keeper;

  return;
}

void Character::do_wizhelp (std::string argument)
{
  char buf[MAX_STRING_LENGTH];
  std::string buf1;

  int col = 0;
  for (int cmd = 0; cmd_table[cmd].name[0] != '\0'; cmd++) {
    if (cmd_table[cmd].level >= LEVEL_HERO
      && cmd_table[cmd].level <= get_trust ()) {
      snprintf (buf, sizeof buf, "%-12s", cmd_table[cmd].name);
      buf1.append(buf);
      if (++col % 6 == 0)
        buf1.append("\r\n");
    }
  }

  if (col % 6 != 0)
    buf1.append("\r\n");
  send_to_char (buf1);
  return;
}

void Character::do_bamfin (std::string argument)
{
  if (!is_npc ()) {
    smash_tilde (argument);
    pcdata->bamfin = argument;
    send_to_char ("Ok.\r\n");
  }
  return;
}

void Character::do_bamfout (std::string argument)
{
  if (!is_npc ()) {
    smash_tilde (argument);
    pcdata->bamfout = argument;
    send_to_char ("Ok.\r\n");
  }
  return;
}

void Character::do_deny (std::string argument)
{
  std::string arg;

  one_argument (argument, arg);
  if (arg.empty()) {
    send_to_char ("Deny whom?\r\n");
    return;
  }

  Character *victim;
  if ((victim = get_char_world (arg)) == NULL) {
    send_to_char ("They aren't here.\r\n");
    return;
  }

  if (victim->is_npc ()) {
    send_to_char ("Not on NPC's.\r\n");
    return;
  }

  if (victim->get_trust () >= get_trust ()) {
    send_to_char ("You failed.\r\n");
    return;
  }

  SET_BIT (victim->actflags, PLR_DENY);
  victim->send_to_char ("You are denied access!\r\n");
  send_to_char ("OK.\r\n");
  victim->do_quit ("");

  return;
}

void Character::do_disconnect (std::string argument)
{
  // :WARNING: There is a bug in this routine!  The mud will crash if you
  // disconnect the descriptor that immediately follows yours in
  // descriptor_list.  close_socket() invalidates the iterator in
  // 'process input' in game_loop.
  // FIXED by adding deepdenext iterator

  std::string arg;

  one_argument (argument, arg);
  if (arg.empty()) {
    send_to_char ("Disconnect whom?\r\n");
    return;
  }

  Character *victim;
  if ((victim = get_char_world (arg)) == NULL) {
    send_to_char ("They aren't here.\r\n");
    return;
  }

  if (victim->desc == NULL) {
    act ("$N doesn't have a descriptor.", NULL, victim, TO_CHAR);
    return;
  }

  DescIter d = std::find(descriptor_list.begin(),descriptor_list.end(),victim->desc);
  if (d != descriptor_list.end()) {
    (*d)->close_socket();
    send_to_char ("Ok.\r\n");
    return;
  }

  bug_printf ("Do_disconnect: desc not found.");
  send_to_char ("Descriptor not found!\r\n");
  return;
}

void Character::do_pardon (std::string argument)
{
  std::string arg1, arg2;

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

  if (arg1.empty() || arg2.empty()) {
    send_to_char ("Syntax: pardon <character> <killer|thief>.\r\n");
    return;
  }

  Character *victim;
  if ((victim = get_char_world (arg1)) == NULL) {
    send_to_char ("They aren't here.\r\n");
    return;
  }

  if (victim->is_npc ()) {
    send_to_char ("Not on NPC's.\r\n");
    return;
  }

  if (!str_cmp (arg2, "killer")) {
    if (IS_SET (victim->actflags, PLR_KILLER)) {
      REMOVE_BIT (victim->actflags, PLR_KILLER);
      send_to_char ("Killer flag removed.\r\n");
      victim->send_to_char ("You are no longer a KILLER.\r\n");
    }
    return;
  }

  if (!str_cmp (arg2, "thief")) {
    if (IS_SET (victim->actflags, PLR_THIEF)) {
      REMOVE_BIT (victim->actflags, PLR_THIEF);
      send_to_char ("Thief flag removed.\r\n");
      victim->send_to_char ("You are no longer a THIEF.\r\n");
    }
    return;
  }

  send_to_char ("Syntax: pardon <character> <killer|thief>.\r\n");
  return;
}

void Character::do_echo (std::string argument)
{
  if (argument.empty()) {
    send_to_char ("Echo what?\r\n");
    return;
  }

  for (DescIter d = descriptor_list.begin();
    d != descriptor_list.end(); d++) {
    if ((*d)->connected == CON_PLAYING) {
      (*d)->character->send_to_char (argument + "\r\n");
    }
  }

  return;
}

void Character::do_recho (std::string argument)
{
  if (argument.empty()) {
    send_to_char ("Recho what?\r\n");
    return;
  }

  for (DescIter d = descriptor_list.begin();
    d != descriptor_list.end(); d++) {
    if ((*d)->connected == CON_PLAYING && (*d)->character->in_room == in_room) {
      (*d)->character->send_to_char (argument + "\r\n");
    }
  }

  return;
}

void Character::do_transfer (std::string argument)
{
  std::string arg1, arg2;

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

  if (arg1.empty()) {
    send_to_char ("Transfer whom (and where)?\r\n");
    return;
  }

  if (!str_cmp (arg1, "all")) {
    for (DescIter d = descriptor_list.begin();
      d != descriptor_list.end(); d++) {
      if ((*d)->connected == CON_PLAYING
        && (*d)->character != this
        && (*d)->character->in_room != NULL && can_see((*d)->character)) {
        char buf[MAX_STRING_LENGTH];
        snprintf (buf, sizeof buf, "%s %s", (*d)->character->name.c_str(), arg2.c_str());
        do_transfer (buf);
      }
    }
    return;
  }

  /*
   * Thanks to Grodyn for the optional location parameter.
   */
  Room *location;
  if (arg2.empty()) {
    location = in_room;
  } else {
    if ((location = find_location (this, arg2)) == NULL) {
      send_to_char ("No such location.\r\n");
      return;
    }

    if (location->is_private()) {
      send_to_char ("That room is private right now.\r\n");
      return;
    }
  }

  Character *victim;
  if ((victim = get_char_world (arg1)) == NULL) {
    send_to_char ("They aren't here.\r\n");
    return;
  }

  if (victim->in_room == NULL) {
    send_to_char ("They are in limbo.\r\n");
    return;
  }

  if (victim->fighting != NULL)
    victim->stop_fighting(true);
  victim->act ("$n disappears in a mushroom cloud.", NULL, NULL, TO_ROOM);
  victim->char_from_room();
  victim->char_to_room(location);
  victim->act ("$n arrives from a puff of smoke.", NULL, NULL, TO_ROOM);
  if (this != victim)
    act ("$n has transferred you.", NULL, victim, TO_VICT);
  victim->do_look ("auto");
  send_to_char ("Ok.\r\n");
}

void Character::do_at (std::string argument)
{
  std::string arg;

  argument = one_argument (argument, arg);

  if (arg.empty() || argument.empty()) {
    send_to_char ("At where what?\r\n");
    return;
  }

  Room *location;
  if ((location = find_location (this, arg)) == NULL) {
    send_to_char ("No such location.\r\n");
    return;
  }

  if (location->is_private()) {
    send_to_char ("That room is private right now.\r\n");
    return;
  }

  Room* original = in_room;
  char_from_room();
  char_to_room(location);
  interpret (argument);

  /*
   * See if 'this' still exists before continuing!
   * Handles 'at XXXX quit' case.
   */
  CharIter c;
  for (c = char_list.begin(); c != char_list.end(); c++) {
    if (*c == this) {
      char_from_room();
      char_to_room(original);
      break;
    }
  }

  return;
}

void Character::do_goto (std::string argument)
{
  std::string arg;

  one_argument (argument, arg);
  if (arg.empty()) {
    send_to_char ("Goto where?\r\n");
    return;
  }

  Room *location;
  if ((location = find_location (this, arg)) == NULL) {
    send_to_char ("No such location.\r\n");
    return;
  }

  if (location->is_private()) {
    send_to_char ("That room is private right now.\r\n");
    return;
  }

  if (fighting != NULL)
    stop_fighting (true);
  act ("$n $T.", NULL,
    (pcdata != NULL && !pcdata->bamfout.empty())
    ? pcdata->bamfout.c_str() : "leaves in a swirling mist", TO_ROOM);

  char_from_room();
  char_to_room(location);

  act ("$n $T.", NULL,
    (pcdata != NULL && !pcdata->bamfin.empty())
    ? pcdata->bamfin.c_str() : "appears in a swirling mist", TO_ROOM);

  do_look ("auto");
  return;
}

void Character::do_rstat (std::string argument)
{
  std::string arg, buf1;

  one_argument (argument, arg);

  Room* location = arg.empty() ? in_room : find_location (this, arg);
  if (location == NULL) {
    send_to_char ("No such location.\r\n");
    return;
  }

  if (in_room != location && location->is_private()) {
    send_to_char ("That room is private right now.\r\n");
    return;
  }

  char buf[MAX_STRING_LENGTH];
  snprintf (buf, sizeof buf, "Name: '%s.'\r\nArea: '%s'.\r\n",
    location->name.c_str(), location->area->name.c_str());
  buf1.append(buf);

  snprintf (buf, sizeof buf,
    "Vnum: %d.  Sector: %d.  Light: %d.\r\n",
    location->vnum, location->sector_type, location->light);
  buf1.append(buf);

  snprintf (buf, sizeof buf,
    "Room flags: %d.\r\nDescription:\r\n%s",
    location->room_flags, location->description.c_str());
  buf1.append(buf);

  if (!location->extra_descr.empty()) {
    buf1.append("Extra description keywords: '");
    std::list<ExtraDescription *>::iterator ed;
    for (ed = location->extra_descr.begin(); ed != location->extra_descr.end(); ed++) {
      buf1.append((*ed)->keyword);
      buf1.append(" ");
    }
    if (buf1[buf1.size() - 1] == ' ')
      buf1.erase(buf1.size() - 1);
    buf1.append("'.\r\n");
  }

  buf1.append("Characters:");
  std::string tmp;
  CharIter rch;
  for (rch = location->people.begin(); rch != location->people.end(); rch++) {
    buf1.append(" ");
    one_argument ((*rch)->name, tmp);
    buf1.append(tmp);
  }

  buf1.append(".\r\nObjects:   ");
  ObjIter o;
  for (o = location->contents.begin(); o != location->contents.end(); o++) {
    buf1.append(" ");
    one_argument ((*o)->name, tmp);
    buf1.append(tmp);
  }
  buf1.append(".\r\n");

  for (int door = 0; door <= 5; door++) {
    Exit *pexit;

    if ((pexit = location->exit[door]) != NULL) {
      snprintf (buf, sizeof buf,
        "Door: %d.  To: %d.  Key: %d.  Exit flags: %d.\r\nKeyword: '%s'.  Description: %s",
        door,
        pexit->to_room != NULL ? pexit->to_room->vnum : 0,
        pexit->key,
        pexit->exit_info,
        pexit->name.c_str(),
        !pexit->description.empty() ? pexit->description.c_str() : "(none).\r\n");
      buf1.append(buf);
    }
  }

  send_to_char (buf1);
  return;
}

void Character::do_ostat (std::string argument)
{
  std::string arg, buf1;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Ostat what?\r\n");
    return;
  }

  Object *obj;
  if ((obj = get_obj_world (arg)) == NULL) {
    send_to_char ("Nothing like that in hell, earth, or heaven.\r\n");
    return;
  }

  char buf[MAX_STRING_LENGTH];
  snprintf (buf, sizeof buf, "Name: %s.\r\n", obj->name.c_str());
  buf1.append(buf);

  snprintf (buf, sizeof buf, "Vnum: %d.  Type: %s.\r\n",
    obj->pIndexData->vnum, obj->item_type_name().c_str());
  buf1.append(buf);

  snprintf (buf, sizeof buf, "Short description: %s.\r\nLong description: %s\r\n",
    obj->short_descr.c_str(), obj->description.c_str());
  buf1.append(buf);

  snprintf (buf, sizeof buf, "Wear bits: %d.  Extra bits: %s.\r\n",
    obj->wear_flags, extra_bit_name (obj->extra_flags).c_str());
  buf1.append(buf);

  snprintf (buf, sizeof buf, "Number: %d/%d.  Weight: %d/%d.\r\n",
    1, obj->get_obj_number(), obj->weight, obj->get_obj_weight());
  buf1.append(buf);

  snprintf (buf, sizeof buf, "Cost: %d.  Timer: %d.  Level: %d.\r\n",
    obj->cost, obj->timer, obj->level);
  buf1.append(buf);

  snprintf (buf, sizeof buf,
    "In room: %d.  In object: %s.  Carried by: %s.  Wear_loc: %d.\r\n",
    obj->in_room == NULL ? 0 : obj->in_room->vnum,
    obj->in_obj == NULL ? "(none)" : obj->in_obj->short_descr.c_str(),
    obj->carried_by == NULL ? "(none)" : obj->carried_by->name.c_str(),
    obj->wear_loc);
  buf1.append(buf);

  snprintf (buf, sizeof buf, "Values: %d %d %d %d.\r\n",
    obj->value[0], obj->value[1], obj->value[2], obj->value[3]);
  buf1.append(buf);

  if (!obj->extra_descr.empty() || !obj->pIndexData->extra_descr.empty()) {
    buf1.append("Extra description keywords: '");
    std::list<ExtraDescription *>::iterator ed;
    for (ed = obj->extra_descr.begin(); ed != obj->extra_descr.end(); ed++) {
      buf1.append((*ed)->keyword);
      buf1.append(" ");
    }
    for (ed = obj->pIndexData->extra_descr.begin(); ed != obj->pIndexData->extra_descr.end(); ed++) {
      buf1.append((*ed)->keyword);
      buf1.append(" ");
    }
    if (buf1[buf1.size() - 1] == ' ')
      buf1.erase(buf1.size() - 1);

    buf1.append("'.\r\n");
  }

  AffIter af;
  for (af = obj->affected.begin(); af != obj->affected.end(); af++) {
    snprintf (buf, sizeof buf, "Affects %s by %d.\r\n",
      affect_loc_name ((*af)->location).c_str(), (*af)->modifier);
    buf1.append(buf);
  }

  for (af = obj->pIndexData->affected.begin(); af != obj->pIndexData->affected.end(); af++) {
    snprintf (buf, sizeof buf, "Affects %s by %d.\r\n",
      affect_loc_name ((*af)->location).c_str(), (*af)->modifier);
    buf1.append(buf);
  }

  send_to_char (buf1);
  return;
}

void Character::do_mstat (std::string argument)
{
  std::string arg, buf1;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Mstat whom?\r\n");
    return;
  }

  Character *victim;
  if ((victim = get_char_world (arg)) == NULL) {
    send_to_char ("They aren't here.\r\n");
    return;
  }

  char buf[MAX_STRING_LENGTH];
  snprintf (buf, sizeof buf, "Name: %s.\r\n", victim->name.c_str());
  buf1.append(buf);

  snprintf (buf, sizeof buf, "Vnum: %d.  Sex: %s.  Room: %d.\r\n",
    victim->is_npc () ? victim->pIndexData->vnum : 0,
    victim->sex == SEX_MALE ? "male" :
    victim->sex == SEX_FEMALE ? "female" : "neutral",
    victim->in_room == NULL ? 0 : victim->in_room->vnum);
  buf1.append(buf);

  snprintf (buf, sizeof buf, "Str: %d.  Int: %d.  Wis: %d.  Dex: %d.  Con: %d.\r\n",
    victim->get_curr_str(), victim->get_curr_int(),
    victim->get_curr_wis(), victim->get_curr_dex(),
    victim->get_curr_con());
  buf1.append(buf);

  snprintf (buf, sizeof buf, "Hp: %d/%d.  Mana: %d/%d.  Move: %d/%d.  Practices: %d.\r\n",
    victim->hit, victim->max_hit,
    victim->mana, victim->max_mana,
    victim->move, victim->max_move, victim->practice);
  buf1.append(buf);

  snprintf (buf, sizeof buf,
    "Lv: %d.  Class: %d.  Align: %d.  AC: %d.  Gold: %d.  Exp: %d.\r\n",
    victim->level, victim->klass, victim->alignment,
    victim->get_ac(), victim->gold, victim->exp);
  buf1.append(buf);

  snprintf (buf, sizeof buf, "Hitroll: %d.  Damroll: %d.  Position: %d.  Wimpy: %d.\r\n",
    victim->get_hitroll(), victim->get_damroll(),
    victim->position, victim->wimpy);
  buf1.append(buf);

  if (!victim->is_npc ()) {
    snprintf (buf, sizeof buf, "Page Lines: %d.\r\n", victim->pcdata->pagelen);
    buf1.append(buf);
  }

  snprintf (buf, sizeof buf, "Fighting: %s.\r\n",
    victim->fighting ? victim->fighting->name.c_str() : "(none)");
  buf1.append(buf);

  if (!victim->is_npc ()) {
    snprintf (buf, sizeof buf,
      "Thirst: %d.  Full: %d.  Drunk: %d.  Saving throw: %d.\r\n",
      victim->pcdata->condition[COND_THIRST],
      victim->pcdata->condition[COND_FULL],
      victim->pcdata->condition[COND_DRUNK], victim->saving_throw);
    buf1.append(buf);
  }

  snprintf (buf, sizeof buf, "Carry number: %d.  Carry weight: %d.\r\n",
    victim->carry_number, victim->carry_weight);
  buf1.append(buf);

  snprintf (buf, sizeof buf, "Age: %d.  Played: %d.  Timer: %d.  Act: %d.\r\n",
    victim->get_age(), (int) victim->played, victim->timer, victim->actflags);
  buf1.append(buf);

  snprintf (buf, sizeof buf, "Master: %s.  Leader: %s.  Affected by: %s.\r\n",
    victim->master ? victim->master->name.c_str() : "(none)",
    victim->leader ? victim->leader->name.c_str() : "(none)",
    affect_bit_name (victim->affected_by).c_str());
  buf1.append(buf);

  snprintf (buf, sizeof buf, "Short description: %s.\r\nLong  description: %s",
    victim->short_descr.c_str(),
    !victim->long_descr.empty() ? victim->long_descr.c_str() : "(none).\r\n");
  buf1.append(buf);

  if (victim->is_npc () && victim->spec_fun != 0)
    buf1.append("Mobile has spec fun.\r\n");

  AffIter af;
  for (af = victim->affected.begin(); af != victim->affected.end(); af++) {
    snprintf (buf, sizeof buf,
      "Spell: '%s' modifies %s by %d for %d hours with bits %s.\r\n",
      skill_table[(int) (*af)->type].name,
      affect_loc_name ((*af)->location).c_str(),
      (*af)->modifier, (*af)->duration, affect_bit_name ((*af)->bitvector).c_str()
      );
    buf1.append(buf);
  }

  send_to_char (buf1);
  return;
}

void Character::do_mfind (std::string argument)
{
  std::string arg, buf1;

  one_argument (argument, arg);
  if (arg.empty()) {
    send_to_char ("Mfind whom?\r\n");
    return;
  }

  bool fAll = !str_cmp (arg, "all");
  bool found = false;
  int nMatch = 0;
  MobPrototype *pMobIndex;
  char buf[MAX_STRING_LENGTH];

  /*
   * Yeah, so iterating over all vnum's takes 10,000 loops.
   * Get_mob_index is fast, and I don't feel like threading another link.
   * Do you?
   * -- Furey
   */
  for (int vn = 0; nMatch < MobPrototype::top_mob; vn++) {
    if ((pMobIndex = get_mob_index (vn)) != NULL) {
      nMatch++;
      if (fAll || is_name (arg, pMobIndex->name)) {
        found = true;
        snprintf (buf, sizeof buf, "[%5d] %s\r\n",
          pMobIndex->vnum, capitalize (pMobIndex->short_descr).c_str());
        buf1.append(buf);
      }
    }
  }

  if (!found) {
    send_to_char ("Nothing like that in hell, earth, or heaven.\r\n");
    return;
  }

  send_to_char (buf1);
  return;
}

void Character::do_ofind (std::string argument)
{
  std::string arg, buf1;

  one_argument (argument, arg);
  if (arg.empty()) {
    send_to_char ("Ofind what?\r\n");
    return;
  }

  bool fAll = !str_cmp (arg, "all");
  bool found = false;
  int nMatch = 0;
  char buf[MAX_STRING_LENGTH];
  ObjectPrototype *pObjIndex;

  /*
   * Yeah, so iterating over all vnum's takes 10,000 loops.
   * Get_obj_index is fast, and I don't feel like threading another link.
   * Do you?
   * -- Furey
   */
  for (int vn = 0; nMatch < ObjectPrototype::top_obj; vn++) {
    if ((pObjIndex = get_obj_index (vn)) != NULL) {
      nMatch++;
      if (fAll || is_name (arg, pObjIndex->name)) {
        found = true;
        snprintf (buf, sizeof buf, "[%5d] %s\r\n",
          pObjIndex->vnum, capitalize (pObjIndex->short_descr).c_str());
        buf1.append(buf);
      }
    }
  }

  if (!found) {
    send_to_char ("Nothing like that in hell, earth, or heaven.\r\n");
    return;
  }

  send_to_char (buf1);
  return;
}

void Character::do_mwhere (std::string argument)
{
  std::string arg;

  one_argument (argument, arg);
  if (arg.empty()) {
    send_to_char ("Mwhere whom?\r\n");
    return;
  }

  char buf[MAX_STRING_LENGTH];
  bool found = false;
  for (CharIter c = char_list.begin(); c != char_list.end(); c++) {
    if ((*c)->is_npc ()
      && (*c)->in_room != NULL && is_name (arg, (*c)->name)) {
      found = true;
      snprintf (buf, sizeof buf, "[%5d] %-28s [%5d] %s\r\n",
        (*c)->pIndexData->vnum,
        (*c)->short_descr.c_str(), (*c)->in_room->vnum, (*c)->in_room->name.c_str());
      send_to_char (buf);
    }
  }

  if (!found) {
    act ("You didn't find any $T.", NULL, arg.c_str(), TO_CHAR);
    return;
  }

  return;
}

void Character::do_hotboo (std::string argument)
{
  send_to_char ("If you want to HOTBOOT, spell it out.\r\n");
  return;
}

void Character::do_hotboot (std::string argument)
{
#if !defined(WIN32) || defined(__DMC__)
  send_to_char ("Hotboot not supported.\r\n");
#else
  extern bool write_to_descriptor (SOCKET desc, const char *txt, int length);

  std::string usr_msg("Hotboot by ");
  usr_msg.append(name);
  usr_msg.append(". Stand by...\r\n");

  int count_users = 0;
  for (DescIter d = descriptor_list.begin(); d != descriptor_list.end(); d++) {
    if (!(*d)->character || (*d)->connected > CON_PLAYING) {
      write_to_descriptor ((*d)->descriptor, "The server is hotbooting.  Please recreate in a few minutes.\r\n", 0);
      (*d)->close_socket();
    } else {
      count_users++;
    }
  }

  char cmd[MAX_INPUT_LENGTH];
  snprintf(cmd, sizeof cmd, "murk %d hotboot", g_port);

  STARTUPINFO start_info;
  PROCESS_INFORMATION proc_info;
  WSAPROTOCOL_INFO proto_info;

  // Get current console parameters
  GetStartupInfo(&start_info);

  // This event will be set when we are ready for copyover
  void * file_event = CreateEvent(NULL, TRUE, FALSE, "file_created");
  if (file_event == NULL) {
    win_errprint("Error creating event, file_created");
    return;
  }

  // We will wait on this event before shutting down
  void * shutdown_event = CreateEvent(NULL, TRUE, FALSE, "ok_to_shutdown");
  if (shutdown_event == NULL) {
    win_errprint("Error creating event, ok_to_shutdown");
    CloseHandle(file_event);
    return;
  }

  // Start running a new server
  if (CreateProcess(NULL, cmd, NULL, NULL, TRUE, NULL, NULL, NULL,
      &start_info, &proc_info) == NULL) {
    win_errprint("Error creating new process");
    CloseHandle(file_event);
    CloseHandle(shutdown_event);
    return;
  }

  std::FILE* fp;
  if ((fp = std::fopen ("hotboot.$$$", "w+b")) == NULL) {
    send_to_char ("Hotboot aborted.\r\n");
    win_errprint("Error creating hotboot file");
    return;
  }

  fwrite(&count_users, sizeof(int),1,fp);  // write number of users
  // duplicate and save the listening sockets info
  if (WSADuplicateSocket(g_listen, proc_info.dwProcessId, &proto_info) == SOCKET_ERROR) {
    bug_printf ("Error duplicating listening socket : %d.", WSAGetLastError());
  }
  fwrite(&proto_info,sizeof(WSAPROTOCOL_INFO),1,fp);
  closesocket(g_listen);

  for (DescIter d = descriptor_list.begin(); d != descriptor_list.end(); d++) {
    if (WSADuplicateSocket((*d)->descriptor, proc_info.dwProcessId, &proto_info) == SOCKET_ERROR) {
      bug_printf ("Error duplicating user socket : %d.", WSAGetLastError());
    }
    Character * ch = (*d)->original ? (*d)->original : (*d)->character;
    if (ch->level == 1) {
      ch->level += 1;
      ch->advance_level();
    }
    ch->save_char_obj();
    write_to_descriptor ((*d)->descriptor, usr_msg.c_str(), 0);
    char chname[25];
    strcpy(chname,ch->name.c_str());
    fwrite(&proto_info,sizeof(WSAPROTOCOL_INFO),1,fp);
    fwrite(chname,sizeof(chname),1,fp);
    closesocket((*d)->descriptor);
  }

  fclose (fp);

  // Indicate we are done with the copyover file
  if (SetEvent(file_event) == NULL) {
    win_errprint("Error setting file_event");
  }

  // Wait for child server to tell us its ok to shutdown
  if (WaitForSingleObject(shutdown_event, INFINITE) == WAIT_FAILED) {
    win_errprint("Error waiting on shutdown_event");
  }

  // cleanup event handles
  CloseHandle(file_event);
  CloseHandle(shutdown_event);
  WIN32CLEANUP
  g_db->shutdown();
  std::exit(0);
#endif
  return;
}

void Character::do_shutdow (std::string argument)
{
  send_to_char ("If you want to SHUTDOWN, spell it out.\r\n");
  return;
}

void Character::do_shutdown (std::string argument)
{
  std::string buf("Shutdown by ");

  buf.append(name);
  buf.append(".");
  append_file (SHUTDOWN_FILE, buf);
  buf.append("\r\n");
  do_echo (buf);
  merc_down = true;
  return;
}

void Character::do_switch (std::string argument)
{
  std::string arg;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Switch into whom?\r\n");
    return;
  }

  if (desc == NULL)
    return;

  if (desc->original != NULL) {
    send_to_char ("You are already switched.\r\n");
    return;
  }

  Character *victim;
  if ((victim = get_char_world (arg)) == NULL) {
    send_to_char ("They aren't here.\r\n");
    return;
  }

  if (victim == this) {
    send_to_char ("Ok.\r\n");
    return;
  }

  /*
   * Pointed out by Da Pub (What Mud)
   */
  if (!victim->is_npc ()) {
    send_to_char ("You cannot switch into a player!\r\n");
    return;
  }

  if (victim->desc != NULL) {
    send_to_char ("Character in use.\r\n");
    return;
  }

  desc->character = victim;
  desc->original = this;
  victim->desc = desc;
  desc = NULL;
  victim->send_to_char ("Ok.\r\n");
  return;
}

void Character::do_return (std::string argument)
{
  if (desc == NULL)
    return;

  if (desc->original == NULL) {
    send_to_char ("You aren't switched.\r\n");
    return;
  }

  send_to_char ("You return to your original body.\r\n");
  desc->character = desc->original;
  desc->original = NULL;
  desc->character->desc = desc;
  desc = NULL;
  return;
}

void Character::do_mload (std::string argument)
{
  std::string arg;
  MobPrototype *pMobIndex;
  Character *victim;

  one_argument (argument, arg);

  if (arg.empty() || !is_number (arg)) {
    send_to_char ("Syntax: mload <vnum>.\r\n");
    return;
  }

  if ((pMobIndex = get_mob_index (std::atoi (arg.c_str()))) == NULL) {
    send_to_char ("No mob has that vnum.\r\n");
    return;
  }

  victim = pMobIndex->create_mobile ();
  victim->char_to_room(in_room);
  act ("$n has created $N!", NULL, victim, TO_ROOM);
  send_to_char ("Ok.\r\n");
  return;
}

void Character::do_oload (std::string argument)
{
  std::string arg1, arg2;

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

  if (arg1.empty() || !is_number (arg1)) {
    send_to_char ("Syntax: oload <vnum> <level>.\r\n");
    return;
  }

  int lvl;
  if (arg2.empty()) {
    lvl = get_trust ();
  } else {
    /*
     * New feature from Alander.
     */
    if (!is_number (arg2)) {
      send_to_char ("Syntax: oload <vnum> <level>.\r\n");
      return;
    }
    lvl = std::atoi (arg2.c_str());
    if (lvl < 0 || lvl > get_trust ()) {
      send_to_char ("Limited to your trust level.\r\n");
      return;
    }
  }

  ObjectPrototype *pObjIndex;
  if ((pObjIndex = get_obj_index (std::atoi (arg1.c_str()))) == NULL) {
    send_to_char ("No object has that vnum.\r\n");
    return;
  }

  Object *obj = pObjIndex->create_object(lvl);
  if (obj->can_wear(ITEM_TAKE)) {
    obj->obj_to_char (this);
  } else {
    obj->obj_to_room (in_room);
    act ("$n has created $p!", obj, NULL, TO_ROOM);
  }
  send_to_char ("Ok.\r\n");
  return;
}

void Character::do_purge (std::string argument)
{
  std::string arg;
  Character *victim;
  Object *obj;

  one_argument (argument, arg);

  if (arg.empty()) {
    /* 'purge' */

    CharIter rch, rnext;
    for (rch = in_room->people.begin(); rch != in_room->people.end(); rch = rnext) {
      victim = *rch;
      rnext = ++rch;
      if (victim->is_npc () && victim != this)
        victim->extract_char (true);
    }

    ObjIter o, onext;
    for (o = in_room->contents.begin(); o != in_room->contents.end(); o = onext) {
      obj = *o;
      onext = ++o;
      obj->extract_obj ();
    }

    act ("$n purges the room!", NULL, NULL, TO_ROOM);
    send_to_char ("Ok.\r\n");
    return;
  }

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

  if (!victim->is_npc ()) {
    send_to_char ("Not on PC's.\r\n");
    return;
  }

  act ("$n purges $N.", NULL, victim, TO_NOTVICT);
  victim->extract_char (true);
  return;
}

void Character::do_advance (std::string argument)
{
  std::string arg1, arg2;
  Character *victim;
  int lvl;

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

  if (arg1.empty() || arg2.empty() || !is_number (arg2)) {
    send_to_char ("Syntax: advance <char> <level>.\r\n");
    return;
  }

  if ((victim = get_char_room (arg1)) == NULL) {
    send_to_char ("That player is not here.\r\n");
    return;
  }

  if (victim->is_npc ()) {
    send_to_char ("Not on NPC's.\r\n");
    return;
  }

  if ((lvl = std::atoi (arg2.c_str())) < 1 || lvl > 40) {
    send_to_char ("Level must be 1 to 40.\r\n");
    return;
  }

  if (lvl > get_trust ()) {
    send_to_char ("Limited to your trust level.\r\n");
    return;
  }

  /*
   * Lower level:
   *   Reset to level 1.
   *   Then raise again.
   *   Currently, an imp can lower another imp.
   *   -- Swiftest
   */
  if (lvl <= victim->level) {
    int sn;

    send_to_char ("Lowering a player's level!\r\n");
    victim->send_to_char ("**** OOOOHHHHHHHHHH  NNNNOOOO ****\r\n");
    victim->level = 1;
    victim->exp = 1000;
    victim->max_hit = 10;
    victim->max_mana = 100;
    victim->max_move = 100;
    for (sn = 0; sn < MAX_SKILL; sn++)
      victim->pcdata->learned[sn] = 0;
    victim->practice = 0;
    victim->hit = victim->max_hit;
    victim->mana = victim->max_mana;
    victim->move = victim->max_move;
    victim->advance_level();
  } else {
    send_to_char ("Raising a player's level!\r\n");
    victim->send_to_char ("**** OOOOHHHHHHHHHH  YYYYEEEESSS ****\r\n");
  }

  for (int iLevel = victim->level; iLevel < lvl; iLevel++) {
    victim->send_to_char ("You raise a level!!  ");
    victim->level += 1;
    victim->advance_level();
  }
  victim->exp = 1000 * std::max (1, victim->level);
  victim->trust = 0;
  return;
}

void Character::do_trust (std::string argument)
{
  std::string arg1, arg2;
  Character *victim;
  int lvl;

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

  if (arg1.empty() || arg2.empty() || !is_number (arg2)) {
    send_to_char ("Syntax: trust <char> <level>.\r\n");
    return;
  }

  if ((victim = get_char_room (arg1)) == NULL) {
    send_to_char ("That player is not here.\r\n");
    return;
  }

  if ((lvl = std::atoi (arg2.c_str())) < 0 || lvl > 40) {
    send_to_char ("Level must be 0 (reset) or 1 to 40.\r\n");
    return;
  }

  if (lvl > get_trust ()) {
    send_to_char ("Limited to your trust.\r\n");
    return;
  }

  victim->trust = lvl;
  return;
}

void Character::do_restore (std::string argument)
{
  std::string arg;
  Character *victim;

  one_argument (argument, arg);
  if (arg.empty()) {
    send_to_char ("Restore whom?\r\n");
    return;
  }

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

  victim->hit = victim->max_hit;
  victim->mana = victim->max_mana;
  victim->move = victim->max_move;
  victim->update_pos();
  act ("$n has restored you.", NULL, victim, TO_VICT);
  send_to_char ("Ok.\r\n");
  return;
}

void Character::do_freeze (std::string argument)
{
  std::string arg;
  Character *victim;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Freeze whom?\r\n");
    return;
  }

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

  if (victim->is_npc ()) {
    send_to_char ("Not on NPC's.\r\n");
    return;
  }

  if (victim->get_trust () >= get_trust ()) {
    send_to_char ("You failed.\r\n");
    return;
  }

  if (IS_SET (victim->actflags, PLR_FREEZE)) {
    REMOVE_BIT (victim->actflags, PLR_FREEZE);
    victim->send_to_char ("You can play again.\r\n");
    send_to_char ("FREEZE removed.\r\n");
  } else {
    SET_BIT (victim->actflags, PLR_FREEZE);
    victim->send_to_char ("You can't do ANYthing!\r\n");
    send_to_char ("FREEZE set.\r\n");
  }

  victim->save_char_obj();

  return;
}

void Character::do_noemote (std::string argument)
{
  std::string arg;
  Character *victim;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Noemote whom?\r\n");
    return;
  }

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

  if (victim->is_npc ()) {
    send_to_char ("Not on NPC's.\r\n");
    return;
  }

  if (victim->get_trust () >= get_trust ()) {
    send_to_char ("You failed.\r\n");
    return;
  }

  if (IS_SET (victim->actflags, PLR_NO_EMOTE)) {
    REMOVE_BIT (victim->actflags, PLR_NO_EMOTE);
    victim->send_to_char ("You can emote again.\r\n");
    send_to_char ("NO_EMOTE removed.\r\n");
  } else {
    SET_BIT (victim->actflags, PLR_NO_EMOTE);
    victim->send_to_char ("You can't emote!\r\n");
    send_to_char ("NO_EMOTE set.\r\n");
  }

  return;
}

void Character::do_notell (std::string argument)
{
  std::string arg;
  Character *victim;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Notell whom?");
    return;
  }

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

  if (victim->is_npc ()) {
    send_to_char ("Not on NPC's.\r\n");
    return;
  }

  if (victim->get_trust () >= get_trust ()) {
    send_to_char ("You failed.\r\n");
    return;
  }

  if (IS_SET (victim->actflags, PLR_NO_TELL)) {
    REMOVE_BIT (victim->actflags, PLR_NO_TELL);
    victim->send_to_char ("You can tell again.\r\n");
    send_to_char ("NO_TELL removed.\r\n");
  } else {
    SET_BIT (victim->actflags, PLR_NO_TELL);
    victim->send_to_char ("You can't tell!\r\n");
    send_to_char ("NO_TELL set.\r\n");
  }

  return;
}

void Character::do_silence (std::string argument)
{
  std::string arg;
  Character *victim;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Silence whom?");
    return;
  }

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

  if (victim->is_npc ()) {
    send_to_char ("Not on NPC's.\r\n");
    return;
  }

  if (victim->get_trust () >= get_trust ()) {
    send_to_char ("You failed.\r\n");
    return;
  }

  if (IS_SET (victim->actflags, PLR_SILENCE)) {
    REMOVE_BIT (victim->actflags, PLR_SILENCE);
    victim->send_to_char ("You can use channels again.\r\n");
    send_to_char ("SILENCE removed.\r\n");
  } else {
    SET_BIT (victim->actflags, PLR_SILENCE);
    victim->send_to_char ("You can't use channels!\r\n");
    send_to_char ("SILENCE set.\r\n");
  }

  return;
}

void Character::do_peace (std::string argument)
{
  CharIter rch;
  for (rch = in_room->people.begin(); rch != in_room->people.end(); rch++) {
    if ((*rch)->fighting != NULL)
      (*rch)->stop_fighting (true);
  }

  send_to_char ("Ok.\r\n");
  return;
}

void Character::do_ban (std::string argument)
{
  std::string buf;
  std::string arg;

  if (is_npc ())
    return;

  one_argument (argument, arg);

  if (arg.empty()) {
    buf = "Banned sites:\r\n";
    for (std::list<Ban*>::iterator p = ban_list.begin(); p != ban_list.end(); p++) {
      buf.append((*p)->name);
      buf.append("\r\n");
    }
    send_to_char (buf);
    return;
  }

  for (std::list<Ban*>::iterator p = ban_list.begin(); p != ban_list.end(); p++) {
    if (!str_cmp (arg, (*p)->name)) {
      send_to_char ("That site is already banned!\r\n");
      return;
    }
  }

  Ban *pban = new Ban(arg);
  ban_list.push_back(pban);
  send_to_char ("Ok.\r\n");
  return;
}

void Character::do_allow (std::string argument)
{
  std::string arg;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Remove which site from the ban list?\r\n");
    return;
  }

  std::list<Ban*>::iterator next, curr;
  for (std::list<Ban*>::iterator pban = ban_list.begin();
    pban != ban_list.end(); pban = next) {
    curr = pban;
    next = ++pban;
    if (!str_cmp (arg, (*curr)->name)) {
      delete *curr;
      ban_list.erase(curr);
      send_to_char ("Ok.\r\n");
      return;
    }
  }

  send_to_char ("Site is not banned.\r\n");
  return;
}

void Character::do_wizlock (std::string argument)
{
  wizlock = !wizlock;

  if (wizlock)
    send_to_char ("Game wizlocked.\r\n");
  else
    send_to_char ("Game un-wizlocked.\r\n");

  return;
}

void Character::do_slookup (std::string argument)
{
  char buf[MAX_STRING_LENGTH];
  std::string arg;
  int sn;

  one_argument (argument, arg);
  if (arg.empty()) {
    send_to_char ("Slookup what?\r\n");
    return;
  }

  if (!str_cmp (arg, "all")) {
    std::string buf1;
    for (sn = 0; sn < MAX_SKILL; sn++) {
      if (skill_table[sn].name == NULL)
        break;
      snprintf (buf, sizeof buf, "Sn: %4d Skill/spell: '%s'\r\n",
        sn, skill_table[sn].name);
      buf1.append(buf);
    }
    send_to_char (buf1);
  } else {
    if ((sn = skill_lookup (arg)) < 0) {
      send_to_char ("No such skill or spell.\r\n");
      return;
    }

    snprintf (buf, sizeof buf, "Sn: %4d Skill/spell: '%s'\r\n",
      sn, skill_table[sn].name);
    send_to_char (buf);
  }

  return;
}

void Character::do_sset (std::string argument)
{
  std::string arg1, arg2, arg3;
  Character *victim;
  int value;
  int sn;
  bool fAll;

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

  if (arg1.empty() || arg2.empty() || arg3.empty()) {
    send_to_char ("Syntax: sset <victim> <skill> <value>\r\n");
    send_to_char ("or:     sset <victim> all     <value>\r\n");
    send_to_char ("Skill being any skill or spell.\r\n");
    return;
  }

  if ((victim = get_char_world (arg1)) == NULL) {
    send_to_char ("They aren't here.\r\n");
    return;
  }

  if (victim->is_npc ()) {
    send_to_char ("Not on NPC's.\r\n");
    return;
  }

  fAll = !str_cmp (arg2, "all");
  sn = 0;
  if (!fAll && (sn = skill_lookup (arg2)) < 0) {
    send_to_char ("No such skill or spell.\r\n");
    return;
  }

  /*
   * Snarf the value.
   */
  if (!is_number (arg3)) {
    send_to_char ("Value must be numeric.\r\n");
    return;
  }

  value = std::atoi (arg3.c_str());
  if (value < 0 || value > 100) {
    send_to_char ("Value range is 0 to 100.\r\n");
    return;
  }

  if (fAll) {
    for (sn = 0; sn < MAX_SKILL; sn++) {
      if (skill_table[sn].name != NULL)
        victim->pcdata->learned[sn] = value;
    }
  } else {
    victim->pcdata->learned[sn] = value;
  }

  return;
}

void Character::do_mset (std::string argument)
{
  std::string arg1, arg2, arg3;
  char buf[MAX_STRING_LENGTH];
  Character *victim;
  int value, max;

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

  if (arg1.empty() || arg2.empty() || arg3.empty()) {
    send_to_char ("Syntax: mset <victim> <field>  <value>\r\n");
    send_to_char ("or:     mset <victim> <string> <value>\r\n");
    send_to_char ("\r\n");
    send_to_char ("Field being one of:\r\n");
    send_to_char ("  str int wis dex con sex class level\r\n");
    send_to_char ("  gold hp mana move practice align\r\n");
    send_to_char ("  thirst drunk full");
    send_to_char ("\r\n");
    send_to_char ("String being one of:\r\n");
    send_to_char ("  name short long description title spec\r\n");
    return;
  }

  if ((victim = get_char_world (arg1)) == NULL) {
    send_to_char ("They aren't here.\r\n");
    return;
  }

  /*
   * Snarf the value (which need not be numeric).
   */
  value = is_number (arg3) ? std::atoi (arg3.c_str()) : -1;

  /*
   * Set something.
   */
  if (!str_cmp (arg2, "str")) {
    if (victim->is_npc ()) {
      send_to_char ("Not on NPC's.\r\n");
      return;
    }

    if (class_table[klass].attr_prime == APPLY_STR)
      max = 25;
    else
      max = 18;

    if (value < 3 || value > max) {
      snprintf (buf, sizeof buf, "Strength range is 3 to %d.\r\n", max);
      send_to_char (buf);
      return;
    }

    victim->pcdata->set_perm("str", value);
    return;
  }

  if (!str_cmp (arg2, "int")) {
    if (victim->is_npc ()) {
      send_to_char ("Not on NPC's.\r\n");
      return;
    }

    if (class_table[klass].attr_prime == APPLY_INT)
      max = 25;
    else
      max = 18;

    if (value < 3 || value > max) {
      snprintf (buf, sizeof buf, "Intelligence range is 3 to %d.\r\n", max);
      send_to_char (buf);
      return;
    }

    victim->pcdata->set_perm("int", value);
    return;
  }

  if (!str_cmp (arg2, "wis")) {
    if (victim->is_npc ()) {
      send_to_char ("Not on NPC's.\r\n");
      return;
    }

    if (class_table[klass].attr_prime == APPLY_WIS)
      max = 25;
    else
      max = 18;

    if (value < 3 || value > max) {
      snprintf (buf, sizeof buf, "Wisdom range is 3 to %d.\r\n", max);
      send_to_char (buf);
      return;
    }

    victim->pcdata->set_perm("wis", value);
    return;
  }

  if (!str_cmp (arg2, "dex")) {
    if (victim->is_npc ()) {
      send_to_char ("Not on NPC's.\r\n");
      return;
    }

    if (class_table[klass].attr_prime == APPLY_DEX)
      max = 25;
    else
      max = 18;

    if (value < 3 || value > max) {
      snprintf (buf, sizeof buf, "Dexterity range is 3 to %d.\r\n", max);
      send_to_char (buf);
      return;
    }

    victim->pcdata->set_perm("dex", value);
    return;
  }

  if (!str_cmp (arg2, "con")) {
    if (victim->is_npc ()) {
      send_to_char ("Not on NPC's.\r\n");
      return;
    }

    if (class_table[klass].attr_prime == APPLY_CON)
      max = 25;
    else
      max = 18;

    if (value < 3 || value > max) {
      snprintf (buf, sizeof buf, "Constitution range is 3 to %d.\r\n", max);
      send_to_char (buf);
      return;
    }

    victim->pcdata->set_perm("con", value);
    return;
  }

  if (!str_cmp (arg2, "sex")) {
    if (value < 0 || value > 2) {
      send_to_char ("Sex range is 0 to 2.\r\n");
      return;
    }
    victim->sex = value;
    return;
  }

  if (!str_cmp (arg2, "class")) {
    if (value < 0 || value >= CLASS_MAX) {
      char buf[MAX_STRING_LENGTH];

      snprintf (buf, sizeof buf, "Class range is 0 to %d.\n", CLASS_MAX - 1);
      send_to_char (buf);
      return;
    }
    victim->klass = value;
    return;
  }

  if (!str_cmp (arg2, "level")) {
    if (!victim->is_npc ()) {
      send_to_char ("Not on PC's.\r\n");
      return;
    }

    if (value < 0 || value > 50) {
      send_to_char ("Level range is 0 to 50.\r\n");
      return;
    }
    victim->level = value;
    return;
  }

  if (!str_cmp (arg2, "gold")) {
    victim->gold = value;
    return;
  }

  if (!str_cmp (arg2, "hp")) {
    if (value < -10 || value > 30000) {
      send_to_char ("Hp range is -10 to 30,000 hit points.\r\n");
      return;
    }
    victim->max_hit = value;
    return;
  }

  if (!str_cmp (arg2, "mana")) {
    if (value < 0 || value > 30000) {
      send_to_char ("Mana range is 0 to 30,000 mana points.\r\n");
      return;
    }
    victim->max_mana = value;
    return;
  }

  if (!str_cmp (arg2, "move")) {
    if (value < 0 || value > 30000) {
      send_to_char ("Move range is 0 to 30,000 move points.\r\n");
      return;
    }
    victim->max_move = value;
    return;
  }

  if (!str_cmp (arg2, "practice")) {
    if (value < 0 || value > 100) {
      send_to_char ("Practice range is 0 to 100 sessions.\r\n");
      return;
    }
    victim->practice = value;
    return;
  }

  if (!str_cmp (arg2, "align")) {
    if (value < -1000 || value > 1000) {
      send_to_char ("Alignment range is -1000 to 1000.\r\n");
      return;
    }
    victim->alignment = value;
    return;
  }

  if (!str_cmp (arg2, "thirst")) {
    if (victim->is_npc ()) {
      send_to_char ("Not on NPC's.\r\n");
      return;
    }

    if (value < 0 || value > 100) {
      send_to_char ("Thirst range is 0 to 100.\r\n");
      return;
    }

    victim->pcdata->condition[COND_THIRST] = value;
    return;
  }

  if (!str_cmp (arg2, "drunk")) {
    if (victim->is_npc ()) {
      send_to_char ("Not on NPC's.\r\n");
      return;
    }

    if (value < 0 || value > 100) {
      send_to_char ("Drunk range is 0 to 100.\r\n");
      return;
    }

    victim->pcdata->condition[COND_DRUNK] = value;
    return;
  }

  if (!str_cmp (arg2, "full")) {
    if (victim->is_npc ()) {
      send_to_char ("Not on NPC's.\r\n");
      return;
    }

    if (value < 0 || value > 100) {
      send_to_char ("Full range is 0 to 100.\r\n");
      return;
    }

    victim->pcdata->condition[COND_FULL] = value;
    return;
  }

  if (!str_cmp (arg2, "name")) {
    if (!victim->is_npc ()) {
      send_to_char ("Not on PC's.\r\n");
      return;
    }

    victim->name = arg3;
    return;
  }

  if (!str_cmp (arg2, "short")) {
    victim->short_descr = arg3;
    return;
  }

  if (!str_cmp (arg2, "long")) {
    victim->long_descr = arg3;
    return;
  }

  if (!str_cmp (arg2, "title")) {
    if (victim->is_npc ()) {
      send_to_char ("Not on NPC's.\r\n");
      return;
    }

    victim->set_title(arg3);
    return;
  }

  if (!str_cmp (arg2, "spec")) {
    if (!victim->is_npc ()) {
      send_to_char ("Not on PC's.\r\n");
      return;
    }

    if ((victim->spec_fun = spec_lookup (arg3)) == 0) {
      send_to_char ("No such spec fun.\r\n");
      return;
    }

    return;
  }

  /*
   * Generate usage message.
   */
  do_mset ("");
  return;
}

void Character::do_oset (std::string argument)
{
  std::string arg1, arg2, arg3;
  Object *obj;
  int value;

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

  if (arg1.empty() || arg2.empty() || arg3.empty()) {
    send_to_char ("Syntax: oset <object> <field>  <value>\r\n");
    send_to_char ("or:     oset <object> <string> <value>\r\n");
    send_to_char ("\r\n");
    send_to_char ("Field being one of:\r\n");
    send_to_char ("  value0 value1 value2 value3\r\n");
    send_to_char ("  extra wear level weight cost timer\r\n");
    send_to_char ("\r\n");
    send_to_char ("String being one of:\r\n");
    send_to_char ("  name short long ed\r\n");
    return;
  }

  if ((obj = get_obj_world (arg1)) == NULL) {
    send_to_char ("Nothing like that in hell, earth, or heaven.\r\n");
    return;
  }

  /*
   * Snarf the value (which need not be numeric).
   */
  value = std::atoi (arg3.c_str());

  /*
   * Set something.
   */
  if (!str_cmp (arg2, "value0") || !str_cmp (arg2, "v0")) {
    obj->value[0] = value;
    return;
  }

  if (!str_cmp (arg2, "value1") || !str_cmp (arg2, "v1")) {
    obj->value[1] = value;
    return;
  }

  if (!str_cmp (arg2, "value2") || !str_cmp (arg2, "v2")) {
    obj->value[2] = value;
    return;
  }

  if (!str_cmp (arg2, "value3") || !str_cmp (arg2, "v3")) {
    obj->value[3] = value;
    return;
  }

  if (!str_cmp (arg2, "extra")) {
    obj->extra_flags = value;
    return;
  }

  if (!str_cmp (arg2, "wear")) {
    obj->wear_flags = value;
    return;
  }

  if (!str_cmp (arg2, "level")) {
    obj->level = value;
    return;
  }

  if (!str_cmp (arg2, "weight")) {
    obj->weight = value;
    return;
  }

  if (!str_cmp (arg2, "cost")) {
    obj->cost = value;
    return;
  }

  if (!str_cmp (arg2, "timer")) {
    obj->timer = value;
    return;
  }

  if (!str_cmp (arg2, "name")) {
    obj->name = arg3;
    return;
  }

  if (!str_cmp (arg2, "short")) {
    obj->short_descr = arg3;
    return;
  }

  if (!str_cmp (arg2, "long")) {
    obj->description = arg3;
    return;
  }

  if (!str_cmp (arg2, "ed")) {
    ExtraDescription *ed;

    argument = one_argument (argument, arg3);
    if (argument.empty()) {
      send_to_char ("Syntax: oset <object> ed <keyword> <string>\r\n");
      return;
    }

    ed = new ExtraDescription();

    ed->keyword = arg3;
    ed->description = argument;
    obj->extra_descr.push_back(ed);
    return;
  }

  /*
   * Generate usage message.
   */
  do_oset ("");
  return;
}

void Character::do_rset (std::string argument)
{
  std::string arg1, arg2, arg3;
  Room *location;
  int value;

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

  if (arg1.empty() || arg2.empty() || arg3.empty()) {
    send_to_char ("Syntax: rset <location> <field> value\r\n");
    send_to_char ("\r\n");
    send_to_char ("Field being one of:\r\n");
    send_to_char ("  flags sector\r\n");
    return;
  }

  if ((location = find_location (this, arg1)) == NULL) {
    send_to_char ("No such location.\r\n");
    return;
  }

  /*
   * Snarf the value.
   */
  if (!is_number (arg3)) {
    send_to_char ("Value must be numeric.\r\n");
    return;
  }
  value = std::atoi (arg3.c_str());

  /*
   * Set something.
   */
  if (!str_cmp (arg2, "flags")) {
    location->room_flags = value;
    return;
  }

  if (!str_cmp (arg2, "sector")) {
    location->sector_type = value;
    return;
  }

  /*
   * Generate usage message.
   */
  do_rset ("");
  return;
}

void Character::do_users (std::string argument)
{
  char buf[MAX_STRING_LENGTH];
  char buf2[MAX_STRING_LENGTH];
  int count;

  count = 0;
  buf[0] = '\0';
  buf2[0] = '\0';
  for (DescIter d = descriptor_list.begin();
    d != descriptor_list.end(); d++) {
    if ((*d)->character != NULL && can_see((*d)->character)) {
      count++;
      snprintf (buf + strlen(buf), sizeof(buf) - strlen(buf), "[%3d %2d] %s@%s\r\n",
        (*d)->descriptor,
        (*d)->connected,
        (*d)->original ? (*d)->original->name.c_str() :
        (*d)->character ? (*d)->character->name.c_str() : "(none)", (*d)->host.c_str());
    }
  }

  snprintf (buf2, sizeof buf2, "%d user%s\r\n", count, count == 1 ? "" : "s");
  strncat (buf, buf2, sizeof buf - sizeof buf2);
  send_to_char (buf);
  return;
}

/*
 * Thanks to Grodyn for pointing out bugs in this function.
 */
void Character::do_force (std::string argument)
{
  std::string arg;
  int trst;
  int cmd;

  argument = one_argument (argument, arg);

  if (arg.empty() || argument.empty()) {
    send_to_char ("Force whom to do what?\r\n");
    return;
  }

  /*
   * Look for command in command table.
   */
  trst = get_trust ();
  for (cmd = 0; cmd_table[cmd].name[0] != '\0'; cmd++) {
    if (argument[0] == cmd_table[cmd].name[0]
      && !str_prefix (argument, cmd_table[cmd].name)
      && (cmd_table[cmd].level > trst && cmd_table[cmd].level != 41)) {
      send_to_char ("You cant even do that yourself!\r\n");
      return;
    }
  }

  if (!str_cmp (arg, "all")) {
    Character *vch;
    CharIter c, next;
    for (c = char_list.begin(); c != char_list.end(); c = next) {
      vch = *c;
      next = ++c;
      if (!vch->is_npc () && vch->get_trust () < get_trust ()) {
        MOBtrigger = false;
        act ("$n forces you to '$t'.", argument.c_str(), vch, TO_VICT);
        vch->interpret (argument);
      }
    }
  } else {
    Character *victim;

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

    if (victim == this) {
      send_to_char ("Aye aye, right away!\r\n");
      return;
    }

    if (victim->get_trust () >= get_trust ()) {
      send_to_char ("Do it yourself!\r\n");
      return;
    }

    MOBtrigger = false;
    act ("$n forces you to '$t'.", argument.c_str(), victim, TO_VICT);
    victim->interpret (argument);
  }

  send_to_char ("Ok.\r\n");
  return;
}

void Character::do_holylight (std::string argument)
{
  if (is_npc ())
    return;

  if (IS_SET (actflags, PLR_HOLYLIGHT)) {
    REMOVE_BIT (actflags, PLR_HOLYLIGHT);
    send_to_char ("Holy light mode off.\r\n");
  } else {
    SET_BIT (actflags, PLR_HOLYLIGHT);
    send_to_char ("Holy light mode on.\r\n");
  }

  return;
}

/* Wizify and Wizbit sent in by M. B. King */
void Character::do_wizify (std::string argument)
{
  std::string arg1;
  Character *victim;

  argument = one_argument (argument, arg1);
  if (arg1.empty()) {
    send_to_char ("Syntax: wizify <name>\r\n");
    return;
  }
  if ((victim = get_char_world (arg1)) == NULL) {
    send_to_char ("They aren't here.\r\n");
    return;
  }
  if (victim->is_npc ()) {
    send_to_char ("Not on mobs.\r\n");
    return;
  }
  victim->wizbit = !victim->wizbit;
  if (victim->wizbit) {
    act ("$N wizified.\r\n", NULL, victim, TO_CHAR);
    act ("$n has wizified you!\r\n", NULL, victim, TO_VICT);
  } else {
    act ("$N dewizzed.\r\n", NULL, victim, TO_CHAR);
    act ("$n has dewizzed you!\r\n", NULL, victim, TO_VICT);
  }

  victim->do_save ("");
  return;
}

/* Idea from Talen of Vego's do_where command */
void Character::do_owhere (std::string argument)
{
  char buf[MAX_STRING_LENGTH];
  std::string arg;
  bool found = false;
  Object *in_obj;
  int obj_counter = 1;

  one_argument (argument, arg);

  if (arg.empty()) {
    send_to_char ("Syntax:  owhere <object>.\r\n");
    return;
  } else {
    ObjIter o;
    for (o = object_list.begin(); o != object_list.end(); o++) {
      if (!can_see_obj(*o) || !is_name (arg, (*o)->name))
        continue;

      found = true;

      for (in_obj = *o; in_obj->in_obj != NULL; in_obj = in_obj->in_obj) ;

      if (in_obj->carried_by != NULL) {
        snprintf (buf, sizeof buf, "[%2d] %s carried by %s.\r\n", obj_counter,
          (*o)->short_descr.c_str(), in_obj->carried_by->describe_to(this).c_str());
      } else {
        snprintf (buf, sizeof buf, "[%2d] %s in %s.\r\n", obj_counter,
          (*o)->short_descr.c_str(), (in_obj->in_room == NULL) ?
          "somewhere" : in_obj->in_room->name.c_str());
      }

      obj_counter++;
      buf[0] = toupper (buf[0]);
      send_to_char (buf);
    }
  }

  if (!found)
    send_to_char ("Nothing like that in hell, earth, or heaven.\r\n");

  return;
}