/***************************************************************************
 *  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 "descriptor.hpp"
#include "pcdata.hpp"
#include "object.hpp"
#include "room.hpp"
#include "mobproto.hpp"
#include "objproto.hpp"
#include "affect.hpp"
#include "area.hpp"
#include "exit.hpp"
#include "database.hpp"
#include "world.hpp"

// temp globals
extern void multi_hit(Character *ch, Character *victim, int dt);
extern void mprog_act_trigger (const std::string & buf, Character * mob, Character * ch,
  Object * obj, void *vo);
extern void mprog_entry_trigger (Character * mob);
extern void mprog_greet_trigger (Character * mob);
extern std::string get_title (int klass, int level, int sex);

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


Character::Character() :
  master(NULL), leader(NULL), fighting(NULL),
  reply(NULL), spec_fun(NULL), pIndexData(NULL), desc(NULL),
  pnote(NULL), in_room(NULL), was_in_room(NULL),
  pcdata(NULL), sex(0), klass(0), race(0), level(0), trust(0), wizbit(false),
  played(0), save_time(0), last_note(0), timer(0),
  wait(0), hit(20), max_hit(20), mana(100), max_mana(100), move(100),
  max_move(100), gold(0), exp(0), actflags(0), affected_by(0),
  position(POS_STANDING), practice(21), carry_weight(0), carry_number(0),
  saving_throw(0), alignment(0), hitroll(0), damroll(0), armor(100),
  wimpy(0), deaf(0), mpact(NULL), mpactnum(0) {
  logon = g_world->get_current_time();
}

/*
 * Free a character.
 */
Character::~Character()
{
  Object *obj;
  Affect *paf;

  ObjIter o, onext;
  for (o = carrying.begin(); o != carrying.end(); o = onext) {
    obj = *o;
    onext = ++o;
    obj->extract_obj ();
  }

  AffIter af, anext;
  for (af = affected.begin(); af != affected.end(); af = anext) {
    paf = *af;
    anext = ++af;
    affect_remove (paf);
  }

  if (pcdata != NULL) {
    delete pcdata;
  }

  return;
}

int Character::mana_cost(int sn) {
  if (is_npc())
    return 0;
  else
    return std::max(skill_table[sn].min_mana, 100 /
      (2 + level - skill_table[sn].skill_level[klass]));
}

/*
 * Write to one char.
 */
void Character::send_to_char (const std::string & txt)
{
  if (txt.empty() || desc == NULL)
    return;
  desc->showstr_head = (char*)std::malloc(txt.size()+1);
  strncpy (desc->showstr_head, txt.c_str(), txt.size()+1);
  desc->showstr_point = desc->showstr_head;
  desc->show_string ("");

}

/*
 * Append a string to a file.
 */
void Character::append_file (const char *file, const std::string & str)
{
  if (is_npc () || str.empty())
    return;

  std::ofstream outfile;

  outfile.open (file, std::ofstream::out | std::ofstream::app | std::ofstream::binary);
  if (outfile.is_open()) {
    outfile << "[" << std::setw(5) << (in_room ? in_room->vnum : 0) <<
      "] " << name << ": " << str << std::endl;
    outfile.close();
  } else {
    std::perror (file);
    send_to_char ("Could not open the file!\r\n");
  }
  return;
}

/*
 * True if char can see victim.
 */
bool Character::can_see (Character * victim)
{
  if (this == victim)
    return true;

  if (!is_npc () && IS_SET (actflags, PLR_HOLYLIGHT))
    return true;

  if (is_affected (AFF_BLIND))
    return false;

  if (in_room->is_dark() && !is_affected (AFF_INFRARED))
    return false;

  if (victim->is_affected (AFF_INVISIBLE)
    && !is_affected (AFF_DETECT_INVIS))
    return false;

  if (victim->is_affected (AFF_HIDE)
    && !is_affected (AFF_DETECT_HIDDEN)
    && victim->fighting == NULL
    && (is_npc () ? !victim->is_npc () : victim->is_npc ()))
    return false;

  return true;
}

/*
 * True if char can see obj.
 */
bool Character::can_see_obj (Object * obj)
{
  if (!is_npc () && IS_SET (actflags, PLR_HOLYLIGHT))
    return true;

  if (obj->item_type == ITEM_POTION)
    return true;

  if (is_affected (AFF_BLIND))
    return false;

  if (obj->item_type == ITEM_LIGHT && obj->value[2] != 0)
    return true;

  if (in_room->is_dark() && !is_affected (AFF_INFRARED))
    return false;

  if (IS_SET (obj->extra_flags, ITEM_INVIS)
    && !is_affected (AFF_DETECT_INVIS))
    return false;

  return true;
}

/*
 * True if char can drop obj.
 */
bool Character::can_drop_obj (Object * obj)
{
  if (!IS_SET (obj->extra_flags, ITEM_NODROP))
    return true;

  if (!is_npc () && level >= LEVEL_IMMORTAL)
    return true;

  return false;
}

/*
 * The primary output interface for formatted output.
 */
void Character::act (const std::string & format, const void *arg1, const void *arg2, int type)
{
  static const char * he_she[] = { "it", "he", "she" };
  static const char * him_her[] = { "it", "him", "her" };
  static const char * his_her[] = { "its", "his", "her" };

  Character *vch = (Character *) arg2;
  Object *obj1 = (Object *) arg1;
  Object *obj2 = (Object *) arg2;

  /*
   * Discard null and zero-length messages.
   */
  if (format.empty())
    return;

  CharIter to = in_room->people.begin();
  CharIter tend = in_room->people.end();

  if (type == TO_VICT) {
    if (vch == NULL) {
      bug_printf ("Act: null vch with TO_VICT.");
      return;
    }
    to = vch->in_room->people.begin();
    tend = vch->in_room->people.end();
  }

  for (; to != tend; to++) {
    if (((*to)->desc == NULL
        && ((*to)->is_npc () && !((*to)->pIndexData->progtypes & ACT_PROG)))
      || !(*to)->is_awake ())
      continue;

    if (type == TO_CHAR && *to != this)
      continue;
    if (type == TO_VICT && (*to != vch || *to == this))
      continue;
    if (type == TO_ROOM && *to == this)
      continue;
    if (type == TO_NOTVICT && (*to == this || *to == vch))
      continue;
    if (!is_npc() && (*to)->is_gagged(name))
      continue;

    std::string buf;

    std::string::const_iterator str = format.begin();
    while (str != format.end()) {
      if (*str != '$') {
        buf.append(1, *str);
        str++;
        continue;
      }
      ++str;

      if (arg2 == NULL && *str >= 'A' && *str <= 'Z') {
        bug_printf ("Act: missing arg2 for code %d.", *str);
        buf.append(" <@@@> ");
      } else {
        switch (*str) {
        default:
          bug_printf ("Act: bad code %d.", *str);
          buf.append(" <@@@> ");
          break;
          /* Thx alex for 't' idea */
        case 't':
          buf.append((char *) arg1);
          break;
        case 'T':
          buf.append((char *) arg2);
          break;
        case 'n':
          buf.append(describe_to(*to));
          break;
        case 'N':
          buf.append(vch->describe_to(*to));
          break;
        case 'e':
          buf.append(he_she[URANGE (0, sex, 2)]);
          break;
        case 'E':
          buf.append(he_she[URANGE (0, vch->sex, 2)]);
          break;
        case 'm':
          buf.append(him_her[URANGE (0, sex, 2)]);
          break;
        case 'M':
          buf.append(him_her[URANGE (0, vch->sex, 2)]);
          break;
        case 's':
          buf.append(his_her[URANGE (0, sex, 2)]);
          break;
        case 'S':
          buf.append(his_her[URANGE (0, vch->sex, 2)]);
          break;

        case 'p':
          buf.append((*to)->can_see_obj(obj1)
            ? obj1->short_descr.c_str() : "something");
          break;

        case 'P':
          buf.append((*to)->can_see_obj(obj2)
            ? obj2->short_descr.c_str() : "something");
          break;

        case 'd':
          if (arg2 == NULL || ((char *) arg2)[0] == '\0') {
            buf.append("door");
          } else {
            std::string fname;
            one_argument ((char *) arg2, fname);
            buf.append(fname);
          }
          break;
        }
      }

      ++str;
    }

    buf.append("\r\n");
    buf[0] = toupper(buf[0]);
    if ((*to)->desc)
      (*to)->desc->write_to_buffer (buf);
    if (MOBtrigger)
      mprog_act_trigger (buf, *to, this, obj1, vch);
    /* Added by Kahn */
  }

  MOBtrigger = true;
  return;
}

bool Character::is_npc() {
  return actflags & ACT_IS_NPC;
}

bool Character::is_awake() {
  return position > POS_SLEEPING;
}

bool Character::is_good() {
  return alignment >= 350;
}

bool Character::is_evil() {
  return alignment <= -350;
}

bool Character::is_neutral() {
  return !is_good() && !is_evil();
}

bool Character::is_affected(int flg) {
  return affected_by & flg;
}

int Character::get_ac() {
  if (is_awake())
    return armor + dex_app[get_curr_dex()].defensive;
  else
    return armor;
}

int Character::get_hitroll() {
  return hitroll + str_app[get_curr_str()].tohit;
}

int Character::get_damroll() {
  return damroll + str_app[get_curr_str()].todam;
}

/*
 * Retrieve character's current strength.
 */
int Character::get_curr_str() {
  int max;

  if (is_npc ())
    return 13;

  if (class_table[klass].attr_prime == APPLY_STR)
    max = 25;
  else
    max = 22;

  return URANGE (3, pcdata->get_curr("str"), max);
}

/*
 * Retrieve character's current intelligence.
 */
int Character::get_curr_int() {
  int max;

  if (is_npc ())
    return 13;

  if (class_table[klass].attr_prime == APPLY_INT)
    max = 25;
  else
    max = 22;

  return URANGE (3, pcdata->get_curr("int"), max);
}

/*
 * Retrieve character's current wisdom.
 */
int Character::get_curr_wis() {
  int max;

  if (is_npc ())
    return 13;

  if (class_table[klass].attr_prime == APPLY_WIS)
    max = 25;
  else
    max = 22;

  return URANGE (3, pcdata->get_curr("wis"), max);
}

/*
 * Retrieve character's current dexterity.
 */
int Character::get_curr_dex() {
  int max;

  if (is_npc ())
    return 13;

  if (class_table[klass].attr_prime == APPLY_DEX)
    max = 25;
  else
    max = 22;

  return URANGE (3, pcdata->get_curr("dex"), max);
}

/*
 * Retrieve character's current constitution.
 */
int Character::get_curr_con() {
  int max;

  if (is_npc ())
    return 13;

  if (class_table[klass].attr_prime == APPLY_CON)
    max = 25;
  else
    max = 22;

  return URANGE (3, pcdata->get_curr("con"), max);
}

/*
 * Retrieve a character's age.
 */
int Character::get_age() {
  return 17 + (played + (int) (g_world->get_current_time() - logon)) / 14400;
  /* 12240 assumes 30 second hours, 24 hours a day, 20 day - Kahn */
}

/*
 * Retrieve a character's carry capacity.
 */
int Character::can_carry_n() {
  if (!is_npc () && level >= LEVEL_IMMORTAL)
    return 1000;

  if (is_npc () && IS_SET (actflags, ACT_PET))
    return 0;

  return MAX_WEAR + 2 * get_curr_dex() / 2;
}

/*
 * Retrieve a character's carry capacity.
 */
int Character::can_carry_w() {
  if (!is_npc () && level >= LEVEL_IMMORTAL)
    return 1000000;

  if (is_npc () && IS_SET (actflags, ACT_PET))
    return 0;

  return str_app[get_curr_str()].carry;
}

/*
 * Retrieve a character's trusted level for permission checking.
 */
int Character::get_trust() {
  Character *ch;

  if (desc != NULL && desc->original != NULL)
    ch = desc->original;
  else
    ch = this;

  if (ch->trust != 0)
    return ch->trust;

  if (ch->is_npc () && ch->level >= LEVEL_HERO)
    return LEVEL_HERO - 1;
  else
    return ch->level;
}

bool Character::is_immortal() {
  return get_trust() >= LEVEL_IMMORTAL;
}

bool Character::is_hero() {
  return get_trust() >= LEVEL_HERO;
}

int Character::is_outside() {
  return !(in_room->room_flags & ROOM_INDOORS);
}

void Character::wait_state(int npulse) {
  wait = std::max(wait, npulse);
}

/*
 * Compute a saving throw.
 * Negative apply's make saving throw better.
 */
bool Character::saves_spell (int lvl) {
  int save;

  save = 50 + (level - lvl - saving_throw) * 5;
  save = URANGE (5, save, 95);
  return number_percent () < save;
}

std::string Character::describe_to (Character* looker) {
  if (looker->can_see(this)) {
    if (is_npc())
      return short_descr;
    else
      return name;
  } else {
    return "someone";
  }
}

/*
 * Find a piece of eq on a character.
 */
Object * Character::get_eq_char (int iWear)
{
  for (ObjIter o = carrying.begin(); o != carrying.end(); o++) {
    if ((*o)->wear_loc == iWear)
      return *o;
  }

  return NULL;
}

/*
 * Apply or remove an affect to a character.
 */
void Character::affect_modify (Affect * paf, bool fAdd)
{
  Object *wield;

  int mod = paf->modifier;

  if (fAdd) {
    SET_BIT (affected_by, paf->bitvector);
  } else {
    REMOVE_BIT (affected_by, paf->bitvector);
    mod = 0 - mod;
  }

  if (is_npc ())
    return;

  switch (paf->location) {
  default:
    bug_printf ("Affect_modify: unknown location %d.", paf->location);
    return;

  case APPLY_NONE:
    break;
  case APPLY_STR:
    pcdata->set_mod("str", pcdata->get_mod("str") + mod);
    break;
  case APPLY_DEX:
    pcdata->set_mod("dex", pcdata->get_mod("dex") + mod);
    break;
  case APPLY_INT:
    pcdata->set_mod("int", pcdata->get_mod("int") + mod);
    break;
  case APPLY_WIS:
    pcdata->set_mod("wis", pcdata->get_mod("wis") + mod);
    break;
  case APPLY_CON:
    pcdata->set_mod("con", pcdata->get_mod("con") + mod);
    break;
  case APPLY_SEX:
    sex += mod;
    break;
  case APPLY_CLASS:
    break;
  case APPLY_LEVEL:
    break;
  case APPLY_AGE:
    break;
  case APPLY_HEIGHT:
    break;
  case APPLY_WEIGHT:
    break;
  case APPLY_MANA:
    max_mana += mod;
    break;
  case APPLY_HIT:
    max_hit += mod;
    break;
  case APPLY_MOVE:
    max_move += mod;
    break;
  case APPLY_GOLD:
    break;
  case APPLY_EXP:
    break;
  case APPLY_AC:
    armor += mod;
    break;
  case APPLY_HITROLL:
    hitroll += mod;
    break;
  case APPLY_DAMROLL:
    damroll += mod;
    break;
  case APPLY_SAVING_PARA:
    saving_throw += mod;
    break;
  case APPLY_SAVING_ROD:
    saving_throw += mod;
    break;
  case APPLY_SAVING_PETRI:
    saving_throw += mod;
    break;
  case APPLY_SAVING_BREATH:
    saving_throw += mod;
    break;
  case APPLY_SAVING_SPELL:
    saving_throw += mod;
    break;
  }

  /*
   * Check for weapon wielding.
   * Guard against recursion (for weapons with affects).
   */
  if ((wield = get_eq_char (WEAR_WIELD)) != NULL
    && wield->get_obj_weight() > str_app[get_curr_str()].wield) {
    static int depth;

    if (depth == 0) {
      depth++;
      act ("You drop $p.", wield, NULL, TO_CHAR);
      act ("$n drops $p.", wield, NULL, TO_ROOM);
      wield->obj_from_char();
      wield->obj_to_room (in_room);
      depth--;
    }
  }

  return;
}

/*
 * Unequip a char with an obj.
 */
void Character::unequip_char (Object * obj)
{
  if (obj->wear_loc == WEAR_NONE) {
    bug_printf ("Unequip_char: already unequipped.");
    return;
  }

  armor += obj->apply_ac (obj->wear_loc);
  obj->wear_loc = -1;

  AffIter paf;
  for (paf = obj->pIndexData->affected.begin(); paf != obj->pIndexData->affected.end(); paf++)
    affect_modify (*paf, false);
  for (paf = obj->affected.begin(); paf != obj->affected.end(); paf++)
    affect_modify (*paf, false);

  if (obj->item_type == ITEM_LIGHT
    && obj->value[2] != 0 && in_room != NULL && in_room->light > 0)
    --in_room->light;

  if (obj->item_type == ITEM_DARKNESS
    && obj->value[2] != 0 && in_room != NULL && in_room->light < 1)
    ++in_room->light;

  return;
}

/*
 * Give an affect to a char.
 */
void Character::affect_to_char (Affect * paf)
{
  Affect *paf_new = new Affect();

  *paf_new = *paf;
  affected.push_back(paf_new);

  affect_modify (paf_new, true);
  return;
}

/*
 * Remove an affect from a char.
 */
void Character::affect_remove (Affect * paf)
{
  if (affected.empty()) {
    bug_printf ("Affect_remove: no affect.");
    return;
  }

  affect_modify (paf, false);

  affected.erase(find(affected.begin(), affected.end(), paf));

  delete paf;
  return;
}

/*
 * Strip all affects of a given sn.
 */
void Character::affect_strip (int sn)
{
  Affect *paf;

  AffIter af, next;
  for (af = affected.begin(); af != affected.end(); af = next) {
    paf = *af;
    next = ++af;
    if (paf->type == sn)
      affect_remove (paf);
  }

  return;
}

/*
 * Return true if a char is affected by a spell.
 */
bool Character::has_affect (int sn)
{
  AffIter af;
  for (af = affected.begin(); af != affected.end(); af++) {
    if ((*af)->type == sn)
      return true;
  }

  return false;
}

/*
 * Add or enhance an affect.
 */
void Character::affect_join (Affect * paf)
{
  AffIter af;
  for (af = affected.begin(); af != affected.end(); af++) {
    if ((*af)->type == paf->type) {
      paf->duration += (*af)->duration;
      paf->modifier += (*af)->modifier;
      affect_remove (*af);
      break;
    }
  }

  affect_to_char (paf);
  return;
}

/*
 * Find a char in the room.
 */
Character * Character::get_char_room (const std::string & argument)
{
  std::string arg;
  int number;
  int count;

  number = number_argument (argument, arg);
  count = 0;
  if (!str_cmp (arg, "self"))
    return this;

  CharIter rch;
  for (rch = in_room->people.begin(); rch != in_room->people.end(); rch++) {
    if (!can_see(*rch) || !is_name (arg, (*rch)->name))
      continue;
    if (++count == number)
      return *rch;
  }

  return NULL;
}

/*
 * Find a char in the world.
 */
Character * Character::get_char_world (const std::string & argument)
{
  std::string arg;
  Character *wch;
  int number;
  int count;

  if ((wch = get_char_room (argument)) != NULL)
    return wch;

  number = number_argument (argument, arg);
  count = 0;
  CharIter c;
  for (c = char_list.begin(); c != char_list.end(); c++) {
    if (!can_see(*c) || !is_name (arg, (*c)->name))
      continue;
    if (++count == number)
      return *c;
  }

  return NULL;
}

/*
 * Find an obj in a list.
 */
Object * Character::get_obj_list (const std::string & argument, std::list<Object *> & list)
{
  std::string arg;
  int number;
  int count;

  number = number_argument (argument, arg);
  count = 0;
  ObjIter obj;
  for (obj = list.begin(); obj != list.end(); obj++) {
    if (can_see_obj(*obj) && is_name (arg, (*obj)->name)) {
      if (++count == number)
        return *obj;
    }
  }

  return NULL;
}

/*
 * Find an obj in player's inventory.
 */
Object * Character::get_obj_carry (const std::string & argument)
{
  std::string arg;
  int number;
  int count;

  number = number_argument (argument, arg);
  count = 0;
  ObjIter o;
  for (o = carrying.begin(); o != carrying.end(); o++) {
    if ((*o)->wear_loc == WEAR_NONE && can_see_obj(*o)
      && is_name (arg, (*o)->name)) {
      if (++count == number)
        return *o;
    }
  }

  return NULL;
}

/*
 * Find an obj in player's equipment.
 */
Object * Character::get_obj_wear (const std::string & argument)
{
  std::string arg;
  int number;
  int count;

  number = number_argument (argument, arg);
  count = 0;
  ObjIter o;
  for (o = carrying.begin(); o != carrying.end(); o++) {
    if ((*o)->wear_loc != WEAR_NONE && can_see_obj(*o)
      && is_name (arg, (*o)->name)) {
      if (++count == number)
        return *o;
    }
  }

  return NULL;
}

/*
 * Find an obj in the room or in inventory.
 */
Object * Character::get_obj_here (const std::string & argument)
{
  Object *obj;

  obj = get_obj_list (argument, in_room->contents);
  if (obj != NULL)
    return obj;

  if ((obj = get_obj_carry (argument)) != NULL)
    return obj;

  if ((obj = get_obj_wear (argument)) != NULL)
    return obj;

  return NULL;
}

/*
 * Find an obj in the world.
 */
Object * Character::get_obj_world (const std::string & argument)
{
  std::string arg;
  Object *obj;

  if ((obj = get_obj_here (argument)) != NULL)
    return obj;

  int number = number_argument (argument, arg);
  int count = 0;
  for (ObjIter o = object_list.begin();
    o != object_list.end(); o++) {
    if (can_see_obj(*o) && is_name (arg, (*o)->name)) {
      if (++count == number)
        return *o;
    }
  }

  return NULL;
}

/*
 * Write the char.
 */
void Character::fwrite_char (std::ofstream & fp)
{

  fp << "#" << (is_npc () ? "MOB" : "PLAYER") << "\n";

  fp << "Name         " << name << "~\n";
  fp << "ShortDescr   " << short_descr << "~\n";
  fp << "LongDescr    " << long_descr << "~\n";
  fp << "Description  " << description << "~\n";
  fp << "Prompt       " << prompt << "~\n";
  fp << "Sex          " << sex << "\n";
  fp << "Class        " << klass << "\n";
  fp << "Race         " << race << "\n";
  fp << "Level        " << level << "\n";
  fp << "Trust        " << trust << "\n";
  fp << "Wizbit       " << wizbit << "\n";
  fp << "Played       " << played + (int) (g_world->get_current_time() - logon) << "\n";
  fp << "Note         " << last_note << "\n";
  fp << "Room         " <<
    ((in_room == get_room_index (ROOM_VNUM_LIMBO)
      && was_in_room != NULL)
    ? was_in_room->vnum : in_room->vnum) << "\n";

  fp << "HpManaMove   " << hit << " " << max_hit << " " <<
       mana << " " << max_mana << " " << move << " " <<
       max_move << "\n";
  fp << "Gold         " << gold << "\n";
  fp << "Exp          " << exp << "\n";
  fp << "Act          " << actflags << "\n";
  fp << "AffectedBy   " << affected_by << "\n";
  /* Bug fix from Alander */
  if (position == POS_FIGHTING)
    fp << "Position     " <<  POS_STANDING<< "\n";
  else
    fp << "Position     " <<  position << "\n";

  fp << "Practice     " << practice << "\n";
  fp << "SavingThrow  " << saving_throw << "\n";
  fp << "Alignment    " << alignment << "\n";
  fp << "Hitroll      " << hitroll << "\n";
  fp << "Damroll      " << damroll << "\n";
  fp << "Armor        " << armor << "\n";
  fp << "Wimpy        " << wimpy << "\n";
  fp << "Deaf         " << deaf << "\n";

  if (is_npc ()) {
    fp << "Vnum         " << pIndexData->vnum << "\n";
  } else {
    fp << "Password     " << pcdata->pwd << "~\n";
    fp << "Bamfin       " << pcdata->bamfin << "~\n";
    fp << "Bamfout      " << pcdata->bamfout << "~\n";
    fp << "Title        " << pcdata->title << "~\n";
    fp << "AttrPerm     " << pcdata->get_perm("str") << " " << pcdata->get_perm("int") <<
      " " << pcdata->get_perm("wis") << " " << pcdata->get_perm("dex") << " " <<
      pcdata->get_perm("con") << "\n";

    fp << "AttrMod      " << pcdata->get_mod("str") << " " << pcdata->get_mod("int") << " "
      << pcdata->get_mod("wis") << " " << pcdata->get_mod("dex") << " " <<
      pcdata->get_mod("con") << "\n";

    fp << "Condition    " << pcdata->condition[0] << " " <<
      pcdata->condition[1] << " " << pcdata->condition[2] << "\n";

    fp << "Pagelen      " << pcdata->pagelen << "\n";

    for (int sn = 0; sn < MAX_SKILL; sn++) {
      if (skill_table[sn].name != NULL && pcdata->learned[sn] > 0) {
        fp << "Skill        " << pcdata->learned[sn] << "'" <<
          skill_table[sn].name << "'\n";
      }
    }
    for (std::list<std::string>::iterator gag = pcdata->gag_list.begin();
      gag != pcdata->gag_list.end(); gag++) {
      fp << "Gag          " << *gag << "~\n";
    }
  }

  for (AffIter af = affected.begin(); af != affected.end(); af++) {
    fp << "Affect " << (*af)->type << " " << (*af)->duration << " " <<
      (*af)->modifier << " " << (*af)->location << " " <<
      (*af)->bitvector << "\n";
  }

  fp << "End\n\n";
  return;
}

/*
 * Save a character and inventory.
 * Would be cool to save NPC's too for quest purposes,
 *   some of the infrastructure is provided.
 */
void Character::save_char_obj ()
{
  char strsave[MAX_INPUT_LENGTH];
  std::ofstream fp;

  if (is_npc () || level < 2)
    return;

  Character * ch = this;
  if (desc != NULL && desc->original != NULL)
    ch = desc->original;

  ch->save_time = g_world->get_current_time();

  /* player files parsed directories by Yaz 4th Realm */
  snprintf (strsave, sizeof strsave, "%s%s", PLAYER_DIR, capitalize(ch->name).c_str());
  fp.open (strsave, std::ofstream::out | std::ofstream::binary);
  if (!fp.is_open()) {
    bug_printf ("Save_char_obj: fopen");
    std::perror (strsave);
  } else {
    ch->fwrite_char (fp);
    std::list<Object*>::reverse_iterator o;
    for (o = ch->carrying.rbegin(); o != ch->carrying.rend(); o++)
      (*o)->fwrite_obj (ch, fp, 0);
    fp << "#END\n";
  }
  fp.close();
  return;
}

/*
 * Read in a char.
 */
void Character::fread_char (std::ifstream & fp)
{
  std::string word;
  bool fMatch;

  for (;;) {
    word = fp.eof() ? std::string("End") : fread_word (fp);
    fMatch = false;

    switch (toupper (word[0])) {
    case '*':
      fMatch = true;
      fread_to_eol (fp);
      break;

    case 'A':
      KEY ("Act", actflags, fread_number (fp));
      KEY ("AffectedBy", affected_by, fread_number (fp));
      KEY ("Alignment", alignment, fread_number (fp));
      KEY ("Armor", armor, fread_number (fp));

      if (!str_cmp (word, "Affect")) {
        Affect *paf;

        paf = new Affect();

        paf->type = fread_number (fp);
        paf->duration = fread_number (fp);
        paf->modifier = fread_number (fp);
        paf->location = fread_number (fp);
        paf->bitvector = fread_number (fp);
        affected.push_back(paf);
        fMatch = true;
        break;
      }

      if (!str_cmp (word, "AttrMod")) {
        pcdata->set_mod("str", fread_number (fp));
        pcdata->set_mod("int", fread_number (fp));
        pcdata->set_mod("wis", fread_number (fp));
        pcdata->set_mod("dex", fread_number (fp));
        pcdata->set_mod("con", fread_number (fp));
        fMatch = true;
        break;
      }

      if (!str_cmp (word, "AttrPerm")) {
        pcdata->set_perm("str", fread_number (fp));
        pcdata->set_perm("int", fread_number (fp));
        pcdata->set_perm("wis", fread_number (fp));
        pcdata->set_perm("dex", fread_number (fp));
        pcdata->set_perm("con", fread_number (fp));
        fMatch = true;
        break;
      }
      break;

    case 'B':
      KEY ("Bamfin", pcdata->bamfin, fread_string (fp));
      KEY ("Bamfout", pcdata->bamfout, fread_string (fp));
      break;

    case 'C':
      KEY ("Class", klass, fread_number (fp));

      if (!str_cmp (word, "Condition")) {
        pcdata->condition[0] = fread_number (fp);
        pcdata->condition[1] = fread_number (fp);
        pcdata->condition[2] = fread_number (fp);
        fMatch = true;
        break;
      }
      break;

    case 'D':
      KEY ("Damroll", damroll, fread_number (fp));
      KEY ("Deaf", deaf, fread_number (fp));
      KEY ("Description", description, fread_string (fp));
      break;

    case 'E':
      if (!str_cmp (word, "End"))
        return;
      KEY ("Exp", exp, fread_number (fp));
      break;

    case 'G':
      KEY ("Gold", gold, fread_number (fp));
      if (!str_cmp (word, "Gag")) {
        pcdata->gag_list.push_back(fread_string (fp));
        fMatch = true;
        break;
      }
      break;

    case 'H':
      KEY ("Hitroll", hitroll, fread_number (fp));

      if (!str_cmp (word, "HpManaMove")) {
        hit = fread_number (fp);
        max_hit = fread_number (fp);
        mana = fread_number (fp);
        max_mana = fread_number (fp);
        move = fread_number (fp);
        max_move = fread_number (fp);
        fMatch = true;
        break;
      }
      break;

    case 'L':
      KEY ("Level", level, fread_number (fp));
      KEY ("LongDescr", long_descr, fread_string (fp));
      break;

    case 'N':
      if (!str_cmp (word, "Name")) {
        /*
         * Name already set externally.
         */
        fread_to_eol (fp);
        fMatch = true;
        break;
      }
      KEY ("Note", last_note, fread_number (fp));
      break;

    case 'P':
      KEY ("Pagelen", pcdata->pagelen, fread_number (fp));
      KEY ("Password", pcdata->pwd, fread_string (fp));
      KEY ("Played", played, fread_number (fp));
      KEY ("Position", position, fread_number (fp));
      KEY ("Practice", practice, fread_number (fp));
      KEY ("Prompt", prompt, fread_string (fp));
      break;

    case 'R':
      KEY ("Race", race, fread_number (fp));

      if (!str_cmp (word, "Room")) {
        in_room = get_room_index (fread_number (fp));
        if (in_room == NULL)
          in_room = get_room_index (ROOM_VNUM_LIMBO);
        fMatch = true;
        break;
      }

      break;

    case 'S':
      KEY ("SavingThrow", saving_throw, fread_number (fp));
      KEY ("Sex", sex, fread_number (fp));
      KEY ("ShortDescr", short_descr, fread_string (fp));

      if (!str_cmp (word, "Skill")) {
        int sn;
        int value;

        value = fread_number (fp);
        sn = skill_lookup (fread_word (fp));
        if (sn < 0)
          bug_printf ("Fread_char: unknown skill.");
        else
          pcdata->learned[sn] = value;
        fMatch = true;
      }

      break;

    case 'T':
      KEY ("Trust", trust, fread_number (fp));

      if (!str_cmp (word, "Title")) {
        pcdata->title = fread_string (fp);
        if (isalpha (pcdata->title[0]) || isdigit (pcdata->title[0])) {
          pcdata->title = " " + pcdata->title;
        }
        fMatch = true;
        break;
      }

      break;

    case 'V':
      if (!str_cmp (word, "Vnum")) {
        pIndexData = get_mob_index (fread_number (fp));
        fMatch = true;
        break;
      }
      break;

    case 'W':
      KEY ("Wimpy", wimpy, fread_number (fp));
      KEY ("Wizbit", wizbit, fread_number (fp));
      break;
    }

    /* Make sure old chars have this field - Kahn */
    if (!pcdata->pagelen)
      pcdata->pagelen = 20;
    if (prompt.empty())
      prompt = "<%h %m %mv> ";

    if (!fMatch) {
      bug_printf ("Fread_char: no match.");
      fread_to_eol (fp);
    }
  }
}

/*
 * Equip a char with an obj.
 */
void Character::equip_char (Object * obj, int iWear)
{

  if (get_eq_char (iWear) != NULL) {
    bug_printf ("Equip_char: already equipped (%d).", iWear);
    return;
  }

  if ((obj->is_obj_stat(ITEM_ANTI_EVIL) && is_evil ())
    || (obj->is_obj_stat(ITEM_ANTI_GOOD) && is_good ())
    || (obj->is_obj_stat(ITEM_ANTI_NEUTRAL) && is_neutral ())) {
    /*
     * Thanks to Morgenes for the bug fix here!
     */
    act ("You are zapped by $p and drop it.", obj, NULL, TO_CHAR);
    act ("$n is zapped by $p and drops it.", obj, NULL, TO_ROOM);
    obj->obj_from_char();
    obj->obj_to_room (in_room);
    return;
  }

  armor -= obj->apply_ac (iWear);
  obj->wear_loc = iWear;

  AffIter af;
  for (af = obj->pIndexData->affected.begin(); af != obj->pIndexData->affected.end(); af++)
    affect_modify (*af, true);
  for (af = obj->affected.begin(); af != obj->affected.end(); af++)
    affect_modify (*af, true);

  if (obj->item_type == ITEM_LIGHT
    && obj->value[2] != 0 && in_room != NULL)
    ++in_room->light;

  if (obj->item_type == ITEM_DARKNESS
    && obj->value[2] != 0 && in_room != NULL)
    --in_room->light;

  return;
}

/*
 * Move a char out of a room.
 */
void Character::char_from_room ()
{
  Object *obj;

  if (in_room == NULL) {
    bug_printf ("Char_from_room: NULL.");
    return;
  }

  if (!is_npc ())
    --in_room->area->nplayer;

  if ((obj = get_eq_char (WEAR_LIGHT)) != NULL
    && obj->item_type == ITEM_LIGHT
    && obj->value[2] != 0 && in_room->light > 0)
    --in_room->light;

  if ((obj = get_eq_char (WEAR_LIGHT)) != NULL
    && obj->item_type == ITEM_DARKNESS
    && obj->value[2] != 0 && in_room->light < 1)
    ++in_room->light;

  deeprmnext = in_room->people.erase(
    find(in_room->people.begin(), in_room->people.end(), this));
  in_room = NULL;
  return;
}

/*
 * Move a char into a room.
 */
void Character::char_to_room (Room * pRoomIndex)
{
  Object *obj;

  if (pRoomIndex == NULL) {
    bug_printf ("Char_to_room: NULL.");
    return;
  }

  in_room = pRoomIndex;
  pRoomIndex->people.push_back(this);

  if (!is_npc ())
    ++in_room->area->nplayer;

  if ((obj = get_eq_char (WEAR_LIGHT)) != NULL
    && obj->item_type == ITEM_LIGHT && obj->value[2] != 0)
    ++in_room->light;

  if ((obj = get_eq_char (WEAR_LIGHT)) != NULL
    && obj->item_type == ITEM_DARKNESS && obj->value[2] != 0)
    --in_room->light;

  return;
}

void Character::set_title (const std::string & title)
{
  if (is_npc ()) {
    bug_printf ("Set_title: NPC.");
    return;
  }

  if (isalpha (title[0]) || isdigit (title[0])) {
    pcdata->title = " " + title;
  } else {
    pcdata->title = title;
  }

  return;
}

bool Character::is_switched ()
{
  if (!is_npc () || desc == NULL)
    return false;
  return true;
}

bool Character::mp_commands ()
{                               /* Can MOBProged mobs
                                   use mpcommands? true if yes.
                                   - Kahn */
  if (is_switched())
    return false;

  if (is_npc ()
    && pIndexData->progtypes && !is_affected (AFF_CHARM))
    return true;

  return false;

}

/*
 * Advancement stuff.
 */
void Character::advance_level ()
{
  char buf[MAX_STRING_LENGTH];
  int add_hp, add_mana, add_move, add_prac;

  snprintf (buf, sizeof buf, "the %s", get_title(klass, level, sex).c_str());
  set_title(buf);

  add_hp = con_app[get_curr_con()].hitp +
    number_range (class_table[klass].hp_min, class_table[klass].hp_max);
  add_mana = class_table[klass].fMana ? number_range (2,
    (2 * get_curr_int() + get_curr_wis()) / 8)
    : 0;
  add_move = number_range (5, (get_curr_con() + get_curr_dex()) / 4);
  add_prac = wis_app[get_curr_wis()].practice;

  add_hp = std::max (1, add_hp);
  add_mana = std::max (0, add_mana);
  add_move = std::max (10, add_move);

  max_hit += add_hp;
  max_mana += add_mana;
  max_move += add_move;
  practice += add_prac;

  if (!is_npc ())
    REMOVE_BIT (actflags, PLR_BOUGHT_PET);

  snprintf (buf, sizeof buf,
    "Your gain is: %d/%d hp, %d/%d m, %d/%d mv %d/%d prac.\r\n",
    add_hp, max_hit, add_mana, max_mana, add_move, max_move, add_prac,
    practice);
  send_to_char (buf);
  return;
}

void Character::gain_exp(int gain)
{
  if (is_npc () || level >= LEVEL_HERO)
    return;

  exp = std::max (1000, exp + gain);
  while (level < LEVEL_HERO && exp >= 1000 * (level + 1)) {
    send_to_char ("You raise a level!!  ");
    level += 1;
    advance_level();
  }

  return;
}

/*
 * Regeneration stuff.
 */
int Character::hit_gain ()
{
  int gain;

  if (is_npc ()) {
    gain = level * 3 / 2;
  } else {
    gain = std::min (5, level);

    switch (position) {
    case POS_SLEEPING:
      gain += get_curr_con();
      break;
    case POS_RESTING:
      gain += get_curr_con() / 2;
      break;
    }

    if (pcdata->condition[COND_FULL] == 0)
      gain /= 2;

    if (pcdata->condition[COND_THIRST] == 0)
      gain /= 2;

  }

  if (is_affected (AFF_POISON))
    gain /= 4;

  return std::min (gain, max_hit - hit);
}

int Character::mana_gain ()
{
  int gain;

  if (is_npc ()) {
    gain = level;
  } else {
    gain = std::min (5, level / 2);

    switch (position) {
    case POS_SLEEPING:
      gain += get_curr_int() * 2;
      break;
    case POS_RESTING:
      gain += get_curr_int();
      break;
    }

    if (pcdata->condition[COND_FULL] == 0)
      gain /= 2;

    if (pcdata->condition[COND_THIRST] == 0)
      gain /= 2;

  }

  if (is_affected (AFF_POISON))
    gain /= 4;

  return std::min (gain, max_mana - mana);
}

int Character::move_gain ()
{
  int gain;

  if (is_npc ()) {
    gain = level;
  } else {
    gain = std::max (15, 2 * level);

    switch (position) {
    case POS_SLEEPING:
      gain += get_curr_dex();
      break;
    case POS_RESTING:
      gain += get_curr_dex() / 2;
      break;
    }

    if (pcdata->condition[COND_FULL] == 0)
      gain /= 2;

    if (pcdata->condition[COND_THIRST] == 0)
      gain /= 2;
  }

  if (is_affected (AFF_POISON))
    gain /= 4;

  return std::min (gain, max_move - move);
}

void Character::gain_condition (int iCond, int value)
{
  if (value == 0 || is_npc () || level >= LEVEL_HERO)
    return;

  int condition = pcdata->condition[iCond];
  pcdata->condition[iCond] = URANGE (0, condition + value, 48);

  if (pcdata->condition[iCond] == 0) {
    switch (iCond) {
    case COND_FULL:
      send_to_char ("You are hungry.\r\n");
      break;

    case COND_THIRST:
      send_to_char ("You are thirsty.\r\n");
      break;

    case COND_DRUNK:
      if (condition != 0)
        send_to_char ("You are sober.\r\n");
      break;
    }
  }

  return;
}

void Character::add_follower (Character * m)
{

  if (master != NULL) {
    bug_printf ("Add_follower: non-null master.");
    return;
  }

  master = m;
  leader = NULL;

  if (m->can_see(this))
    act ("$n now follows you.", NULL, m, TO_VICT);

  act ("You now follow $N.", NULL, m, TO_CHAR);

  return;
}

void Character::stop_follower()
{

  if (master == NULL) {
    bug_printf ("Stop_follower: null master.");
    return;
  }

  if (is_affected (AFF_CHARM)) {
    REMOVE_BIT (affected_by, AFF_CHARM);
    affect_strip (skill_lookup("charm person"));
  }

  if (master->can_see(this))
    act ("$n stops following you.", NULL, master, TO_VICT);
  act ("You stop following $N.", NULL, master, TO_CHAR);

  master = NULL;
  leader = NULL;
  return;
}

void Character::die_follower ()
{
  if (master != NULL)
    stop_follower();

  leader = NULL;

  for (CharIter c = char_list.begin(); c != char_list.end(); c++) {
    if ((*c)->master == this)
      (*c)->stop_follower();
    if ((*c)->leader == this)
      (*c)->leader = *c;
  }

  return;
}

/*
 * Set position of a victim.
 */
void Character::update_pos ()
{
  if (hit > 0) {
    if (position <= POS_STUNNED)
      position = POS_STANDING;
    return;
  }

  if (is_npc () || hit <= -11) {
    position = POS_DEAD;
    return;
  }

  if (hit <= -6)
    position = POS_MORTAL;
  else if (hit <= -3)
    position = POS_INCAP;
  else
    position = POS_STUNNED;

  return;
}

/*
 * Start fights.
 */
void Character::set_fighting (Character * victim)
{
  if (fighting != NULL) {
    bug_printf ("Set_fighting: already fighting");
    return;
  }

  if (is_affected (AFF_SLEEP))
    affect_strip (skill_lookup("sleep"));

  fighting = victim;
  position = POS_FIGHTING;

  return;
}

/*
 * Stop fights.
 */
void Character::stop_fighting (bool fBoth)
{
  CharIter c;
  for (c = char_list.begin(); c != char_list.end(); c++) {
    if (*c == this || (fBoth && (*c)->fighting == this)) {
      (*c)->fighting = NULL;
      (*c)->position = POS_STANDING;
      (*c)->update_pos();
    }
  }

  return;
}

bool Character::check_blind ()
{
  if (!is_npc () && IS_SET (actflags, PLR_HOLYLIGHT))
    return true;

  if (is_affected (AFF_BLIND)) {
    send_to_char ("You can't see a thing!\r\n");
    return false;
  }

  return true;
}

int Character::find_door (const std::string & arg)
{
  Exit *pexit;
  int door;

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

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

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

  return door;
}

bool Character::has_key (int key)
{
  ObjIter o;
  for (o = carrying.begin(); o != carrying.end(); o++) {
    if ((*o)->pIndexData->vnum == key)
      return true;
  }

  return false;
}

void Character::get_obj (Object * obj, Object * container)
{
  if (!obj->can_wear(ITEM_TAKE)) {
    send_to_char ("You can't take that.\r\n");
    return;
  }

  if (carry_number + obj->get_obj_number() > can_carry_n()) {
    act ("$d: you can't carry that many items.",
      NULL, obj->name.c_str(), TO_CHAR);
    return;
  }

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

  if (container != NULL) {
    act ("You get $p from $P.", obj, container, TO_CHAR);
    act ("$n gets $p from $P.", obj, container, TO_ROOM);
    obj->obj_from_obj ();
  } else {
    act ("You get $p.", obj, container, TO_CHAR);
    act ("$n gets $p.", obj, container, TO_ROOM);
    obj->obj_from_room ();
  }

  if (obj->item_type == ITEM_MONEY) {
    gold += obj->value[0];
    obj->extract_obj ();
  } else {
    obj->obj_to_char (this);
  }

  return;
}

/*
 * Flag a character as extractable
 */
void Character::extract_char (bool fPull)
{
  if (is_npc())
    SET_BIT(actflags, ACT_EXTRACT);
  else
    SET_BIT(actflags, PLR_EXTRACT);

  extract_chars = true;
}

/*
 * Extract a char from the world.
 */
void Character::extract_char_old (bool fPull)
{
  Object *obj;

  if (in_room == NULL) {
    bug_printf ("Extract_char: NULL.");
    return;
  }

  if (fPull)
    die_follower();

  stop_fighting (true);

  ObjIter o, onext;
  for (o = carrying.begin(); o != carrying.end(); o = onext) {
    obj = *o;
    onext = ++o;
    obj->extract_obj();
  }

  char_from_room ();

  if (!fPull) {
    char_to_room(get_room_index (ROOM_VNUM_ALTAR));
    return;
  }

  if (is_npc ())
    --pIndexData->count;

  if (desc != NULL && desc->original != NULL)
    do_return ("");

  CharIter c;
  for (c = char_list.begin(); c != char_list.end(); c++) {
    if ((*c)->reply == this)
      (*c)->reply = NULL;
  }

  deepchnext = char_list.erase(find(char_list.begin(), char_list.end(), this));

  if (desc)
    desc->character = NULL;
  delete this;
  return;
}

void Character::stop_idling ()
{
  if (desc == NULL || desc->connected != CON_PLAYING
    || was_in_room == NULL || in_room != get_room_index (ROOM_VNUM_LIMBO))
    return;

  timer = 0;
  char_from_room();
  char_to_room(was_in_room);
  was_in_room = NULL;
  act ("$n has returned from the void.", NULL, NULL, TO_ROOM);
  return;
}

/*
 * Remove an object.
 */
bool Character::remove_obj (int iWear, bool fReplace)
{
  Object *obj;

  if ((obj = get_eq_char (iWear)) == NULL)
    return true;

  if (!fReplace)
    return false;

  if (IS_SET (obj->extra_flags, ITEM_NOREMOVE)) {
    act ("You can't remove $p.", obj, NULL, TO_CHAR);
    return false;
  }

  unequip_char(obj);
  act ("$n stops using $p.", obj, NULL, TO_ROOM);
  act ("You stop using $p.", obj, NULL, TO_CHAR);
  return true;
}

/*
 * Wear one object.
 * Optional replacement of existing objects.
 * Big repetitive code, ick.
 */
void Character::wear_obj (Object * obj, bool fReplace)
{
  char buf[MAX_STRING_LENGTH];

  if (level < obj->level) {
    snprintf (buf, sizeof buf, "You must be level %d to use this object.\r\n", obj->level);
    send_to_char (buf);
    act ("$n tries to use $p, but is too inexperienced.",
      obj, NULL, TO_ROOM);
    return;
  }

  if (obj->item_type == ITEM_LIGHT) {
    if (!remove_obj (WEAR_LIGHT, fReplace))
      return;
    act ("$n lights $p and holds it.", obj, NULL, TO_ROOM);
    act ("You light $p and hold it.", obj, NULL, TO_CHAR);
    equip_char (obj, WEAR_LIGHT);
    return;
  }

  if (obj->item_type == ITEM_DARKNESS) {
    if (!remove_obj (WEAR_LIGHT, fReplace))
      return;
    act ("$n darkens $p and holds it.", obj, NULL, TO_ROOM);
    act ("You darken $p and hold it.", obj, NULL, TO_CHAR);
    equip_char (obj, WEAR_LIGHT);
    return;
  }

  if (obj->can_wear(ITEM_WEAR_FINGER)) {
    if (get_eq_char (WEAR_FINGER_L) != NULL
      && get_eq_char (WEAR_FINGER_R) != NULL
      && !remove_obj (WEAR_FINGER_L, fReplace)
      && !remove_obj (WEAR_FINGER_R, fReplace))
      return;

    if (get_eq_char (WEAR_FINGER_L) == NULL) {
      act ("$n wears $p on $s left finger.", obj, NULL, TO_ROOM);
      act ("You wear $p on your left finger.", obj, NULL, TO_CHAR);
      equip_char (obj, WEAR_FINGER_L);
      return;
    }

    if (get_eq_char (WEAR_FINGER_R) == NULL) {
      act ("$n wears $p on $s right finger.", obj, NULL, TO_ROOM);
      act ("You wear $p on your right finger.", obj, NULL, TO_CHAR);
      equip_char (obj, WEAR_FINGER_R);
      return;
    }

    bug_printf ("Wear_obj: no free finger.");
    send_to_char ("You already wear two rings.\r\n");
    return;
  }

  if (obj->can_wear(ITEM_WEAR_NECK)) {
    if (get_eq_char (WEAR_NECK_1) != NULL
      && get_eq_char (WEAR_NECK_2) != NULL
      && !remove_obj (WEAR_NECK_1, fReplace)
      && !remove_obj (WEAR_NECK_2, fReplace))
      return;

    if (get_eq_char (WEAR_NECK_1) == NULL) {
      act ("$n wears $p around $s neck.", obj, NULL, TO_ROOM);
      act ("You wear $p around your neck.", obj, NULL, TO_CHAR);
      equip_char (obj, WEAR_NECK_1);
      return;
    }

    if (get_eq_char (WEAR_NECK_2) == NULL) {
      act ("$n wears $p around $s neck.", obj, NULL, TO_ROOM);
      act ("You wear $p around your neck.", obj, NULL, TO_CHAR);
      equip_char (obj, WEAR_NECK_2);
      return;
    }

    bug_printf ("Wear_obj: no free neck.");
    send_to_char ("You already wear two neck items.\r\n");
    return;
  }

  if (obj->can_wear(ITEM_WEAR_BODY)) {
    if (!remove_obj (WEAR_BODY, fReplace))
      return;
    act ("$n wears $p on $s body.", obj, NULL, TO_ROOM);
    act ("You wear $p on your body.", obj, NULL, TO_CHAR);
    equip_char (obj, WEAR_BODY);
    return;
  }

  if (obj->can_wear(ITEM_WEAR_HEAD)) {
    if (!remove_obj (WEAR_HEAD, fReplace))
      return;
    act ("$n wears $p on $s head.", obj, NULL, TO_ROOM);
    act ("You wear $p on your head.", obj, NULL, TO_CHAR);
    equip_char (obj, WEAR_HEAD);
    return;
  }

  if (obj->can_wear(ITEM_WEAR_LEGS)) {
    if (!remove_obj (WEAR_LEGS, fReplace))
      return;
    act ("$n wears $p on $s legs.", obj, NULL, TO_ROOM);
    act ("You wear $p on your legs.", obj, NULL, TO_CHAR);
    equip_char (obj, WEAR_LEGS);
    return;
  }

  if (obj->can_wear(ITEM_WEAR_FEET)) {
    if (!remove_obj (WEAR_FEET, fReplace))
      return;
    act ("$n wears $p on $s feet.", obj, NULL, TO_ROOM);
    act ("You wear $p on your feet.", obj, NULL, TO_CHAR);
    equip_char (obj, WEAR_FEET);
    return;
  }

  if (obj->can_wear(ITEM_WEAR_HANDS)) {
    if (!remove_obj (WEAR_HANDS, fReplace))
      return;
    act ("$n wears $p on $s hands.", obj, NULL, TO_ROOM);
    act ("You wear $p on your hands.", obj, NULL, TO_CHAR);
    equip_char (obj, WEAR_HANDS);
    return;
  }

  if (obj->can_wear(ITEM_WEAR_ARMS)) {
    if (!remove_obj (WEAR_ARMS, fReplace))
      return;
    act ("$n wears $p on $s arms.", obj, NULL, TO_ROOM);
    act ("You wear $p on your arms.", obj, NULL, TO_CHAR);
    equip_char (obj, WEAR_ARMS);
    return;
  }

  if (obj->can_wear(ITEM_WEAR_ABOUT)) {
    if (!remove_obj (WEAR_ABOUT, fReplace))
      return;
    act ("$n wears $p about $s body.", obj, NULL, TO_ROOM);
    act ("You wear $p about your body.", obj, NULL, TO_CHAR);
    equip_char (obj, WEAR_ABOUT);
    return;
  }

  if (obj->can_wear(ITEM_WEAR_WAIST)) {
    if (!remove_obj (WEAR_WAIST, fReplace))
      return;
    act ("$n wears $p about $s waist.", obj, NULL, TO_ROOM);
    act ("You wear $p about your waist.", obj, NULL, TO_CHAR);
    equip_char (obj, WEAR_WAIST);
    return;
  }

  if (obj->can_wear(ITEM_WEAR_WRIST)) {
    if (get_eq_char (WEAR_WRIST_L) != NULL
      && get_eq_char (WEAR_WRIST_R) != NULL
      && !remove_obj (WEAR_WRIST_L, fReplace)
      && !remove_obj (WEAR_WRIST_R, fReplace))
      return;

    if (get_eq_char (WEAR_WRIST_L) == NULL) {
      act ("$n wears $p around $s left wrist.", obj, NULL, TO_ROOM);
      act ("You wear $p around your left wrist.", obj, NULL, TO_CHAR);
      equip_char (obj, WEAR_WRIST_L);
      return;
    }

    if (get_eq_char (WEAR_WRIST_R) == NULL) {
      act ("$n wears $p around $s right wrist.", obj, NULL, TO_ROOM);
      act ("You wear $p around your right wrist.", obj, NULL, TO_CHAR);
      equip_char (obj, WEAR_WRIST_R);
      return;
    }

    bug_printf ("Wear_obj: no free wrist.");
    send_to_char ("You already wear two wrist items.\r\n");
    return;
  }

  if (obj->can_wear( ITEM_WEAR_SHIELD)) {
    if (!remove_obj (WEAR_SHIELD, fReplace))
      return;
    act ("$n wears $p as a shield.", obj, NULL, TO_ROOM);
    act ("You wear $p as a shield.", obj, NULL, TO_CHAR);
    equip_char (obj, WEAR_SHIELD);
    return;
  }

  if (obj->can_wear(ITEM_WIELD)) {
    if (!remove_obj (WEAR_WIELD, fReplace))
      return;

    if (obj->get_obj_weight() > str_app[get_curr_str()].wield) {
      send_to_char ("It is too heavy for you to wield.\r\n");
      return;
    }

    act ("$n wields $p.", obj, NULL, TO_ROOM);
    act ("You wield $p.", obj, NULL, TO_CHAR);
    equip_char (obj, WEAR_WIELD);
    return;
  }

  if (obj->can_wear(ITEM_HOLD)) {
    if (!remove_obj (WEAR_HOLD, fReplace))
      return;
    act ("$n holds $p in $s hands.", obj, NULL, TO_ROOM);
    act ("You hold $p in your hands.", obj, NULL, TO_CHAR);
    equip_char (obj, WEAR_HOLD);
    return;
  }

  if (fReplace)
    send_to_char ("You can't wear, wield, or hold that.\r\n");

  return;
}

/*
 * Show a list to a character.
 * Can coalesce duplicated items.
 */
void Character::show_list_to_char (std::list<Object *> & list, bool fShort,
  bool fShowNothing)
{
  char buf[MAX_STRING_LENGTH];
  int nShow;
  int iShow;
  bool fCombine;

  if (desc == NULL)
    return;

  /*
   * Alloc space for output lines.
   */
  std::string * prgpstrShow = new std::string[list.size()];
  int * prgnShow = new int[list.size()];
  nShow = 0;

  /*
   * Format the list of objects.
   */
  ObjIter obj;
  for (obj = list.begin(); obj != list.end(); obj++) {
    if ((*obj)->wear_loc == WEAR_NONE && can_see_obj(*obj)) {
      std::string pstrShow = (*obj)->format_obj_to_char (this, fShort);
      fCombine = false;

      if (is_npc () || IS_SET (actflags, PLR_COMBINE)) {
        /*
         * Look for duplicates, case sensitive.
         * Matches tend to be near end so run loop backwords.
         */
        for (iShow = nShow - 1; iShow >= 0; iShow--) {
          if (!strcmp (prgpstrShow[iShow].c_str(), pstrShow.c_str())) {
            prgnShow[iShow]++;
            fCombine = true;
            break;
          }
        }
      }

      /*
       * Couldn't combine, or didn't want to.
       */
      if (!fCombine) {
        prgpstrShow[nShow] = pstrShow;
        prgnShow[nShow] = 1;
        nShow++;
      }
    }
  }

  /*
   * Output the formatted list.
   */
  for (iShow = 0; iShow < nShow; iShow++) {
    if (is_npc () || IS_SET (actflags, PLR_COMBINE)) {
      if (prgnShow[iShow] != 1) {
        snprintf (buf, sizeof buf, "(%2d) ", prgnShow[iShow]);
        send_to_char (buf);
      } else {
        send_to_char ("     ");
      }
    }
    send_to_char (prgpstrShow[iShow]);
    send_to_char ("\r\n");
  }

  if (fShowNothing && nShow == 0) {
    if (is_npc () || IS_SET (actflags, PLR_COMBINE))
      send_to_char ("     ");
    send_to_char ("Nothing.\r\n");
  }

  /*
   * Clean up.
   */
  delete [] prgnShow;
  delete [] prgpstrShow;

  return;
}

void Character::show_char_to_char_0 (Character * victim)
{
  std::string buf;

  if (victim->is_affected (AFF_INVISIBLE))
    buf.append("(Invis) ");
  if (victim->is_affected (AFF_HIDE))
    buf.append("(Hide) ");
  if (victim->is_affected (AFF_CHARM))
    buf.append("(Charmed) ");
  if (victim->is_affected (AFF_PASS_DOOR))
    buf.append("(Translucent) ");
  if (victim->is_affected (AFF_FAERIE_FIRE))
    buf.append("(Pink Aura) ");
  if (victim->is_evil () && is_affected (AFF_DETECT_EVIL))
    buf.append("(Red Aura) ");
  if (victim->is_affected (AFF_SANCTUARY))
    buf.append("(White Aura) ");
  if (!victim->is_npc () && IS_SET (victim->actflags, PLR_KILLER))
    buf.append("(KILLER) ");
  if (!victim->is_npc () && IS_SET (victim->actflags, PLR_THIEF))
    buf.append("(THIEF) ");

  if (victim->position == POS_STANDING && !victim->long_descr.empty()) {
    buf.append(victim->long_descr);
    send_to_char (buf);
    return;
  }

  buf.append(victim->describe_to(this));
  if (!victim->is_npc () && !IS_SET (actflags, PLR_BRIEF))
    buf.append(victim->pcdata->title);

  switch (victim->position) {
  case POS_DEAD:
    buf.append(" is DEAD!!");
    break;
  case POS_MORTAL:
    buf.append(" is mortally wounded.");
    break;
  case POS_INCAP:
    buf.append(" is incapacitated.");
    break;
  case POS_STUNNED:
    buf.append(" is lying here stunned.");
    break;
  case POS_SLEEPING:
    buf.append(" is sleeping here.");
    break;
  case POS_RESTING:
    buf.append(" is resting here.");
    break;
  case POS_STANDING:
    buf.append(" is here.");
    break;
  case POS_FIGHTING:
    buf.append(" is here, fighting ");
    if (victim->fighting == NULL)
      buf.append("thin air??");
    else if (victim->fighting == this)
      buf.append("YOU!");
    else if (victim->in_room == victim->fighting->in_room) {
      buf.append(victim->fighting->describe_to(this));
      buf.append(".");
    } else
      buf.append("somone who left??");
    break;
  }

  buf.append("\r\n");
  buf[0] = toupper(buf[0]);
  send_to_char (buf);
  return;
}

void Character::show_char_to_char_1 (Character * victim)
{
  std::string buf;
  Object *obj;
  int iWear;
  int percent;
  bool found;

  if (victim->can_see(this)) {
    act ("$n looks at you.", NULL, victim, TO_VICT);
    act ("$n looks at $N.", NULL, victim, TO_NOTVICT);
  }

  if (victim->description[0] != '\0') {
    send_to_char (victim->description);
  } else {
    act ("You see nothing special about $M.", NULL, victim, TO_CHAR);
  }

  if (victim->max_hit > 0)
    percent = (100 * victim->hit) / victim->max_hit;
  else
    percent = -1;

  buf = victim->describe_to(this);

  if (percent >= 100)
    buf.append(" is in perfect health.\r\n");
  else if (percent >= 90)
    buf.append(" is slightly scratched.\r\n");
  else if (percent >= 80)
    buf.append(" has a few bruises.\r\n");
  else if (percent >= 70)
    buf.append(" has some cuts.\r\n");
  else if (percent >= 60)
    buf.append(" has several wounds.\r\n");
  else if (percent >= 50)
    buf.append(" has many nasty wounds.\r\n");
  else if (percent >= 40)
    buf.append(" is bleeding freely.\r\n");
  else if (percent >= 30)
    buf.append(" is covered in blood.\r\n");
  else if (percent >= 20)
    buf.append(" is leaking guts.\r\n");
  else if (percent >= 10)
    buf.append(" is almost dead.\r\n");
  else
    buf.append(" is DYING.\r\n");

  buf[0] = toupper (buf[0]);
  send_to_char (buf);

  found = false;
  for (iWear = 0; iWear < MAX_WEAR; iWear++) {
    if ((obj = victim->get_eq_char (iWear)) != NULL && can_see_obj(obj)) {
      if (!found) {
        send_to_char ("\r\n");
        act ("$N is using:", NULL, victim, TO_CHAR);
        found = true;
      }
      send_to_char (where_name[iWear]);
      send_to_char (obj->format_obj_to_char (this, true));
      send_to_char ("\r\n");
    }
  }

  if (victim != this && !is_npc ()
    && number_percent () < pcdata->learned[skill_lookup("peek")]) {
    send_to_char ("\r\nYou peek at the inventory:\r\n");
    show_list_to_char (victim->carrying, true, true);
  }

  return;
}

void Character::show_char_to_char (std::list<Character *> & list)
{
  CharIter rch;

  for (rch = list.begin(); rch != list.end(); rch++) {
    if (*rch == this)
      continue;

    if (can_see(*rch)) {
      show_char_to_char_0 (*rch);
    } else if (in_room->is_dark()
      && (*rch)->is_affected (AFF_INFRARED)) {
      send_to_char ("You see glowing red eyes watching YOU!\r\n");
    }
  }

  return;
}

void Character::move_char (int door)
{
  Character *fch;
  Room *in_rm;
  Room *to_room;
  Exit *pexit;

  if (door < 0 || door > 5) {
    bug_printf ("Do_move: bad door %d.", door);
    return;
  }

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

  if (IS_SET (pexit->exit_info, EX_CLOSED)
    && !is_affected (AFF_PASS_DOOR)) {
    act ("The $d is closed.", NULL, pexit->name.c_str(), TO_CHAR);
    return;
  }

  if (is_affected (AFF_CHARM)
    && master != NULL && in_rm == master->in_room) {
    send_to_char ("What?  And leave your beloved master?\r\n");
    return;
  }

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

  if (!is_npc ()) {
    int iClass;
    int mv;

    for (iClass = 0; iClass < CLASS_MAX; iClass++) {
      if (iClass != klass && to_room->vnum == class_table[iClass].guild) {
        send_to_char ("You aren't allowed in there.\r\n");
        return;
      }
    }

    if (in_rm->sector_type == SECT_AIR || to_room->sector_type == SECT_AIR) {
      if (!is_affected (AFF_FLYING)) {
        send_to_char ("You can't fly.\r\n");
        return;
      }
    }

    if (in_rm->sector_type == SECT_WATER_NOSWIM
      || to_room->sector_type == SECT_WATER_NOSWIM) {
      /*
       * Look for a boat.
       */
      bool found = false;

      /*
       * Suggestion for flying above water by Sludge
       */
      if (is_affected (AFF_FLYING))
        found = true;

      for (ObjIter o = carrying.begin(); o != carrying.end(); o++) {
        if ((*o)->item_type == ITEM_BOAT) {
          found = true;
          break;
        }
      }
      if (!found) {
        send_to_char ("You need a boat to go there.\r\n");
        return;
      }
    }

    mv = movement_loss[std::min ((int)SECT_MAX - 1, in_rm->sector_type)]
      + movement_loss[std::min ((int)SECT_MAX - 1, to_room->sector_type)];

    if (move < mv) {
      send_to_char ("You are too exhausted.\r\n");
      return;
    }

    wait_state (1);
    move -= mv;
  }

  if (!is_affected (AFF_SNEAK))
    act ("$n leaves $T.", NULL, dir_name[door].c_str(), TO_ROOM);

  char_from_room();
  char_to_room(to_room);
  if (!is_affected (AFF_SNEAK))
    act ("$n has arrived.", NULL, NULL, TO_ROOM);

  do_look ("auto");

  CharIter rch, next;
  for (rch = in_rm->people.begin(); rch != in_rm->people.end(); rch = next) {
    fch = *rch;
    next = ++rch;
    if (fch->master == this && fch->position == POS_STANDING) {
      fch->act ("You follow $N.", NULL, this, TO_CHAR);
      fch->move_char (door);
    }
  }

  if (this)
    mprog_entry_trigger (this);
  if (this)
    mprog_greet_trigger (this);
  return;
}

bool Character::check_social (const std::string & command, const std::string & argument)
{
  std::string arg;
  Character *victim;
  char *sql = sqlite3_mprintf(
    "SELECT name, char_no_arg, others_no_arg, char_found, others_found, vict_found, char_auto, others_auto FROM socials WHERE NAME LIKE '%q%%'",
    command.c_str());
  sqlite3_stmt *stmt = NULL;

  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 false;
  }

  if (sqlite3_step(stmt) != SQLITE_ROW) {
    sqlite3_finalize(stmt);
    sqlite3_free(sql);
    return false;
  }

  if (!is_npc () && IS_SET (actflags, PLR_NO_EMOTE)) {
    send_to_char ("You are anti-social!\r\n");
    sqlite3_finalize(stmt);
    sqlite3_free(sql);
    return true;
  }

  switch (position) {
  case POS_DEAD:
    send_to_char ("Lie still; you are DEAD.\r\n");
    sqlite3_finalize(stmt);
    sqlite3_free(sql);
    return true;

  case POS_INCAP:
  case POS_MORTAL:
    send_to_char ("You are hurt far too bad for that.\r\n");
    sqlite3_finalize(stmt);
    sqlite3_free(sql);
    return true;

  case POS_STUNNED:
    send_to_char ("You are too stunned to do that.\r\n");
    sqlite3_finalize(stmt);
    sqlite3_free(sql);
    return true;

  case POS_SLEEPING:
    /*
     * I just know this is the path to a 12" 'if' statement.  :(
     * But two players asked for it already!  -- Furey
     */
    if (!str_cmp ((const char*)sqlite3_column_text( stmt, 0 ), "snore"))
      break;
    send_to_char ("In your dreams, or what?\r\n");
    sqlite3_finalize(stmt);
    sqlite3_free(sql);
    return true;

  }

  one_argument (argument, arg);
  victim = NULL;
  if (arg.empty()) {
    act ((const char*)sqlite3_column_text( stmt, 2 ), NULL, victim, TO_ROOM);
    act ((const char*)sqlite3_column_text( stmt, 1 ), NULL, victim, TO_CHAR);
  } else if ((victim = get_char_room (arg)) == NULL) {
    send_to_char ("They aren't here.\r\n");
  } else if (victim == this) {
    act ((const char*)sqlite3_column_text( stmt, 7 ), NULL, victim, TO_ROOM);
    act ((const char*)sqlite3_column_text( stmt, 6 ), NULL, victim, TO_CHAR);
  } else {
    act ((const char*)sqlite3_column_text( stmt, 4 ), NULL, victim, TO_NOTVICT);
    act ((const char*)sqlite3_column_text( stmt, 3 ), NULL, victim, TO_CHAR);
    act ((const char*)sqlite3_column_text( stmt, 5 ), NULL, victim, TO_VICT);

    if (!is_npc () && victim->is_npc ()
      && !victim->is_affected (AFF_CHARM)
      && victim->is_awake ()) {
      switch (number_range (0, 15)) {
      case 0:
        multi_hit (victim, this, TYPE_UNDEFINED);
        break;

      case 1:
      case 2:
      case 3:
      case 4:
      case 5:
      case 6:
      case 7:
      case 8:
        victim->act ((const char*)sqlite3_column_text( stmt, 4 ), NULL, this, TO_NOTVICT);
        victim->act ((const char*)sqlite3_column_text( stmt, 3 ), NULL, this, TO_CHAR);
        victim->act ((const char*)sqlite3_column_text( stmt, 5 ), NULL, this, TO_VICT);
        break;

      case 9:
      case 10:
      case 11:
      case 12:
        victim->act ("$n slaps $N.", NULL, this, TO_NOTVICT);
        victim->act ("You slap $N.", NULL, this, TO_CHAR);
        victim->act ("$n slaps you.", NULL, this, TO_VICT);
        break;
      }
    }
  }

  sqlite3_finalize(stmt);
  sqlite3_free(sql);
  return true;
}

/*
 * The main entry point for executing commands.
 * Can be recursively called from 'at', 'order', 'force'.
 */
void Character::interpret (std::string argument)
{
  std::string command;
  int cmd;
  bool found;

  if (desc != NULL)
    desc->incomm.erase();
  /*
   * Strip leading spaces.
   */
  argument.erase(0, argument.find_first_not_of(" "));
  if (argument.empty())
    return;

  /*
   * No hiding.
   */
  REMOVE_BIT (affected_by, AFF_HIDE);

  /*
   * Implement freeze command.
   */
  if (!is_npc () && IS_SET (actflags, PLR_FREEZE)) {
    send_to_char ("You're totally frozen!\r\n");
    return;
  }

  /*
   * Grab the command word.
   * Special parsing so ' can be a command,
   *   also no spaces needed after punctuation.
   */
  if (!isalpha (argument[0]) && !isdigit (argument[0])) {
    command.assign(argument, 0, 1);
    argument.erase(0, 1);
    argument.erase(0, argument.find_first_not_of(" "));
  } else {
    argument = one_argument(argument, command);
  }

  /*
   * Look for command in command table.
   */
  found = false;
  int trst = get_trust ();
  for (cmd = 0; cmd_table[cmd].name[0] != '\0'; cmd++) {
    if (command[0] == cmd_table[cmd].name[0]
      && !str_prefix (command, cmd_table[cmd].name)
      && (cmd_table[cmd].level <= trst || mp_commands())) {
      found = true;
      break;
    }
  }

  if (!found) {
    /*
     * Look for command in socials table.
     */
    if (!check_social (command, argument))
      send_to_char ("Huh?\r\n");
    return;
  }

  /*
   * Character not in position for command?
   */
  if (position < cmd_table[cmd].position) {
    switch (position) {
    case POS_DEAD:
      send_to_char ("Lie still; you are DEAD.\r\n");
      break;

    case POS_MORTAL:
    case POS_INCAP:
      send_to_char ("You are hurt far too bad for that.\r\n");
      break;

    case POS_STUNNED:
      send_to_char ("You are too stunned to do that.\r\n");
      break;

    case POS_SLEEPING:
      send_to_char ("In your dreams, or what?\r\n");
      break;

    case POS_RESTING:
      send_to_char ("Nah... You feel too relaxed...\r\n");
      break;

    case POS_FIGHTING:
      send_to_char ("No way!  You are still fighting!\r\n");
      break;

    }
    return;
  }

  /*
   * Dispatch the command.
   */
  (this->*(cmd_table[cmd].do_fun)) (argument);

  return;
}

bool Character::is_gagged(std::string & nm) {
  if (is_npc ())
    return false;

  std::string gagname = capitalize(nm);
  std::list<std::string>::iterator fnd;
  fnd = find(pcdata->gag_list.begin(), pcdata->gag_list.end(), gagname);
  if (fnd != pcdata->gag_list.end())
    return true;
  return false;
}