musicmud-2.1.6/data/
musicmud-2.1.6/data/help/
musicmud-2.1.6/data/policy/
musicmud-2.1.6/data/wild/
musicmud-2.1.6/data/world/
musicmud-2.1.6/doc/
musicmud-2.1.6/src/ident/
musicmud-2.1.6/src/lua/
musicmud-2.1.6/src/lua/include/
musicmud-2.1.6/src/lua/src/lib/
musicmud-2.1.6/src/lua/src/lua/
musicmud-2.1.6/src/lua/src/luac/
/* 
 * MusicMUD - Communications module
 * Copyright (C) 1998-2003 Abigail Brady
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * 
 */

#include <algorithm>
#include <string.h>
#include <ctype.h>

#include "musicmud.h"
#include "misc.h"
#include "verbs.h"
#include "State.h"
#include "Player.h"
#include "levels.h"
#include "msi.h"
#include "aberchat.h"
#include "util.h"
#include "pflags.h"
#include "Mission.h"
#include "Socket.h"

#include "events.h"
#include "trap.h"
#include "paths.h"
#include "shared.h"
#include "move.h"
#include "format.h"
#include "zoneload.h"
#include "hooks.h"
#include "emsg.h"
#include "nations.h"

#define MODULE "comms"

static void eq_write_to_file(FILE *f, MudObject *who) {
  int quitting = who->get_int("!quitting", 0);

  NewWorld stuff;
  add_storeable_eq(who, stuff);

  MudObject *o;
  int i;
  MudObject *w = who->get_object(KEY_WIELD);
  MudObject *m = mount(who);

  foreach(&stuff, o, i) {
    o->set("!id", i);
    if (w==o) {
      o->set("$wielder", who->id);
    } else {
      o->unset("$wielder");
    }

    if (m==o) {
      o->set("$rider", who->id);
    } else {
      o->unset("$rider");
    }
  }

  foreach(&stuff, o, i) {

    MudObject *clo = o->get_object("cloneof");
    MudObject *k = clo;
    if (!k) {
      k = o;
    }

    fprintf(f, "cloneof %s {\n", k->id);
    fprintf(f, "int !id %i\n", o->get_int("!id"));
    if (o->owner) {
      int i = o->owner->get_int("!id");
      if (i != -1) {
	fprintf(f, "int !inid %i\n", i);
      }
    }

    o->save_difference(f, k);
    fprintf(f, "}\n");

    if (quitting) { 
      vanish(o);
    }
  }
}

static bool verb_cls(MudObject *o, int, const char **) {
    o->printf("\033[2J\033[H\2");
    return true;
}

extern const char *build_date;

static string format_time(time_t t) {
    char thing[256];
    time_t seconds = t % 60;
    time_t minutes = t / 60;
    time_t hours = minutes / 60;
    minutes = minutes % 60;
    if (hours < 24) 
	return ssprintf("%02i:%02i:%02i", (int)hours, (int)minutes, (int)seconds);
    else {
	return ssprintf("%i day%s, %02i:%02i:%02i", (int)hours / 24, 
	    hours >= 48 ? "s" : "", 
		(int)hours % 24, (int)minutes, (int)seconds);
    }
    return thing;
}

static string up_time() {
  return format_time( now - mud_start);
}

static time_t uptime() {
  FILE *f = xopen("/proc/", "uptime", "r");
  if (!f) return 0;
  float d;
  fscanf(f, "%f", &d);
  xclose(f);
  return (int)d;
}

static string box_time() {
    time_t ut = uptime();
    if (ut)
	return format_time( ut );
    else
	return "??";
}

static bool verb_time(MudObject *player, int, const char **) {
    
    time_t tim = now;
    
    player->printf("%s\n", title_for("Time", player).c_str());
    player->printf("Current time        - %s\n", ctime_for(player, &tim));
    player->printf("Last zone reset     - %s\n", ctime_for(player, &mud_reset));
    if (code_reload) 
	player->printf("Last code reload    - %s\n", ctime_for(player, &code_reload));
    if (mud_reboot)
      player->printf("Last reboot         - %s\n", ctime_for(player, &mud_reboot));
    player->printf("Mud Executed        - %s\n", ctime_for(player, &mud_start));
    player->printf("Mud Compiled        - %s\n", build_date);
    player->printf("Mud Uptime          - %s\n", up_time().c_str());
    
    player->printf("Server Uptime       - %s\n", box_time().c_str());
    player->printf("%s\n", footer_for(player).c_str());
    
    return true;
}

static bool verb_emoteto(MudObject *player, int argc, const char *argv[]) {
    if (argc < 2) {
	player->printf("Emote to who?\n");
	return true;
    }
   if (player->get_flag(FL_SLEEPING)) {
        player->printf("You toss and turn in your sleep.\n");
        player->oprintf(cansee, "%#M tosses and turns %s sleep.\n",player, his_or_her(player));
        return true;
    }

    MudObject *dest = get_player(player, argv[1]);
    if (!dest) {
	player->printf("Emote to who?\n");
	return true;
    }

    if (argc < 3) {
	player->printf("Emote what?\n");
	return true;
    }

    string what = the_rest(argc, argv, 2);
    player->printf("You emote to %M : %s\n", dest, what.c_str());
    dest->printf("%#M %s\n", player, what.c_str());
    return true;
}


static bool verb_emote(MudObject *player, int argc, const char *argv[]) {
    if (argc < 2) {
	player->printf("Emote what?\n");
	return true;
    }

    MudObject *r = player->get_object("!$1");
    int ry = 0;
 
    string fs;
    if (r && r->get("name"))
      fs = r->get("name");

    string xm = fs;
    fs += ".";
    xm += "!";

    if (r)
      for (int i=1;i<argc;i++) {
	if (streq(argv[i], "$1")) {
	  argv[i] = r->get("name");
	  ry = 1;
	}
	if (streq(argv[i], "$1.")) {
	  argv[i] = fs.c_str();
	  ry = 1;
	}
	if (streq(argv[i], "$1!")) {
	  argv[i] = xm.c_str();
	  ry = 1;
	}
      }

    if (!ry)
      r = 0;

    string what = the_rest(argc, argv, 1);
    player->printf("You emote : %s\n", what.c_str());

    if (what[0] != '\'')
      what = " " + what;

    if (r) {
      player->oprintf(filter(player) && cansee && avoid(r), "%#M%s\n", player, what.c_str());
      player->lprintf(filter(player) && cansee && avoid(r), "%#M%s\n", player, what.c_str());
      for (int i=1;i<argc;i++) {
	if (streq(argv[i], r->get("name")))
	  argv[i] = "you";
	if (streq(argv[i], fs.c_str()))
	  argv[i] = "you.";
	if (streq(argv[i], xm.c_str()))
	  argv[i] = "you!";
      }
       
      what = the_rest(argc, argv, 1);
      if (what[0] != '\'')
	what = " " + what;
      if (filter(player)(r) && cansee(r))
	r->printf("%#M%s\n", player, what.c_str());

    } else {
      player->oprintf(filter(player) && cansee, "%#M%s\n", player, what.c_str());
      player->lprintf(filter(player) && cansee, "%#M%s\n", player, what.c_str());
    }
    return true;
}


static bool verb_away(MudObject *player, int argc, const char **argv) {
    if (player->get("away") && argc == 1) {
	player->unset("away");
	broadcast(filter(player), "%#M is no longer away.\n", player);
	return true;
    }
    
    if (argc < 2) {
	player->set("away", ".");
	broadcast(filter(player), "%#M is away : no particular reason.\n", player);
	return true;
    }
    
    string reason = the_rest(argc, argv, 1);
    
    player->set("away", reason.c_str());
    
    broadcast(filter(player), "%#M is away : %s\2^n.\n", player, reason.c_str());
    
    return true;
    
}

static bool verb_coding(MudObject *player, int, const char **) {
    if (!player->get_priv(PFL_CODER)) {
	player->printf("Only coders may be marked as coding.\n");
	return true;
    }
    
    player->set_bflag(FL_CODING, !player->get_flag(FL_CODING));
    player->printf("You are %s coding.\n", player->get_flag(FL_CODING)?
		   "now":"no longer");
    return true;
}

static bool verb_aloof(MudObject *player, int, const char **) {
  if (player->get_flag(FL_ALOOF)==0) {
    time_t achange = player->get_int("$aloofchange", 0);
    if (achange > (now-60)) {
      player->printf("You can't do that now.\n");
      return true;
    }
  }
  player->set("$aloofchange", now);

  player->set_bflag(FL_ALOOF, !player->get_flag(FL_ALOOF));
  player->printf("You are %s aloof.\n", player->get_flag(FL_ALOOF)?"now":"no longer");
  return true;
}


static bool is_linkdead(Player *p) {
  if (p && !p->p) return true;
  return false;
}

static void deaccent(char *a) {
  if (!a)
    return;

  while (*a) {

    char *replacements[33] = {
      "s", "a", "a", "a", "a", "a", "a", "e", "c", "e", "e", "e", "e", "i", "i", "i", "i", "d", "n",
      "o", "o", "o", "o", "o", "/", "o", "u", "u", "u", "u", "y", "t", "y"
    };

      unsigned char l=(unsigned char)(*a);
      
      if (l >= 0xdf) {
	*a = replacements[l-0xdf][0];
      }
	
    a++;
  }
}

/*
 *  There is an object with 
 *
 *    archetype's proof pointing at an object here
 *
 *  we have been told foo bar
 *
 *  it should probe in order
 *
 *       this:foo.1
 *       this:bar.1
 *  archetype:foo.1
 *  archetype:bar.1
 *       this:foo
 *       this:bar
 *  archetype:foo
 *  archetype:bar
 *
 */

static bool seektell(MudObject *who, int wordc, const char **words) {
  MudObject *a1 = who->get_object("!$1");
  string txt = the_rest(wordc, words, 0);
  if (dotrap(E_ONTELL, a1, who, 0, txt.c_str()))
  /* ::: tell o1==the mobile, txt==text you've told it; return 1 to not do any more searching */
    return true;
  bool q = 0;
  for (int i=0;i<wordc;i++) {
    if (q) 
      break;
    if (!words[i])
      continue;
    char foo[1024];
    snprintf(foo, 1000, "tell.%s", words[i]);
    char *v = foo + strlen("tell.");
    foo[1000] = 0;
    deaccent(v);
    while (*v) {
      *v = tolower(*v);
      if (*v == '?' || *v == '.' || *v==';' || *v==',' || *v==':' || *v=='!') {
	*v = 0;
	break;
      }
      v++;
    }

    MudObject *statof = who->get_object("tellstate");
    if (!statof)
      statof = who;

    int thestate = state(statof);

    MudObject *tellproof = who->get_object("tellproof");

    if (tellproof) {
      thestate = 0;

      if (tellproof->owner == who)
	thestate = 1;
      if (tellproof->owner == who->owner)
	thestate = 1;
      if (tellproof->owner) {
	if (tellproof->owner->owner == who->owner) {
	  thestate = 1;
	}
      }

    if (statof != who && statof && thestate)
      thestate = state(statof);

    }
    
    MudObject *mis = 0;
    int mstate = 0;
    if (a1) {
      mis = a1->get_object(KEY_ACCEPTED);
      mstate = mis?mis->get_int("!state"):0;
      if (mstate==0)
	mstate = thestate;
    }

    const char *what=0;

#define consider(a) do { string la=a; if (!what) what = who->get(la.c_str()); if (dotrap(la.c_str(), a1, who, 0, 0, 1)!=2) what = ""; } while (0)

    /* ::: tell.* */

    if (streq(foo, "tell.follow") && who->follows==a1) {
      what = lang("tell $1 I am following you.", who);
    }

    if (mis) {
      consider(ssprintf("%s.%s.%i", foo, mis->id, mstate));
      consider(ssprintf("%ss.%s.%i", foo, mis->id, mstate));
      consider(ssprintf("%s.%s", foo, mis->id));
      consider(ssprintf("%ss.%s", foo, mis->id));
    }
    consider(ssprintf("%s.%i", foo, thestate));
    consider(ssprintf("%ss.%i", foo, thestate));
    consider(ssprintf("%s", foo));
    consider(ssprintf("%ss", foo));

    if (what) {
      itell(who, what);
      q = 1;
    }
  }
  return q;
}

class can_affect : public showto {
  const char *z;
public:
  can_affect(const char *z) : z(z) {
  }
  
  bool operator()(const MudObject *o) const {
    return ::can_affect(o, z);
  }
};

static bool do_tell(MudObject *player, MudObject *who, string what, const char **words, int wordc)
{
    if (!is_player(who) && !is_mobile(who)) {
	player->printf("%#Y is not a person.\n", who);
	return true;
    }

    if (who == player) {
      player->printf("First sign of madness, that.\n");
      return true;
    }

    if (who->owner != player->owner && is_mobile(who) && !streq(who->get("zone"), "template")) {
      player->printf("You cannot talk to mobiles remotely.\n");
      return true;
    }

    if (is_player(who) && is_linkdead((Player*)who)) {
      player->printf("%#M is linkdead.\n", who);
      return true;
    }

    if (!visible_to(who, player)) {
      player->printf("You are not visible to %M.\n", who);
      return true;
    }

    if (is_player(who)) {
      Player *p = (Player *)who;
      const char *state = p->get_state()->id;
      if (strstr(state, "make")) {
	player->printf("%#M is building, and currently not available.\n", who);
	return true;
      }
      if (strstr(state, "mail")) {
	player->printf("%#M is in the mailer, and currently not available.\n", who);
	return true;
      }
      if (strstr(state, "board")) {
	player->printf("%#M is making a posting, and currently not available.\n", who);
	return true;
      }
    }

    if (who->get("away")) {
      if (streq(who->get("away"), ".")) {
	player->printf("%#M is away and might not respond.\n", who);
      } else {
	player->printf("%#M is away : %s, and might not respond.\n", who, who->get("away"));
      }
    }

    if (who->get_flag(FL_CODING)) {
      player->printf("%#M is marked as CODING, and might not respond.\n", who);
    }
    if (who->get_flag(FL_SLEEPING)) {
        player->printf("%#M is sleeping.\n",who);
    }

    if (player->get_flag(FL_CODING)) {
      player->printf("Don't forget that you are marked as CODING.\n");
    }

    const char *verb = "tell";
    const char *verb_s = "tells";
    const char *wl = what.c_str();
    if (wl[strlen(wl)-1]=='?') {
      verb = "ask";
      verb_s = "asks";
    }
    if (wl[strlen(wl)-1]=='!') {
      verb = "exclaim to";
      verb_s = "exclaims to";
    }

    //    who->tell(player, what.c_str(), verb);
    who->set("!$1", player->id);

    if (filter(player)(who))
      who->printf("%#M %s you ^`^{t^Z%s\2^}^'\n", player, player->get_flag(FL_PLURAL)?verb:verb_s, what.c_str());
    if (!player->get_flag(FL_NOHEARBACK))
      player->printf("You %s %M ^`^{t^Z%s\2^}^'\n", verb, who, what.c_str());

    if (who->get_flag(FL_MOBILE) && !player->get_flag(FL_MOBILE))
      if (!seektell(who, wordc, words)) {
	if (who->get_flag(FL_STORE) || (who->owner->get_flag(FL_STORE) && who->owner->get_object("shopmob")==who)) {
	  MudObject *list = who->get_flag(FL_STORE)?who:who->owner;
	  NewWorld wot;
	  for (int i=0;i<list->array_size("shop");i++) {
	    MudObject *s = list->array_get_object("shop", i);
	    if (s)
	      wot.add(*s);
	  }
	  for (int i=0;i<list->array_size("$shop");i++) {
	    MudObject *s = list->array_get_object("$shop", i);
	    if (s)
	      wot.add(*s);
	  }
	  NewWorld wh = match(player, wordc, words, 0, LOOK_SPECIAL, 0, &wot);
	  if (wh.getsize()) {
	    who->interpretf("tell %s %s", player->id, lang("Yes, we stock those.", who));
	    return true;
	  }
	}
	
	if (who->get("telldefault"))
	  who->interpret(who->get("telldefault"));
	else if (who->get("tell.default"))
	  who->interpret(who->get("tell.default"));
	else if (!who->get_flag(FL_CANTTALK)) {
	  
	  const char *huh[] = 
	  {
	    lang("tell $1 Huh?", who),
	    lang("tell $1 Pardon?", who),
	    lang("tell $1 Excuse me?", who),
	    lang("tell $1 There's no point telling me about that.", who),
	    lang("tell $1 I don't know anything about that.", who),
	  };
	  
	  who->interpret(huh[random_number(5)]);
	  
	  if (!who->get_flag(FL_CANTTALK) && !who->snoopers.getsize()) {
	    log(class can_affect(who->get("zone")), FL_NOTELLSPAM, PFL_SEEINFO, 0, "tell", "%s asked '%s' - triggered telldefault", drillid(who), what.c_str());
	  }
	}
      }
   

    return true;
}

static void do_tell(MudObject *from, MudObject *unto, const char **words, int wordc)
{
  if (from->get_flag(FL_DUMB)) {
    unto->printf("You can't say anything.\n");
    return;
  }
  
  if (from->get_flag(FL_SLEEPING)) {
    unto->printf("You mumble in your sleep.\n");
    unto->oprintf(canhear, "%#M mumbles in %s sleep.\n", unto, his_or_her(unto));
    return;
  }

  string s = the_rest(wordc, words, 0);
  int hastext = 0;
  for (size_t i=0;i<s.length();i++) {
    hastext |= !isspace(s[i]);
  }

  if (!hastext) {
    from->printf("What do you want to tell %s?\n", him_or_her(unto));
    return;
  }
  
  do_tell(from, unto, s.c_str(), words, wordc);
}

static bool verb_reply(MudObject *player, int argc, const char *argv[]) {
    if (argc < 2) {
	player->printf("reply <speech>\n");
	return true;
    }

    if (player->get_flag(FL_DUMB)) {
        player->printf("You can't say anything.\n");
	return true;
    }

    if (player->get_flag(FL_SLEEPING)) {
        player->printf("You mumble in your sleep.\n");
        player->oprintf(canhear, "%#M mumbles in %s sleep.\n",player, his_or_her(player));
        return true;
    }

    MudObject *who = player->get_object("!$1");
    if (!who) {
        player->printf("Reply to who?\n");
	return true;
    }
    string what = the_rest(argc, argv, 1);
    what = possibly_drunkify(player, what);
    do_tell(player, who, what.c_str(), argv+1, argc-1);

    return true;
}

static bool verb_betold(MudObject *player, int argc, const char **argv) {
  if (argc < 2) {
    player->printf("Act as if told what?\n");
    return true;
  }
  
  seektell(player, 1, argv+1);

  return true;
}

static bool isplayer(const TeleObject &o) {
  return is_player(o.what);
}

static bool verb_which(MudObject *who, int argc, const char **argv)
{
  if (argc < 2) {
    who->printf("Which what?\n");
    return true;
  }
  
  int idx=0;

  for (;;idx++) {
    string s = ssprintf("%s%i", argv[1], idx+1);
    const char *real[] = {s.c_str(), 0};
    NewWorld b2 = match(who, 1, real, isplayer, LOOK_BOTH|IGNORE_EXITS|IGNORE_CATEGORIES);
    if (b2.getsize()==0)
      break;
    who->printf("%i - %M\n", idx+1, b2.get(0));
  }

  if (idx==0) {
    who->printf("No matches for %s.\n", argv[1]);
  }

  return true;
}

static bool verb_tell(MudObject *player, int argc, const char *argv[]) {
    if (argc < 2) {
	player->printf("Tell who?\n");
	return true;
    }

    if (player->get_flag(FL_DUMB)) {
        player->printf("You can't say anything.\n");
	return true;
    }

    if (player->get_flag(FL_SLEEPING)) {
        player->printf("You mumble in your sleep.\n");
        player->oprintf(canhear, "%#M mumbles in %s sleep.\n",player, his_or_her(player));
        return true;
    }

    NewWorld ply;
    MudObject *o;
    int i;
    foreach(players, o, i) 
      ply.add(*o);

    NewWorld blah = match(player, 1, argv+1, isplayer, LOOK_ROOM|IGNORE_EXITS|LOOK_SPECIAL|
			  IGNORE_CATEGORIES, 0, &ply);
    MudObject *who = blah.getsize()?blah.get(0):0;

    if (!who && strchr(argv[1], '@') && strchr(argv[1], '@')!=argv[1]) {
	string what = "atell ";
	what += the_rest(argc, argv, 1);
	player->interpret(what.c_str());
	return true;
    }
        
    if (!who && streq(argv[1], "$1")) {
        who = player->get_object("!$1");
    }
    
    if (!who)
      who = planet->get(argv[1]);

    if (who && !visible_to(who, player))
      who = 0;

    if (!who) {
	player->printf("Cannot find %s.\n", argv[1]);
	return true;
    }

    if (argc < 3) {
	player->printf("What do you want to tell %s?\n", him_or_her(who));
	return true;
    }

    string what = the_rest(argc, argv, 2);
    string txt = possibly_drunkify(player, what);

    do_tell(player, who, txt, argv+2, argc-2);
    return false;
}


static bool verb_whisper(MudObject *player, int argc, const char *argv[]) {
    if (argc < 2) {
	player->printf("Whisper what (and to who?)\n");
	return true;
    }

    if (player->get_flag(FL_DUMB)) {
      if (!player->get_flag(FL_SLEEPING))
        player->oprintf(cansee, "%#M %[grunts/grunt].\n", player);
        player->printf("You can't say anything.\n");
	return true;
    }


    if (player->get_flag(FL_SLEEPING)) {
        player->printf("You mumble in your sleep.\n");
        player->oprintf(canhear, "%#M %[mumbles/mumble] in %s sleep.\n",player, his_or_her(player));
        return true;
    }


    int bad = 0;
    
    int start = 1;

    MudObject *who = 0;
    string rag = argv[1];

    if (rag.length()>=4 && rag[rag.length()-1]=='>') {
      argv[0] = "whisperto";
      rag = rag.substr(0, rag.length()-1);
      argv[1] = rag.c_str();
    }

    if (streq(argv[0], "whisperto")) {
      who = get_player(player, argv[1]);
      if (!who) {
	player->printf("Whisper to who?\n");
	return true;
      }
      if (who->owner != player->owner) {
	player->printf("%#s %|%[is/are] too far away.\n", he_or_she(who), who);
	return true;
      }
      if (argc < 3) {
	player->printf("Whisper what to %s?\n", him_or_her(who));
	return true;
      }
      start++;
    }

    if (who && who->owner != player->owner)
      bad = 1;

    if (bad) {
      player->printf("Whisper to who?\n");
      return true;
    }
    
    string what = the_rest(argc, argv, start);
    string txt = possibly_drunkify(player, what);

    if (!who) {
	player->oprintf(filter(player) && canhear, "%#M whispers ^`^{k^Z%s\2^}^'\n", player, txt.c_str());
	if (!player->get_flag(FL_NOHEARBACK))
	  player->printf("You whisper ^`^{k^Z%s\2^}^'\n", txt.c_str());
	return true;
    }
    if (who == player) {
        who->printf ("First sign of madness, that.\n");
        return true;
    }
    if (filter(player)(who))
      who->printf("%#M whispers to you ^`^{k^Z%s\2^}^'\n",
		player, txt.c_str());
    
    if (!player->get_flag(FL_NOHEARBACK))
      player->printf("You whisper to %M ^`^{k^Z%s\2^}^'\n", who, txt.c_str());
    player->oprintf(filter(player) && cansee && avoid(who), "%#M whispers something to %M.\n", player, who);
    
    return true;
}

static const char *theise(const char *what)
{
  if (streq(what, "north")) return "the north";
  if (streq(what, "south")) return "the south";
  if (streq(what, "east")) return "the east";
  if (streq(what, "west")) return "the west";
  if (streq(what, "down")) return "below";
  if (streq(what, "up")) return "above";
  if (streq(what, "out")) return "outside";
  if (streq(what, "in")) return "inside";
  return what;
}

static MudObject *revexit(MudObject *exit)
{
  MudObject *ln = exit->get_object("link");
  MudObject *o;
  int i;

  const char *oth = other_dir(exit->get("short"));
  
  if (ln) {
    foreach(ln->children, o, i) 
      if (o->get_object("link")==exit->owner && streq(oth, o->get("short")))
	return o;

    foreach(ln->children, o, i) 
      if (o->get_object("link")==exit->owner)
	return o;
  }

  return 0;
}


static bool verb_yell(MudObject *player, int argc, const char *argv[]) {
    if (argc < 2) {
	player->printf("Yell what?\n");
	return true;    
    }

    if (player->get_flag(FL_DUMB)) {
      if (!player->get_flag(FL_SLEEPING))
        player->oprintf(cansee, "%#M %[grunts/grunt].\n", player);
        player->printf("You can't say anything.\n");
	return true;
    }

    if (player->get_flag(FL_CANTTALK) && !player->get_flag(FL_RANTER)) {
        player->oprintf(cansee, "%#M %[looks/look] frustrated.\n", player);
        player->printf("You can't say anything.\n");
	return true;
    }

    if (player->get_flag(FL_SLEEPING)) {
        player->printf("You mumble in your sleep.\n");
        player->oprintf(canhear, "%#M mumbles in %s sleep.\n", player, his_or_her(player));
        return true;
    }

    string what = the_rest(argc, argv, 1);
    string txt = possibly_drunkify(player, what);

    const char *yverb = "yell";
    const char *ingverb = "yelling";
    const char *pverb = "yell";
    const char *sverb = "yells";

    if (!player->get_flag(FL_DEAF)) {
      player->owner->printf("You hear a voice in your head %s ^`^{W^Z%s\2^}^'\n",
			    ingverb, txt.c_str());
    }

    if (!player->get_flag(FL_NOHEARBACK))
      player->printf("You %s ^`^{W^Z%s\2^}^'\n", yverb ? yverb : pverb, txt.c_str());

    player->oprintf(filter(player) && canhear, "%#M %s ^`^{W^Z%s\2^}^'\n", player, 
		    player->get_flag(FL_PLURAL) ? pverb : sverb, txt.c_str());

    World<MudObject> nb;

    player->lprintf(filter(player) && canhear, "%#M %s ^`^{W^Z%s\2^}^'\n", 
		    player, player->get_flag(FL_PLURAL)?pverb:sverb, txt.c_str());

    MudObject *o;
    int i;

    filter f(player);

    foreach(player->owner->children, o, i) {
      MudObject *l = o->get_object("link");
      if (l && l != o->owner) {
	if (linkedlink(o)==l)
	  continue;

	MudObject *p;
	int j;

	MudObject *rev = revexit(o);

	MudObject *door = rev?rev->get_object("door"):NULL;
	const char *er = rev?rev->get("short"):"";
	if (er && *er) er = theise(er);

	foreach(l->children, p, j) {
	  if (!canhear(p))
	    continue;
	  if (!f(p))
	    continue;
	  if (door && state(door) && cansee(p)) {
	    p->printf("You hear someone yelling ^`^{W^Z%s\2^}^' through %Y.\n", txt.c_str(), door);
	  } else if (er) {
	    p->printf("You hear someone yelling ^`^{W^Z%s\2^}^' from %s.\n", txt.c_str(), er);
	  } else {
	    p->printf("You hear someone yelling ^`^{W^Z%s\2^}^'.\n", txt.c_str());
	  }
	}
      }
    }

    return true;
}

static void hear(MudObject *where, MudObject *player, const char *txt)
{
  MudObject *o;
  int i;
  foreach(where->children, o, i) {
    dotrap(E_ONHEAR, player, o, NULL, txt);
    /* ::: hear o1==the object that heard, txt==the text heard; called on every object resursively in pl->owner, including pl and its contents */
    hear(o, player, txt);
  }
}

static bool verb_say(MudObject *player, int argc, const char *argv[]) {
    if (argc < 2) {
	player->printf("Say what?\n");
	return true;    
    }

    if (player->get_flag(FL_DUMB)) {
      if (!player->get_flag(FL_SLEEPING))
        player->oprintf(cansee, "%#M %[grunts/grunt].\n", player);
        player->printf("You can't say anything.\n");
	return true;
    }

    if (player->get_flag(FL_CANTTALK) && !player->get_flag(FL_RANTER)) {
        player->oprintf(cansee, "%#M %[looks/look] frustrated.\n", player);
        player->printf("You can't say anything.\n");
	return true;
    }

    if (player->get_flag(FL_SLEEPING)) {
        player->printf("You mumble in your sleep.\n");
        player->oprintf(canhear, "%#M mumbles in %s sleep.\n", player, his_or_her(player));
        return true;
    }

    int first = 1;
    MudObject *dest = 0;
    
    string rag = argv[1];

    if (rag.length()>=4 && rag[rag.length()-1]=='>') {
      argv[0] = "sayto";
      rag = rag.substr(0, rag.length()-1);
      argv[1] = rag.c_str();
    }

    if (streq(argv[0], "sayto")) {
      dest = get_player(player, argv[1]);
      if (!dest) {
	player->printf("Say to who?\n");
	return true;
      }
      if (dest->owner != player->owner) {
	player->printf("%#s %|%[is/are] too far away.\n", he_or_she(dest), dest);
	return true;
      }
      if (argc < 3) {
	player->printf("Say what to %s?\n", him_or_her(dest));
	return true;
      }
      first++;
    }

    string what = possibly_drunkify(player, the_rest(argc, argv, first));

    const char *sverb = "says", *pverb = "say", *yverb = 0, *ingverb = "saying", *needto=" to";

    const char *last = what.c_str()+strlen(what.c_str());

    if (last != what) {
      last--;

      if (*last=='?' && !dest) { sverb = "asks"; pverb = "ask"; ingverb = "asking"; needto = ""; }
      if (*last=='!' && !dest) { sverb = "exclaims"; pverb = "exclaim"; ingverb = "exclaiming"; }
      
      if (*last==')') {
	if (last!=what) {
	  last--;

	  if (*last==':') {
	    if (last!=what) {
	      sverb = "smiles and says";
	      pverb = "smile and say";
	    }
	  } else
	  if (*last==';') {
	    if (last!=what) {
	      sverb = "winks and says";
	      pverb = "wink and say";
	    }
	  }
	}
      }

      if (*last=='(') {
	if (last!=what) {
	  last--;
	  if (*last==':') {
	    if (last!=what) {
	      sverb = "frowns and says";
	      pverb = "frown and say";
	    }
	  }
	}
      }

      if (*last=='p' || *last=='P') {
	if (last!=what) {
	  last--;
	  if (*last==':') {
	    if (last!=what) {
	      static char sverbt[100];
	      sprintf(sverbt, "sticks %s tongue out and says",
		      his_or_her(player));
	      sverb = sverbt;
	      pverb = "stick their tongues out and say";
	      if (player->get_flag(FL_PLURAL)) {
		yverb = "stick your tongues out and say";
	      } else {
		yverb = "stick your tongue out and say";
	      }
	    }
	  }
	}
      }

      if (*last=='x') {
	if (last!=what) {
	  last--;
	  if (*last==':') {
	    if (last!=what) {
	      sverb = "blows a kiss and says";
	      pverb = "blow a kiss and say";
	    }
	  }
	}
      }
    }

    if (dotrap(E_BEFORESAY, player, player->owner, NULL, what.c_str()))
    /* ::: before_say o1==this room, txt==text to say; return 1 doesn't say it */
      return true;

    if (canhear(player->owner)) {
      player->owner->printf("You hear a voice in your head %s ^`^{s^Z%s\2^}'\n",
			    ingverb, what.c_str());
    }

    if (!dest) {
      if (!player->get_flag(FL_NOHEARBACK))
	player->printf("You %s ^`^{s^Z%s\2^}^'\n", yverb ? yverb : pverb, what.c_str());
      player->oprintf(filter(player) && canhear,  
		      "%#M %s ^`^{s^Z%s\2^}^'\n", player, 
      		      player->get_flag(FL_PLURAL) ? pverb : sverb, what.c_str()); 
		      
    } else if (dest==player) {
      if (!player->get_flag(FL_NOHEARBACK))
	player->printf("You %s%s %s ^`^{s^Z%s\2^}^'\n", yverb ? yverb : pverb, needto, yourself_yourselves(player), what.c_str());
      player->oprintf(filter(player) && canhear, "%#M %s%s %s ^`^{s^Z%s\2^}^'\n", player, 
		      player->get_flag(FL_PLURAL) ? pverb : sverb, needto, himself_or_herself(player), what.c_str());
    } else {
      if (!player->get_flag(FL_NOHEARBACK))
	player->printf("You %s%s %M ^`^{s^Z%s\2^}^'\n", yverb ? yverb : pverb, needto, dest, what.c_str());
      player->oprintf(filter(player) && canhear, "%#M %s%s %P ^`^{s^Z%s\2^}^'\n", player, 
		      player->get_flag(FL_PLURAL) ? pverb : sverb, needto, dest, what.c_str());
    }

    player->lprintf(filter(player) && canhear, "You overhear %M %s ^`^{s^Z%s\2^}^'\n", 
		    player, ingverb, what.c_str());

    /* ::: say o1==this room, txt==text said; */
    dotrap(E_ONSAY, player, player->owner, NULL, what.c_str());

    hear(player->owner, player, what.c_str());

    return true;
}

static bool verb_beep(MudObject *player, int argc, const char **argv) {
  if (argc < 2) {
    player->printf("Beep who?\n");
    return true;
  }
   if (player->get_flag(FL_SLEEPING)) {
        player->printf("You imagine annoying someone.\n");
        player->oprintf(cansee, "%#M makes strange hand motions in %s sleep.\n",player, his_or_her(player));
        return true;
    }

  MudObject *who = get_player(player, argv[1]);
  if (who) {

    if (who->get_flag(FL_NOBEEP)) {
      player->printf("%#M has beeps turned off.\n", who);
      return true;
    }

    filter f(player);

    if (who==player) {
      who->printf("You send yourself annoying beeps!\a\n", player);
    } else {
      if (f(who))
        who->printf("%#M sends you annoying beeps!\a\n", player);
      player->printf("You send %M annoying beeps!\n", who);
    }
  } else {
    player->printf("Nobody here by that name...\n");
  }
  return true;
}

template <Flag flag> class toggle {
  static const char *onstr, *offstr;
  static bool handle(MudObject *who, int argc, const char **argv) {
    if (who->get_flag(flag)) {
      who->set_flag(flag, 0);
      who->printf("%s\n", offstr);
    } else {
      who->set_flag(flag, 1);
      who->printf("%s\n", onstr);
    }
    return true;
  }
public:
  toggle(const char *off, const char *on) {
    onstr = on;
    offstr = off;
  }
  operator verbhandler_t() {
    return handle;
  }
};

template<int flag> const char *toggle<flag>::onstr;
template<int flag> const char *toggle<flag>::offstr;

toggle<FL_NOBEEP>     verb_nobeep    ("Beeps now on.", "Beeps now off.");
toggle<FL_NOSHIP>     verb_noship    ("Dock messages now on.", "Dock messages now off.");
toggle<FL_NOMISS>     verb_nomiss    ("Missed hit messages now on.", "Missed hit messages now off.");
toggle<FL_NOINFO>     verb_noinfo    ("Info now on.", "Info now off.");
toggle<FL_NOSLAIN>    verb_noslain   ("Death log now on.", "Death log now off.");
toggle<FL_NORESET>    verb_noreset   ("Reset messages on.", "Reset messages off.");
toggle<FL_NOTELLSPAM> verb_notellspam("Mobile tells shown.", "Mobile tells hidden.");
toggle<FL_NOBUGS>     verb_nobugs    ("Bug change notifications shown.", "Bug change notifications hidden.");
toggle<FL_NOBUFFER>   verb_nobuffer  ("Linkdead buffer on.", "Linkdead buffer off.");

toggle<FL_LEAVESHIP> verb_autoleave("Automatic leaving of ships off.", "Automatic leaving of ships on.");
toggle<FL_BRIEF> verb_brief("Brief room descriptions off.", "Brief room descriptions on.");

toggle<FL_NONAWS> verb_naws("NAWS on.", "NAWS off.");

toggle<FL_NOHEARBACK> verb_hearback("Hearback off.", "Hearback on.");

static bool verb_lines(MudObject *player, int argc, const char **argv) {
  if (argc>1) {
    int mode = -1;
    if (streq(argv[1], "auto"))  mode = 1;
    if (streq(argv[1], "off"))   mode = 0;
    if (streq(argv[1], "on"))    mode = 2;

    if (mode != -1) {
      player->set("linedraw", mode);
      player->printf("Linedrawing characters set to '%s'\n", argv[1]);
      return true;
    }
  }
  
  const char *cur[] = { "off", "auto", "on", "?" };
  int mode = player->get_int("linedraw", 1);
  if (mode < 0 || mode > 2) mode = 3;

  player->printf("Linedrawing characters can be either 'on', 'off', or 'auto'. Currently '%s'.\n", 
		 cur[mode]);
  return true;
}

static bool verb_saveplayer(MudObject *who, int argc, const char **argv) {
  if (!who->get_flag(FL_LOGGEDIN)) {
    return true;
  }
  if (argc > 1 && streq(argv[1], "all")) {
    if (privs_of(who) <= LEV_COMMANDER) {
      who->printf ("The only person who needs saving around here is you!\n");
      return true;
    }
    MudObject *o;
    int i;
    log(PFL_SEEINFO, 0, "wiz", "saved everyone");
    foreach(players, o, i) { 
      verb_saveplayer(o, 0, 0);
    }
    return true;
  }

  MudObject *home = saveloc(who);
  who->unset("home");
  if (home)
    who->set("home", home->id);

  char temp[256];
  time_t t;
  time(&t);
  strcpy(temp, ctime(&t));
  *strchr(temp, '\n')=0;
  who->set("laston", temp);

  if (!who->get_object(KEY_WIELD) || (who->get_object(KEY_WIELD)->owner != who)) {
    who->unset(KEY_WIELD);
  }
  
  if (is_player(who)) {
    Player *p = (Player *)who;
    who->set("timeon", who->get_int("timeon", 0) + now - p->time_since);
    p->time_since = now;

    FILE *f = xopen_tmp(DATA_USERS, who->id, "w");
    
    if (!f) {
      who->printf("BUG! Cannot open save-player-file.\n");
      return true;
    }
    
    who->save(f, 1);
    
    eq_write_to_file(f, who);
    
    xclose_confirm(f, DATA_USERS, who->id);
    if (who->get_object("home"))
      who->printf("Saved %M %s.\n", who,
		  format_roomname(who->get_object("home"), who, "", "", "at").c_str());
    else
      who->printf("Saved %M.\n", who);
    return true;
  } else {
    who->printf("But you aren't a player!\n");
    return true;
  }
}

static bool verb_quit(MudObject *player, int argc, const char*argv[]) {
  if (!player->ilc) {
    
    if (player->get("_fighting")) {
        player->printf("Not during combat.\n");
        return true;
    }
          
    if (is_player(player) && player->owner && 
        player->owner->get_flag(FL_NOQUIT)) {
        player->printf("You can't quit from this location.\n");
        return true;
    }
  }
 
    if (!player->get_flag(FL_SILENTQUIT)) {
      if (player->owner && player->owner != mud)
        log(PFL_SEEINFO, invis(player), "net", "quitting %s",  format_roomname(player->owner, player, "", "", "from", 1).c_str());
      else
        log(PFL_SEEINFO, invis(player), "net", "quitting");
      player->oprintf(secret(player) && cansee, "%s\n", build_setin(player, setqout).c_str());
      player->lprintf(secret(player) && cansee, "%s\n", build_setin(player, setqout).c_str());
    }

    player->set_flag(FL_SILENTQUIT, 0); 

    if (is_player(player)) {
      Player *p = (Player*)player;
      if (p->p) {
	player->set("hostname", p->p->get_ip());
      }
    }
    player->set("!quitting", 1);
    verb_saveplayer(player, 0, 0);

    player->set_bflag(FL_LOGGEDIN, 0);
    player->set_flag(FL_SLEEPING, 0);
    player->set_flag(FL_SITTING, 0);

    MudObject *o;
    int i;
    foreach(player->children, o, i) {
      vanish(o);
      if (o->owner != player)
	i--;
    }
    player->interpret("drop ALL");

    if (is_player(player)) {
      player->quit = time(NULL)+1;
      player->spewfile(STADATA, "quitmessage");
      player->send_data();
    } else {
      mission_cleanup(player);
      player->nuke_me = 1;
    }

    set_owner(player, "@musicmud");

    return true;
}

static bool verb_converse(Player *who, int argc, const char **argv) {
  if (argc < 2) {
    who->printf("Converse with who?\n");
    return true;
  }

  if (who->get_flag(FL_DUMB)) {
    who->printf("You can't say anything.\n");
    return true;
  }

  if (who->get_flag(FL_SLEEPING)) {
    who->printf("You mumble in your sleep.\n");
    who->oprintf(canhear, "%#M mumbles in %s sleep.\n",who, his_or_her (who));
    return true;
  }
	
  MudObject *with = get_player(who, argv[1]);	
  if (who == with) {
    who->printf("First sign of madness, that.\n");
    return true;
  }

  if (with || (strchr(argv[1], '@') && aberchat_isup)) {
    PersonState *s = new PersonState("converse");
    s->set("with", with->id);
    s->set("prompt", ssprintf("<%s> ", with->get("name")));
    who->push_state(s);
    who->printf("Converse mode activated : ** to quit\n");
  } else {
    PersonState *s = new PersonState("converse");
    if (strcasecmp(argv[1], "typo")==0 || strcasecmp(argv[1], "bug")==0 || strcasecmp(argv[1], "idea")==0) {
      who->printf("Now that would be stupid, moron. Use the %s command properly.\n", argv[1]);
      return true;
    }
    s->set("cmd", argv[1]);
    s->set("prompt", ssprintf("<%s> ", argv[1]).c_str());
    who->push_state(s);
    who->printf("Converse mode activated : ** to quit\n");
  }
  return true;
}

static void state_converse(Player *who, const char *what) {
    if (!(what && *what)) {
        who->printf("<text> to send text, ** to quit, or *command to run <command>\n");
        return;
    }
    if (streq(what, "**")) {
	who->pop_state();
	return;
    }
    if (*what == '*') {
        who->interpret(what+1);
        return;
    }
    if (who->get_flag(FL_SLEEPING)) {
        who->printf("You mumble in your sleep.\n");
        who->oprintf(canhear, "%#M mumbles in %s sleep.\n",who, his_or_her (who));
        return;
    }
    PersonState *state = who->get_person_state();

    if (state->get("cmd")) {
      string cmd = state->get("cmd");
      cmd += " ";
      cmd += what;
      who->interpret(cmd.c_str());
      return;
    }


    MudObject *to = planet->get(state->get("with"));
    
    if (!to) {
	who->printf("Cannot find : %s\n", state->get("with"));
	who->pop_state();
	return;
    }

    if (who->get_flag(FL_DUMB)) {
        who->printf("You can't say anything.\n");
	return;
    }

    string cmd2="tell ";
    cmd2 += to->id;
    cmd2 += " ";
    cmd2 += what;
    who->interpret(cmd2.c_str());
}

static bool verb_laston(MudObject *who, int argc, const char **argv) {
    if (argc < 2) {
	who->printf("Laston time of who?\n");
	return true;
    }
    string name = make_lower(argv[1]);
    const MudObject *p = planet->get(name.c_str());
    if (p && visible_to(who, p)) {
      who->printf("%#M is already on.\n", p);
      return true;
    }

    OfflinePlayer whose(who);
    whose.grab(who, name.c_str());
    p = whose;

    if (!p) {
      who->printf("No such player.\n");
      return true;
    }

    if (!p->get("laston")) {
      who->printf("%#M has never been on.\n", p);
      return true;
    }

    struct tm tm;
    strptime(p->get("laston"), "%c", &tm);
    time_t newtime = mktime(&tm);

    who->printf("%#M was last on at %s.\n", 
		p, 
		ctime_for(who, &newtime));
    
    return true;
}

static bool verb_finger(MudObject *who, int argc, const char **argv) 
{
  if (argc < 2) {
    who->printf("Finger who?\n");
    return true;
  }

  string name = make_lower(argv[1]);

  OfflinePlayer whose(who);
  whose.grab(who, name.c_str());
  const MudObject *p = whose;

  static struct {
    const char *title;
    const char *key;
    bool newline;
    bool time;
  } things[] = {
    { "Last on            ", "laston", 0, 1 } ,
    { "Title              ", "title", 0 } ,
    { "Birthday           ", "finger.dob", 0  } ,
    { "Email Address      ", "finger.email", 0  } ,
    { "Homepage           ", "finger.url" , 0 } ,
    { "UIN (ICQ Number)   ", "finger.uin" , 0 } ,
    { "Name               ", "finger.name", 0  } ,
    { "Away reason        ", "away", 0  } ,
  };

  static struct {
    const char *title;
    const char *key;
  } wizthings[] = {
    { "Mobile Number      ", "finger.phone" } ,
  };

#define THING_COUNT (sizeof(things)/(sizeof(things[0])))
#define WIZ_THING_COUNT (sizeof(wizthings)/(sizeof(wizthings[0])))

  if (!p) {
    who->printf("No such player.\n");
    return true;
  }

  if (!is_player(p)) {
    who->printf("Not a player.\n", p);
    return true;
  }

  who->printf("You finger %M.\n", p);
  
  for (unsigned int i=0;i<THING_COUNT;i++) {
    const char *what = p->get(things[i].key);
    if (what) {
      if (things[i].time) {
	struct tm tm;
	strptime(what, "%c", &tm);
	time_t newtime = mktime(&tm);
	who->printf("^n%s : %s\n", things[i].title, ctime_for(who, &newtime));
      }
      else
	who->printf("^n%s : %s\n", things[i].title, what);
    }				
  }
  
  if (privs_of(who)>=LEV_COMMODORE) {
    
    for (unsigned int i=0;i<WIZ_THING_COUNT;i++) {
      const char *what = p->get(wizthings[i].key);
      if (what) {
	who->printf("^n%s : %s\n", wizthings[i].title, what);
      }
    }

    const Player *pl = whose.pl();
    
    if (!pl->p)
      who->printf("^nLast host           : %s\n",p->get("hostname"));
    else
      who->printf("^nLast host           : %s\n",pl->p->get_ip().c_str());
  }
  
  time_t when = p->get_int("created", 0);
  if (when > 0) {
    who->printf("^nAccount Created     : %s\n", ctime_for(who, &when));
  }
  
  return true;
}

static bool verb_newstyle(MudObject *player, int, const char **) {
  bool w = !player->get_flag(FL_NEWSTYLE);
  player->set_bflag(FL_NEWSTYLE, w);
  player->set_bflag(FL_CLRSTYLE, false);
  player->printf("Newstyle now %s.\n", w ? "on" : "off");
  return true;
}

static bool verb_clrstyle(MudObject *player, int, const char **) {
  bool w = !player->get_flag(FL_CLRSTYLE);
  player->set_bflag(FL_CLRSTYLE, w);
  player->set_bflag(FL_NEWSTYLE, false);
  player->printf("Clrstyle now %s.\n", w ? "on" : "off");
  return true;
}

static bool verb_newlines(MudObject *player, int, const char **) {
  bool w = !player->get_int("copious", 0);
  player->set("copious", w);
  player->printf("Explicit Newlines now %s.\n", w ? "on" : "off");
  return true;
}

static const char *cstr(charset cs)
{
  switch (cs) {
  case CHAR_UNICODE: return "UTF-8";
  case CHAR_LATIN1: return "ISO-8859-1";
  case CHAR_ASCII: return "US-ASCII";
  case CHAR_MACROMAN: return "MAC-ROMAN";
  case CHAR_CP437: return "CP437";
  case CHAR_CP850: return "CP850";
  case CHAR_CP1252: return "CP1252";
  case CHAR_KOI8R: return "KOI8-R";
  default: return "unknown";
  }
}

//! Mapping from a string charset name to the internal charset ID.
struct cset_alias {
  const char *nym;
  charset cs;
} alias[] = {
  {  "UTF-8",     CHAR_UNICODE, },
  {  "Unicode",   CHAR_UNICODE, },
  {  "ASCII",     CHAR_ASCII, },
  {  "US-ASCII",  CHAR_ASCII, },
  {  "Latin",     CHAR_LATIN1, },
  {  "Latin1",    CHAR_LATIN1, },
  {  "ISO-8859-1",CHAR_LATIN1, },
  {  "KOI",       CHAR_KOI8R, },
  {  "KOI8",      CHAR_KOI8R, },
  {  "KOI8R",     CHAR_KOI8R, },
  {  "KOI8-R",    CHAR_KOI8R, },
  {  "MACROMAN",  CHAR_MACROMAN, },
  {  "MAC-ROMAN", CHAR_MACROMAN, },
  {  "CP437",     CHAR_CP437, },
  {  "CP850",     CHAR_CP850, },
  {  "CP1252",    CHAR_CP1252, },
  {  NULL, }
};

static const char *strname(charset cs) {
  switch (cs) {
  case CHAR_UNICODE: return "unicode";
  case CHAR_LATIN1: return "latin1";
  case CHAR_ASCII: return "ascii";
  case CHAR_MACROMAN: return "macroman";
  case CHAR_KOI8R: return "koi8r";
  case CHAR_CP437: return "cp437";
  case CHAR_CP850: return "cp850";
  case CHAR_CP1252: return "cp1252";
  default: return NULL;
  }
}

static void iso2022(MudObject *who, charset ncs)
{
  if (ncs == CHAR_UNICODE)
    who->printf("\033%G\2");
  else {
    who->printf("\033%@\2");

    /* CHAR_LATIN1         -A   8859-1     West Europe
       CHAR_LATIN2         -B       -2     East Europe
       CHAR_LATIN3         -C       -3     South Europe
       CHAR_LATIN4         -D       -4     Baltic States
       CHAR_LATIN/CYRIL    -L       -5     Cyrillic
       CHAR_LATIN/GREEK    -F       -6     Greek
       CHAR_LATIN/ARAB     -G       -7     Arabic
       CHAR_LATIN/HEBREW   -^       -8     Hebrew
       CHAR_LATIN5         -M       -9     Turkish
       CHAR_LATIN6         -V       -10    Nordic
       CHAR_LATIN/THAI     -T       -11    Thai
                                    -12    Indian?
       CHAR_LATIN7         -Y       -13    Baltic Rim
       CHAR_LATIN8         -_       -14    Celtic
       CHAR_LATIN9         -b       -15    West Europe (Euro)
       CHAR_LATIN10                 -16    East Europe (Euro) */

    if (ncs==CHAR_LATIN1)      
      who->printf("\033-A\2");
  }
}

const char *choices = "Choices are Unicode (UTF-8), ASCII, MacRoman, Latin1 (ISO-8859-1), KOI8-R, CP437, CP850, or CP1252.";

static bool verb_charset(MudObject *who, int argc, const char **argv) {
  charset cs = get_charset(who);
  if (argc < 2) {
    who->printf("Your current charset %s '%s'.\n", who->get("charset")?"is explicitly":"is implicitly", cstr(cs));
    who->printf("%s\n", choices);
    return true;
  }

  if (argc>1) {
    int i = 0;

    if (strcasecmp(argv[1], "default")==0) {
      if (!who->get("charset")) {
	who->printf("You are already using the default charset.\n");
	return true;
      }
      who->unset("charset");
      charset ncs = get_charset(who);
      who->printf("You are now using the default charset (currently '%s').\n", cstr(ncs));
      iso2022(who, ncs);
      return true;
    }

    while (alias[i].nym) {
      if (strcasecmp(argv[1], alias[i].nym)==0) {
	if (streq(argv[2], "show")) {
	  charset cs = alias[i].cs;	
	  for (int i=128;i<256;i++) {
	    int uni = cp2uni(i, cs);
	    if (uni!=-1 && uni >= 0xa0) {
	      string c= ssprintf("^#%i;", cp2uni(i, cs));
	      who->printf("%3s ", c.c_str());
	    }
	    else
	      who->printf("    ", cp2uni(i, cs));
	    if ((i & 15)==15)
	      who->printf("\n");
	  }
	  return true;
	}

	if (alias[i].cs == cs && who->get("charset")) {
	  who->printf("Your charset is '%s' already.\n", cstr(cs)); 
	  return true;
	} else {
	  charset ncs = alias[i].cs;
	  
	  who->set("charset", strname(ncs));
	  who->printf("Charset set to '%s'.\n", cstr(ncs)); 

	  iso2022(who, ncs);
	  
	  return true;
	}
      }
      i++;
    }
    who->printf("Didn't understand that. %s\n", choices);
    return true;
  }

  return true;
}

#ifndef NOCOMPRESS
static bool verb_compress(Player *who, int argc, const char **argv)
{
  Player *p = who;
  if (!p->p) {
    who->printf("You are linkdead.\n");
    return true;
  }
  if (!p->p->will_compress) {
    who->printf("Your client doesn't support compression.\n");
    return true;
  }
  if (p->p->compress) {
    p->p->compress_off();
    who->printf("Compression now off.\n");
  }
  else {
    p->p->compress_on();
    who->printf("Compression now on.\n");
  }
  return true;
}
#endif

static bool verb_tip(MudObject *who, int argc, const char **argv)
{
  who->printf("%s\n", random_tip().c_str());
  return true;
}


static bool verb_ignore(MudObject *who,int argc,const char** argv)
{
  if (argc<2) {
    string ignore;
    Object::strit i = who->strs.begin();
    while (i != who->strs.end()) {
      const char *key = i->first.c_str();
      if (strncmp(key, "ignore.", 7)==0) {
	ignore += "^p";
	ignore += toupper(key[7]);
	ignore += key+8;
	ignore += "^n";
	ignore += ", ";
      }
      i++;
    }
    if (ignore.length()) {
      ignore.erase(ignore.length()-2);
      who->printf("You are ignoring %s.\n", ignore.c_str());
    } else {
      who->printf("You aren't ignoring anyone.\n");
    }
    return true;
  }
  string k = "ignore.";
  k += argv[1];
  if (who->get(k.c_str())) {
    who->unset(k.c_str());
    who->printf("No longer ignoring ^p%#s^n.\n", argv[1]);
  } else {
    who->set(k.c_str(), "t");
    who->printf("Now ignoring ^p%#s^n.\n", argv[1]);
  }
  return true;
}


#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))

#define CLEANUP DO_TELL = 0;

#include "verbmodule.h"

void startup() {

  DO_TELL = do_tell;

#ifndef NOCOMPRESS
AUTO_VERB(compress, 4, 0, PFL_NONE);
#endif
AUTO_VERB(ignore, 2, 0, PFL_NONE);

#define verb_ask verb_tell

AUTO_VERB(tip, 3, 0, PFL_NONE);

AUTO_VERB(away, 3, 0, PFL_NONE, VFL_MAGICSPACE);
ADD_ALIAS(afk, 3, away);
AUTO_VERB(beep, 2, 0, PFL_NONE);
AUTO_VERB(coding, 3, 0, PFL_CODER);
AUTO_VERB(aloof, 3, 0, PFL_NONE);

AUTO_VERB(converse, 4, 0, PFL_NONE);
AUTO_VERB(emoteto, 7, 0, PFL_NONE, VFL_MAGICSPACE);
AUTO_VERB(finger, 3, 0, PFL_NONE);
AUTO_VERB(nobeep, 4, 0, PFL_NONE);
AUTO_VERB(noship, 4, 0, PFL_NONE);
AUTO_VERB(nobuffer, 4, 0, PFL_NONE);
AUTO_VERB(nomiss, 4, 0, PFL_NONE);

AUTO_VERB(quit, 4, 0, PFL_NONE);
ADD_ALIAS(logout, 4, quit);

AUTO_VERB(saveplayer, 3, 0, PFL_NONE);
AUTO_VERB(tell, 2, 0, PFL_NONE, VFL_MAGICSPACE);
AUTO_VERB(betold, 6, 0, PFL_NONE);
AUTO_VERB(ask, 3, 0, PFL_NONE, VFL_MAGICSPACE);
AUTO_VERB(reply, 2, 0, PFL_NONE, VFL_MAGICSPACE);
AUTO_VERB(time, 3, 0, PFL_NONE);

AUTO_VERB(yell, 4, 0, PFL_NONE, VFL_MAGICSPACE);

#define verb_sayto verb_say
AUTO_VERB(say, 2, 0, PFL_NONE, VFL_MAGICSPACE);
AUTO_VERB(sayto, 4, 0, PFL_NONE, VFL_MAGICSPACE);
alias_add("'", 1, "say");
alias_add("\"", 1, "say");

#define verb_whisperto verb_whisper
AUTO_VERB(whisper, 3, 0, PFL_NONE, VFL_MAGICSPACE);
AUTO_VERB(whisperto, 8, 0, PFL_NONE, VFL_MAGICSPACE);

AUTO_VERB(emote, 5, 0, PFL_EMOTE, VFL_MAGICSPACE);
alias_add(":", 1, "emote");
alias_add(";", 1, "emote");

AUTO_VERB(charset, 5, 0, PFL_NONE);

AUTO_VERB(autoleave, 5, 0 , PFL_NONE);
AUTO_VERB(laston, 2, 0, PFL_NONE);
AUTO_VERB(cls, 3, 0, PFL_NONE);
ADD_ALIAS(clear, 4, cls);

AUTO_VERB(newstyle, 4, 0, PFL_NONE);
AUTO_VERB(clrstyle, 4, 0, PFL_NONE);
AUTO_VERB(newlines, 5, 0, PFL_NONE);
AUTO_VERB(brief, 3, 0, PFL_NONE);
AUTO_VERB(noinfo, 5, 0, PFL_SEEINFO);
AUTO_VERB(noslain, 5, 0, PFL_GOTO);
AUTO_VERB(noreset, 5, 0, PFL_GOTO);
AUTO_VERB(notellspam, 5, 0, PFL_NONE);
AUTO_VERB(nobugs, 5, 0, PFL_NONE);
AUTO_VERB(hearback, 5, 0, PFL_NONE);

AUTO_VERB(lines, 3, 0, PFL_NONE);

AUTO_VERB(which, 4, 0, PFL_NONE);

 AUTO_VERB(naws, 4, 0, PFL_NONE);

ADD_STATE("converse", "Converse>^n^e", state_converse, false);


}