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 Daemon, version 1.0
 * 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 <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <ctype.h>

#include "Mission.h"
#include "musicmud.h"
#include "util.h"
#include "pflags.h"
#include "death.h"
#include "misc.h"
#include "msi.h"
#include "hooks.h"
#include "events.h"
#include "trap.h"
#include "verbs.h"
#include "paths.h"

#include "units.h"
#include "match.h"
#include "prep.h"
#include "vsprintf.h"
#include "paths.h"
#include "levels.h"
#include "charset.h"
#include "emsg.h"
#include "actions.h"
#include "body.h"

#include "musictok.h"

bool isvowel(char letter) 
{
  if (letter=='a') return 1;
  if (letter=='e') return 1;
  if (letter=='i') return 1;
  if (letter=='o') return 1;
  if (letter=='u') return 1;
  return 0;
}

bool endswith(const string &a, const string &b)
{
  return endswith(a.c_str(), b.c_str());
}

void newending(string &s, const char *old, const char *newend)
{
  s = s.substr(0, s.length()-strlen(old));
  s += newend;
}

string plural_word(const char *word) 
{
  string w = word;
  int an = 0;
  if (w.substr(w.length()-2)=="^n") {
    w = w.substr(0, w.length()-2);
    an = 1;
  }

  if (endswith(w, "knife")) {
    newending(w, "fe", "ves");
  } else

  if (endswith(w, "shelf")) {
    newending(w, "f", "ves");
  } else

  if (endswith(w, "leaf")) {
    newending(w, "f", "ves");
  } else

  if (endswith(w, "thief")) {
    newending(w, "f", "ves");
  } else

  if (endswith(w, "potato")) {
    newending(w, "o", "oes");
  } else

  if (w.substr(w.length()-1)=="s" ||
      w.substr(w.length()-1)=="z" ||
      w.substr(w.length()-1)=="x") {
    w += "es";
  } else if (w.substr(w.length()-2)=="ch" ||
	     w.substr(w.length()-2)=="sh") {
    w += "es";
  } else if (w.length()>=2 && w[w.length()-1]=='y'
	     && !isvowel(w[w.length()-2])) {
    w = w.substr(0, w.length()-1);
    w += "ies";
  } else {
    w += "s";
  }

  if (an) {
    w += "^n";
  }

  return w;

  /* consider having a table of exceptions here

     knife -> knives
     leaf -> leaves
     staff -> staves
     thief -> thieves
     wife -> wives
     foot -> feet
     goose -> geese
     louse -> lice
     mouse -> mice
     man -> men
     woman -> women
     shelf -> shelves
     potato -> potatoes
     child -> children

  */
}

string plural_phrase(const char *phrase)
{
  if (const char *br=strstr(phrase, " (")) {
    string before_p = string(phrase, br-phrase);
    string after_p = br;
    return plural_phrase(before_p.c_str())+after_p;
  }

  if (const char *of=strstr(phrase, " of ")) {
    string before = string(phrase, of-phrase);
    return plural_word(before.c_str()) + of;
  }
  return plural_word(phrase);
}

const char *remove_articles(const char *frase) 
{
  if (!strncasecmp(frase, "a ", 2))    return frase+2;
  if (!strncasecmp(frase, "an ", 3))    return frase+3;
  if (!strncasecmp(frase, "the ", 4))    return frase+4;
  if (!strncasecmp(frase, "some ", 5))    return frase+5;
  return frase;
}

string remove_articles(const string &s) {
  return remove_articles(s.c_str());
}

const char *numbertostring(int number)
{
  if (number == 0) return "no";
  if (number == 1) return "one";
  if (number == 2) return "two";
  if (number == 3) return "three";
  if (number == 4) return "four";
  if (number == 5) return "five";
  if (number == 6) return "six";
  if (number == 7) return "seven";
  if (number == 8) return "eight";
  if (number == 9) return "nine";
  if (number == 10) return "ten";
  if (number == 11) return "eleven";
  if (number == 12) return "twelve";
  if (number == 13) return "thirteen";
  if (number == 14) return "fourteen";
  if (number == 15) return "fifteen";
  return "many";
}

const char *numbertostring(int number, MudObject *of)
{
  if (const char *a=of->array_get("num", number)) {
    return a;
  }
  if (number == 0) return "no";
  if (number == 1) return "one";
  if (number == 2) return "two";
  if (number == 3) return "three";
  if (number == 4) return "four";
  if (number == 5) return "five";
  if (number == 6) return "six";
  if (number == 7) return "seven";
  if (number == 8) return "eight";
  if (number == 9) return "nine";
  if (number == 10) return "ten";
  if (number == 11) return "eleven";
  if (number == 12) return "twelve";
  if (number == 13) return "thirteen";
  if (number == 14) return "fourteen";
  if (number == 15) return "fifteen";
  return "many";
}

string plural_name(MudObject *obj, int islong) 
{
  if (const char *p=name(obj, 1, islong))
    return p;
  else {
    const char *n = name(obj, 0, islong);
    if (!n) n = name(obj);
    if (!n) n = "???";
    return plural_phrase(remove_articles(n));
  }
}

string give_number(MudObject *obj, int n, const char *adj, int islong, bool dontstate)
{
  if (n == 1) {
    if (adj) {      
      return ssprintf("%s %s %s", strchr("aeiou", adj[0])?"an":"a", adj, 
		      remove_articles(name(obj, 0, islong)));
    }
    return name(obj, 0, islong, dontstate)?:"";
  }

  if (adj)
    return ssprintf("%s %s %s", numbertostring(n, obj), adj, plural_name(obj, islong).c_str());
  
  return ssprintf("%s %s", numbertostring(n, obj), plural_name(obj, islong).c_str());
}

mudtime_t mudtime(MudObject *location) 
{
  int tz = get_zonepropint(location, "timezone", 0);
  int daylen = get_zonepropint(location, "daylen", -1);

  if (daylen == 0) {
    struct tm *t = gmtime(&now);

    mudtime_t tm;
    tm.hour = t->tm_hour;
    tm.min  = t->tm_min;
    return tm;
  }

  if (daylen != -1) {
    mudtime_t tm;
    tm.hour = ((now/daylen)+tz)%24;
    tm.min  = ((now % daylen)*60)/daylen;
    return tm;
  }

  return (mudtime_t){12, 0};
  
}

struct imperial_weight {
  int dram;
  int oz;
  int lb;
  int st;
  int cwt;
  int ton;

  imperial_weight(double ounces) {
    dram = (int)(ounces*16);

    oz = dram / 16;
    dram = dram % 16;    

    lb = oz / 16;
    oz = oz % 16;

    st = lb / 14;
    lb = lb % 14;

    cwt = st / 8;
    st = st % 8;

    ton = cwt / 20;
    cwt = cwt % 20;
  }
};

string format_grams(int m2, MudObject *whofor) {
  char foo[100];

  int m = m2;

  /* 1KG = 35.274 Ounces */
  if (streq(whofor->get("measures"), "imperial")) {
    double m3 = (m * 35.274)/KILOGRAM;    
    imperial_weight iw(m3);
    if (iw.ton)
      sprintf(foo, "^W%i ton %i cwt^n", iw.ton, iw.cwt);
    else if (iw.cwt)
      sprintf(foo, "^W%i cwt %i st", iw.cwt, iw.st);
    else if (iw.st)
      sprintf(foo, "^W%i st %i lb^n", iw.st, iw.lb);
    else if (iw.lb)
      sprintf(foo, "^W%i lb %i oz^n", iw.lb, iw.oz);
    else if (iw.oz)
      sprintf(foo, "^W%i oz %i dram^n", iw.oz, iw.dram);
    else if (iw.dram)
      sprintf(foo, "^W%i dram^n", iw.dram);
    else 
      sprintf(foo, "nothing");
    
    return foo;
  }

  //  m *= HECTOGRAM;

  if (m > KILOTON) {
     m /= TON;
     sprintf(foo, "^W%i.%03i^n kt", m / 1000, m % 1000);
  } else if (m > TON) {
    m /= KILOGRAM;
    sprintf(foo, "^W%i.%03i^n t", m / 1000, m % 1000);
  } else if (m > KILOGRAM) {
    sprintf(foo, "^W%i.%03i^n kg", m / KILOGRAM, m % KILOGRAM);
  } else {
    sprintf(foo, "^W%i^n g", m);
  }
  return foo;
}

char *pronoun[PRONOUNS][GENDERS] =
{
/* male       female     plural        neuter    androgynous  unknown   2nd singular 2nd plural */
  {"he",      "she",     "they",       "it",     "ey",     "[he]",      "you",      "you" }, 
  {"him",     "her",     "them",       "it",     "em",     "[him]",     "you",      "you", },
  {"his",     "her",     "their",      "its",    "eir",    "[his]",     "your",     "your"}, 
  {"his",     "hers",    "theirs",     "its",    "eirs",   "[his]",     "yours",    "yours" },
  {"himself", "herself", "themselves", "itself", "emself", "[himself]", "yourself", "yourselves"},
  {"man",     "woman",   "people",     "thing",  "person", "[man]",     "person",   "people"},
};

static gender_t
char_to_gender (char abbrv)
{
	switch (toupper(abbrv)) {
	 case 'M' : return GENDER_MALE;
	 case 'F' : return GENDER_FEMALE;
	 case 'P' : return GENDER_PLURAL;
	 case 'A' : return GENDER_ANDRO;
	 default: return GENDER_NEUTRAL;
	}
}

gender_t get_gender(const MudObject *what) {
	if (!what) return GENDER_UNKNOWN;
	if (what->get_flag(FL_PLURAL))
	  return GENDER_PLURAL;
	const char *gender = what->get("gender");
	if (gender) return char_to_gender(*gender);
	return GENDER_NEUTRAL;
}

string printed_name(MudObject *consumer, MudObject *who, int magic) {
  if (!who) return "[name]";

  const char *n = name(who);
  if (!n) n = who->get("short");
  if (!n) n = who->id;

  if (magic && who==consumer) return "you";

  if (!consumer) 
    return n;
  if (visible_to(consumer, who)) {
    if (invis(who))    
      return (string)"^n("+name(who)+"^n)";
    else
      return n;
  }

  return "^pSomeone^n";
}

string printed_name_aber(MudObject *consumer, MudObject *who) {
  if (!who) return "[name]";
  return name(who);
}

string
sprintf_c(int cap, const char *format, PRINT_ARGS)
{
  string buffer;
  buffer = formatprint(NULL, format, GET_PARMS());
  if (cap) {
    int idx = 0;
    if (buffer[0]=='^') idx+=2;
    buffer[idx] = toupper(buffer[idx]);
  }
  return buffer;
}

static string change_article(MudObject *who, MudObject *what, const char *oname, const char *art)
{
  bool worn = what->get_object(KEY_WORNBY)==who;

  if (worn && strncmp(oname, "a pair of ", 10)==0) {
    return ssprintf("%s %s", art, oname+10);
  }
  if (strncmp(oname, "a ", 2)==0) {
    return ssprintf("%s %s", art, oname+2);
  }
  if (strncmp(oname, "an ", 2)==0) {
    return ssprintf("%s %s", art, oname+3);
  }
  if (strncmp(oname, "some ", 5)==0) {
    return ssprintf("%s %s", art, oname+5);
  }
  return oname;
}

string _possess(MudObject *who, const char *str, MudObject *what) {
  if (!what) return "???";

  if (what->owner==who) {
    return change_article(who, what, str, his_or_her(who));
  } else {
    return change_article(who, what, str, "the");
  }
}

string _mine(MudObject *who, const char *str, MudObject *what) {
  if (!what)
    return "???";

  if (what->owner==who) {
    return change_article(who, what, str, "your");
  } else {
    return change_article(who, what, str, "the");
  }
}

static string _mine(MudObject *who, MudObject *what) {
  return _mine(who, name(what), what);
}

#define mine(a, b) _mine(a, b).c_str()
#define possess(a, b) _mine(a, b).c_str()

string
sprinta (MudObject *consumer, const char *format, MudObject *sender, MudObject *target, 
	 const char *text, int aber, MudObject *prop)
{
  int cap = 0;
  int always_3rdperson = 0;
  
  MudObject *ref=0;
  
  set<MudObject *> mentioned;
  mentioned.insert(sender);
  
  string buffer = "";

  string restricts = "";

  bool likerestrict = 0;

  while (*format) {

    int pnoun = -1;
    int gender = -1;
    
    string candidate;
    
    if (restricts.length() && *format==')') {
      restricts = "";
      if (likerestrict)
	break;
      format++;
      continue;
    }
    
    if (*format!='%') {
      candidate = *format;
      format++;
      goto docandy;
    }
    
    cap = buffer.length()==0;
    always_3rdperson = 0;
    format++;
    
  again:
    switch (*format) {
      
    case '#':
      cap = 1;
      format++;
      goto again;
      
    case '|':
      always_3rdperson = 1;
      format++;
      goto again;
      
    case '*':
      restricts = "";
      format++;
      while (*format && *format!='(') {
	restricts += *format;
	format++;
      }
      if (consumer) {
	likerestrict = 1;
	const char *restrict = restricts.c_str();
	while (*restrict) {
	  if (*restrict=='B' && cansee(consumer)) likerestrict = 0;
	  if (*restrict=='D' && canhear(consumer)) likerestrict = 0;
	  if (*restrict=='M' && consumer->get_flag(FL_DUMB)) likerestrict = 0;
	  if (*restrict=='H' && !wf_wears_with_flag(consumer, FL_HANDSTIED)) likerestrict = 0;
	  
	  restrict++;
	}
      }
      break;
      
    case 'w':
      if (prop && prop->get("short"))
	candidate = sprintf_c(cap, "^o%s^n", prop->get("short"));
      else
	candidate = sprintf_c(cap, "[name]");
      break;
      
    case 'W':
      if (prop && is_person(prop->owner) && mentioned.find(prop->owner)
	  == mentioned.end() && prop->owner->owner == consumer->owner &&
	  prop->owner != consumer)
	{
	  candidate = sprintf_c(cap, "%s's %s", name(prop->owner),
				remove_articles(name(prop)));
	} else
	  if (prop && name(prop))
	    if (prop->owner==consumer && aber==2)
	      candidate = sprintf_c(cap,  "%s", mine(consumer, prop));
	    else if (prop->owner==ref)
	      candidate = sprintf_c(cap,  "%s", 
				    _possess(prop->owner, name(prop), prop).c_str());
	    else
	      candidate = sprintf_c(cap,  "%s", name(prop));
	  else {
	    candidate = sprintf_c(cap, "the [name]");
	  }
      break;
      
    case 's':
      if (text)
	candidate = sprintf_c(cap, "%s", text);
      else
	candidate = sprintf_c (cap, aber==1?printed_name_aber 
			       (consumer, sender).c_str():
			       printed_name (consumer, sender, aber==2).c_str());
	
      candidate += '\2';
      break;
      
    case '[': {
      format++;
      int plur = 0;
      if (aber==2 && consumer==sender && !always_3rdperson)
	plur = 1;
	if (sender && sender->get_flag(FL_PLURAL))
	  plur = 1;
	int seens = 0;
	char *end = strchr(format, ']');
	if (!end) {
	  buffer += "^Rmissing ^n]^R!";
	  return buffer;
	}
	while (*format && *format!=']') {
	  if (*format=='/') 
	    seens=1;
	  else if (plur == seens) {
	    candidate +=  *format;
	  }
	  format++;
	}
	break;
    }
    
    case '<': {
      format++;
      int plur = 0;
      if (aber==2 && consumer==target && !always_3rdperson)
	plur = 1;
      if (target && target->get_flag(FL_PLURAL))
	plur = 1;
      int seens = 0;
      char *end = strchr(format, '>');
      if (!end) {
	buffer += "^Rmissing ^n>^R!";
	return buffer;
      }
      while (*format && *format!='>') {
	if (*format=='/') 
	  seens=1;
	else if (plur == seens) {
	  candidate += *format;
	}
	format++;
      }
      break;
    }
    
    case '1' :
      if (!ref) ref = sender;
      mentioned.insert(sender);
      
      if (aber==2 && consumer==sender && format[1]=='\'' && format[2]=='s' && !always_3rdperson) {
	candidate = cap?"Your":"your";
	format+=2;
	break;
      }
      
      
      candidate = sprintf_c (cap, aber==1
			     ?printed_name_aber (consumer, sender).c_str():
			     printed_name (consumer, sender, always_3rdperson?0:aber==2).c_str());
      
      break;
    case 'a':
      if (!ref) ref = sender;
      mentioned.insert(target);
      
      if (aber==2 && consumer==target && format[1]=='\'' && format[2]=='s' && !always_3rdperson) {
	candidate = cap?"Your":"your";
	format+=2;
	break;
      }
      
      candidate = sprintf_c (cap, aber==1?printed_name_aber (consumer, target).c_str():
			     printed_name (consumer, target, always_3rdperson?0:aber==2).c_str());
      break;
    case '%':
      candidate = '%';
      break;
      
    case '2' : case '3' : case '4' : case '5' : case '6': case '7' :
      
      gender = get_gender (sender);
      pnoun = *format - '2';
      
      if (consumer && sender && !visible_to(consumer, sender)) 
	gender = GENDER_PLURAL;
      
      if (aber==2 && sender==consumer && !always_3rdperson)
	gender = consumer->get_flag(FL_PLURAL)?GENDER_YOU_P:GENDER_YOU_S;
      
      /* FALL THROUGH */
      
    case 'b' : case 'c' : case 'd' : case 'e' : case 'f': case 'g':
    case 'B' : case 'C' : case 'D' : case 'E' : case 'F': case 'G':
      
      if (gender == -1) {
	gender = get_gender (target);
	
	if (consumer && target && !visible_to(consumer, target)) 
	  gender = GENDER_PLURAL;
	
	pnoun = tolower(*format) - 'b';
	if (aber==2 && consumer==target && !always_3rdperson)
	  gender = consumer->get_flag(FL_PLURAL)?GENDER_YOU_P:GENDER_YOU_S;
      }
      
      string tmp = pronoun[pnoun][gender];
      if (cap) {
	if (tmp[0]=='[')
	  tmp[1] = toupper(tmp[1]);
	else
	  tmp[0] = toupper(tmp[0]);
      }
      
      candidate = tmp;
    }
    
    if (*format)
      format++;
    
  docandy:
    if (!restricts.length() || likerestrict)
      buffer += candidate;
  }
  
  return buffer;
}

const char *xname(const char *id) {
	MudObject *o = planet->get(id);
	if (!o) return id;
	return name(o);
}

string
build_setin (MudObject *mynum, 
	     const char *q, const char *n, const char *d, 
	     const char *v)
{
  if (!n) n = "<name>";
  if (!v) v = "<victim>";
  if (!d) d = "<dir>";
  string p = "";
  while (*q) {
    if (*q != '%')
      p += *q++;
    else {
      switch (*++q) {
      case 'n':
	p += n;
	break;
      case 'v':
	p += n;
	break;
      case 'd':
	p += d;
	break;
      case 0:
	--q;
	break;
      default:
	;
      }
      ++q;
    }
  }

  return ssprintf("%#s", p);
}

static const char *def_setmout = "%n is displaced elsewhere.";
static const char *def_setmin = "%n is displaced here.";
static const char *def_setvin = "%n suddenly appears!";
static const char *def_setvout = "%n has vanished!";
static const char *def_setqin = "%n enters the game.";
static const char *def_setqout = "%n leaves the game.";
static const char *def_setsit = "%n, sitting here.";
static const char *def_setstand = "%n, standing here.";
static const char *def_setsleep = "%n is sleeping here.";

static const char *plu_setmout = "%n are displaced elsewhere.";
static const char *plu_setmin = "%n are displaced here.";
static const char *plu_setvin = "%n suddenly appear!";
static const char *plu_setvout = "%n have vanished!";
static const char *plu_setqin = "%n enter the game.";
static const char *plu_setqout = "%n leave the game.";
static const char *plu_setsit = "%n, sitting here.";
static const char *plu_setstand = "%n, standing here.";
static const char *plu_setsleep = "%n are sleeping here.";

static const char *def_set[] = {
	def_setmin, def_setmout, def_setvin, def_setvout, def_setqin, def_setqout, 
        def_setsit, def_setstand, def_setsleep
};

static const char *plu_set[] = {
	plu_setmin, plu_setmout, plu_setvin, plu_setvout, plu_setqin, plu_setqout, 
        plu_setsit, plu_setstand, plu_setsleep
};

static const char *prop_name[] = {
    "setmin", "setmout", "setvin", "setvout", "setqin",
    "setqout", "setsit", "setstand", "setsum", "setsin", "setsout", "setsleep",
};

const char *get_message(MudObject *who, setin_t which) {
	const char *msg = who->get(prop_name[which]); 

	if (msg && (strstr(msg, "%n") || !is_player(who))) 
	  return msg;

	if (linkdead(who) && which==setqout)
	  return who->get_flag(FL_PLURAL)?"The cardboard cutouts of %n leave the game."
	    :"The cardboard cutout of %n leaves the game.";

	if (who->get_flag(FL_PLURAL))
	  return plu_set[which];
	else
	  return def_set[which];
}

char * howsoon(time_t when, bool secs) {
  static char boo[256];
  char *foo = boo;
  boo[0] = 0;
  when -= now;
  
  if (when <= 0)
	return "Now";

  if (when > DAY) {
    int days = when / DAY;
    foo += sprintf(foo, " %i day", days);
    if (days > 1)
      foo += sprintf(foo, "s");
    when %= DAY;
  }

  if (when > HOUR) {
    int days = when / HOUR;
    foo += sprintf(foo, " %i hour", days);
    if (days > 1)
      foo += sprintf(foo, "s");
    when %= HOUR;
  }

  if (when > MINUTE) {
    int days = when / MINUTE;
    foo += sprintf(foo, " %i minute", days);
    if (days > 1)
      foo += sprintf(foo, "s");
    when %= MINUTE;
  }

  if (when && secs) {
    foo += sprintf(foo, " %i second", (int)when);
    if (when > 1)
      foo += sprintf(foo, "s");
  }

  return boo+1;
}


const char *was_or_were(MudObject *of) {
  if (of->get_flag(FL_PLURAL))
    return "were";
  return "was";
}



string nicetime(int secs)
{
  string s = "";
  int hr = (secs / 3600);
  int mn = (secs / 60) % 60;
  int sc = (secs % 60);

  if (hr==1) {
    s += "1 hour";
  } else if (hr) {
    s += ssprintf("%i hours", hr);
  }

  if (mn) {
    if (hr)
      s += ", ";
    if (mn==1) {
      s += "1 minute";
    } else {
      s += ssprintf("%i minutes", mn);
    }
  }

  if (sc) {
    if (mn||hr) 
      s += ", ";
    if (sc==1) {
      s += "1 second";
    } else {
      s += ssprintf("%i seconds", sc);
    }
  }
  return s;
}

string possibly_drunkify(MudObject *who, string what2)
{
  {
    const char *s = what2.c_str();
    int upper = 0;
    int lower = 0;
    while (*s) {
      if (isupper(*s))
	upper++;
      if (islower(*s))
	lower++;
      s++;
    }
    if (upper>=10 && lower==0) {
      what2 = make_lower(what2.c_str());
    }
  }

  if (who->get_int("literate", 1)==0) {
    what2 = " " + what2 + " ";
    const char *s = what2.c_str();
    string w = "";
    int last = 0;
    while (*s) {
      static char *literacy_table[] = 
      {
	" b4", "before",
	" b",   "be",
	" 4",   "for",
	" 2",   "to",
	" 1",   "one",
	" u",   "you",
	" r",   "are",
	" i",   "I",
	" ill",   "I'll",
	" no",   "know",
	" ive",   "I've",
	" cum", "come",
	" nos", "knows",
	" wots", "what's",
	" wen", "when",
	" lol", "oh that was so funny",
	" cant", "can't",
	" ppl", "people",
	" tlk", "talk",
	" tlking", "talking",
	" dam", "damn",
	" g2g", "got to go",
	" txt", "text",
	" thanx", "thank-you, kind sir",
	" wot", "what",
	" ne 1", "anyone",
	" ne1", "anyone",
	" every1", "everyone",
	" sum", "some",
	" sumtime", "sometime",
	" y", "why",
	" yer", "yeah",
	" ys", "why's",
	" sum1", "someone",
	" sum 1", "someone",
	NULL,
      };

      int lt = 0;
      while (literacy_table[lt]) {
	if (!strncmp(s, literacy_table[lt], strlen(literacy_table[lt]))
	    && !isalpha(s[strlen(literacy_table[lt])])) {
	  w += " ";
	  w += literacy_table[lt+1];
	  s += strlen(literacy_table[lt]);
	  goto blah;
	}
	lt+=2;
      }

      if (last == *s && (last == '!' || last == '?')) {
	s++;
	goto blah;
      }

      w += *s;
      last = *s;
      s++;
    blah:;
    }
    w = w.substr(1, w.length()-2);

    what2 = w;
  }

  if (is_player(who) && who->get_int("$pigged")>0) {
    const char *ptype = who->get("$piggy");
  
    for (size_t i=0;animals[i].adj;i++) {
      if (streq(animals[i].adj, ptype) && animals[i].noise)
	return animals[i].noise;
    }
  }

  if ((who->get_int(KEY_PISSED))<(now+120)) {
    return what2;
  }  

  int needed = who->get_int(KEY_PISSED)-now;

  needed -= 120;

  if (needed>1000)
    needed = 100;
  else
    needed /= 10;

  int booze = who->get_int(KEY_PISSED, now);
  if (booze - now > 135*12 && randperc() < 10) {
    who->printf ("You let everyone know what you had for ^Rlunch^n.\n");
    who->oprintf(canhear, "%#M ^R%[B U R P S/B U R P]^n loudly^n.\n", who);       
  }

  const char *what = what2.c_str();

  string tmp = "";
  while (*what) {
    if (random_number(100)<=needed)
      if (*what=='s' && what[1]=='h')
	tmp += "s";
      else if (*what=='s')
	tmp += "sh";
      else if (*what=='c' && what[1]=='e')
	tmp += "sh";
      else if (*what=='c' && what[1]=='i')
	tmp += "sh";
      else if (*what=='r')
	tmp += "w";
      else
	tmp += *what;
    else
      tmp += *what;

    what++;
  }
  return tmp;
}



string title_for(string a, int width, const colourinfo_t &ci, const char *col,
		 const char *chr) {
  string f = col;
  if (!chr)
    chr = "^=";
  f += chr;
  f += chr;
  f += " ^W" +a + " ";
  f += col;
  width -= colour_strlen(a.c_str(), ci);
  width -= 4;
  while (width>0) {
    f+= chr;
    width--;
  }
  f += "^n";
  return f;
}

string title_for(string a, const MudObject *who, const char *col,
		 const char *chr) {
  return title_for(a, columns(who)-1, colinfo(who), col, chr);
}

string footer_for(int width, const char *col, const char *chr) {
  string f = col;;
  if (!chr) 
    chr = "^=";
  while (width>0) {
    f += chr;
    width--;
  }
  f += "^n";
  return f;
}

string footer_for(MudObject *who, const char *fmt, const char *chr) {
  return footer_for(columns(who)-1, fmt, chr);
}

int strpos(const char *foo, char a) {
  char *b = strchr(foo, a);
  if (!b)
    return -1;
  return b-foo;
}

void mud_fprintf(MudObject *to, FILE *f, const char *fmt, PRINT_PARMS) {
  string buf = formatprint(NULL, fmt, GET_PARMS());
  fprintf(f, "%s", buf.c_str());
  return;
               
}

string lookup_name(const char *who) {
  MudObject *wh = planet->get(who);
  if (wh && (!is_player(wh) || !invis(wh))) {
    return wh->get("name");
  }

  FILE *f = xopen(VARDATA, "namelist", "r");
  if (f) {
    while (1) {
      char buffer[1024];
      fgets(buffer, sizeof buffer, f);
      if (feof(f)) break;
      char *pname = strchr(buffer, ' ');
      if (!pname) break;
      *pname = 0;
      pname++;
      *strchr(pname, '\n')=0;
      
      if (streq(buffer, who)) {
	xclose(f);
	return pname;
      }
    }
    xclose(f);
  }
  
  return ssprintf("^p%#s^n", who);
}


string lookup_name(string who) {
  return lookup_name(who.c_str());
}



const char *room_name(MudObject *where, MudObject *from) {
  if (from && from->get_flag(FL_OUTDOORS)) {
    if (const char *outname = where->get("building"))
      return outname;
  }

  const char *room = where->get("room_name");
  
  if (!room)
   room = name(where);

  if (!room)
    room = "???";

  return room;
}

string ssprintf(const char *fmt, PRINT_PARMS) {
  string foo = formatprint(NULL, fmt, GET_PARMS());
  return foo;
}

string format_roomname(MudObject *room, MudObject *o, const char *ic, const char *oc, const char *notin, int yesid) 
{
  string s = format_roomname(room, o, ic, oc, notin);
  if (yesid) {
    if (ic && *ic) s += " ^D(^d";
    else s += " (";
    s += room->id;
    if (ic && *ic) s += "^D)^n";
    else s += ")";
  }
  return s;
}

string format_roomname(MudObject *room, MudObject *o, const char *ic, const char *oc, const char *notin, const char *myprep) 
{
  if (!notin || !*notin)
    notin = "in";

  if (!myprep || !*myprep)
    myprep = 0;

  const char *noton = "on", *notat = "at";
  if (streq(notin, "to"))
    noton = "onto";

  if (streq(notin, "to"))
    notat = "to";

  string b="";

  if (o && room->get_flag(FL_BROKEN) && o->get("$camefrom")) {
    b = ssprintf("the %s side of ", o->get("$camefrom"));
  }

  const char *between=b.c_str();

  const char *rn = room_name(room);
  if (!rn) {
    return "";
  }

  int raw = 0;

  if (is_player(room) || is_mobile(room)) {
    raw = IN;
  }

  if (strncasecmp(rn, "a ", 2)==0)
    raw = IN;
  if (strncasecmp(rn, "an ", 3)==0)
    raw = IN;
  if (strncasecmp(rn, "the ", 4)==0)
    raw = IN;
  if (strncasecmp(rn, "at the ", 7)==0)
    raw = IN;
  
  if (strncasecmp(rn, "by ", 3)==0)
    raw = DIRECT;
  if (strncasecmp(rn, "at ", 3)==0)
    raw = DIRECT;
  if (strncasecmp(rn, "on ", 3)==0)
    raw = DIRECT;
  if (strncasecmp(rn, "in ", 3)==0)
    raw = DIRECT;
  if (strncasecmp(rn, "inside ", 7)==0)
    raw = DIRECT;
  if (strncasecmp(rn, "behind ", 7)==0)
    raw = DIRECT;
  if (strncasecmp(rn, "outside ", 8)==0)
    raw = DIRECT;
  if (strncasecmp(rn, "under ", 6)==0)
    raw = DIRECT;

  string rn2 = rn;
  
  if (raw) {
    rn2[0] = tolower(rn2[0]);
    rn = rn2.c_str();
  }

  if (strstr(rn, " Street"))
    raw = IN;

  if (room->get_int("namestyle")!=-1) {
    raw = room->get_int("namestyle");
    if (streq(myprep, "over"))
      raw = OVER;
  }
  
  switch (raw) {
  case DIRECT:
    return ssprintf("%s%s%s", ic, rn, oc);
    break;
  case OVER:
    return ssprintf("%s %s%s%s", myprep, ic, rn, oc);
    break;
  case IN:
  case 100:
    return ssprintf("%s %s%s%s%s", notin, between, ic, rn, oc);
    break;
  case ON:
    return ssprintf("%s %s%s%s%s", noton, between, ic, rn, oc);
    break;
  case ON_THE:
    return ssprintf("%s %sthe %s%s%s", noton, between, ic, rn, oc);
    break;
  case BY:
    return ssprintf("by %s%s%s%s", between, ic, rn, oc);
    break;
  case BY_THE:
    return ssprintf("by %sthe %s%s%s", between, ic, rn, oc);
    break;
  case AT:
    return ssprintf("%s %s%s%s%s", notat, between, ic, rn, oc);
    break;
  case AT_THE:
    return ssprintf("%s %sthe %s%s%s", notat, between, ic, rn, oc);
    break;
  case ON_BOARD:
    return ssprintf("on board %s%s%s%s", between, ic, rn, oc);
    break;
  case IN_THE:
  default:
    return ssprintf("%s %sthe %s%s%s", notin, between, ic, rn, oc);
    break;
  case ON_OR_BY:
    if (o && o->get_flag(FL_SWIMMING)) {
      return ssprintf("by the %s%s%s", ic, rn, oc);
    } else {
      return ssprintf("on the %s%s%s", ic, rn, oc);
    }
    break;
  case ON_OR_IN:
    if (o && o->get_flag(FL_SWIMMING)) {
      return ssprintf("%s the %s%s%s", notin, ic, rn, oc);
    } else {
      return ssprintf("on the %s%s%s", ic, rn, oc);
    }
  }  
}

void rmstuff(World<MudObject> &what, int &items, string &s, const char *itemstr, const char *cat)
{
  NewWorld clothing;
  NewWorld ctypes;
  MudObject *o;
  int i;
  foreach((&what), o, i) {
    if (category_match(o, cat)) {
      clothing.add(*o);

      MudObject *p = o->get_object("cloneof");
      if (!p)
	p = o;

      if (!ctypes.get(p->id)) {
	ctypes.add(*p);
      }
    }
  }

  if (ctypes.getsize()>2) {
    foreach((&clothing), o, i) {
      what.remove(*o);
    }
    if (s.length()) {
      s += ", ";
    }
    s += numbertostring(clothing.getsize());
    s += itemstr;
    items++;
  }

}

string magiclist(const World<MudObject> &w2, displayname_t r, int further) {
  MudObject *o;
  int i;

  World<MudObject> what = w2;

  int items = 0;
  string s = "";
  
  if (further) {
    rmstuff(what, items, s, " items of clothing", "clothing");
    rmstuff(what, items, s, " pieces of jewelry", "jewelry");
    rmstuff(what, items, s, " items of food", "food");
    rmstuff(what, items, s, " drinks", "drink");
  }

  map<string, int> m;
  map<string, string> plu;
  foreach((&what), o, i) {
    const char *n = 0;
    string ns;

    if (r) {
      ns = r(o);
      n = ns.c_str();
    } else {
      n = name(o);
      if (!n)
	n = o->id;
    }

    if (!n)
      continue;
    
    if (m.find(n)!=m.end()) {
      m[n] = m[n] + 1;
    } else
      m[n] = 1;
    
    if (name(o, 1) && plu.find(n)==plu.end()) {
      plu[n] = name(o, 1);
    }
  }
  
  map<string, int>::iterator it = m.begin();
  while (it != m.end()) {
    
    string ph = it->first;
    const char *num = "";
    if (it->second != 1) {
      ph = plural_phrase(remove_articles(ph.c_str()));
      if (plu.find(it->first)!=plu.end()) {
	ph = plu.find(it->first)->second;
      }
      num = numbertostring(it->second);
    }
    
    if (items == 1)
      s = " and " + s;
    else if (items)
      s = ", " + s;
    
    s = ph + s;
    if (num[0]) {
      string t = num;
      t += " ";
      s = t + s;
    }

    it++;
    items++;
  }  
  return s;
 
}

int unisuits[] = { 0x2660, 0x2665, 0x2663, 0x2666 };

string describe_card(int which, int lon) 
{
  if (which<0) return "unknown card";
  if (which>=52) return "[]";
  int suit = which / 13;
  int val = which % 13;
  static const char *suits[] = { "spade", "heart", "club", "diamond" };
  const char *cols = "wrwr";
  static const char *vals[] = { "ace", "2", "3", "4", "5", "6", "7", "8",
				"9", "ten", "jack", "queen", "king" };
  const char *shortvals = "A23456789TJQK";
  
  if (lon)
    return ssprintf("the %s of %ss", vals[val], suits[suit]);

  return ssprintf("^%c^#%i;^n%c", cols[suit], unisuits[suit],
		  shortvals[val]);
}


string descmob(MudObject *obj, bool col)
{
  if (!is_person(obj))
    return "";
  string s;
  const char *b=obj->get("body");
  if (!b) b = "human";
  s += col?is_player(obj)?"a ^p":"a ^P":"a ";
  if (streq(b, "human")) {
    MudObject *wf = wornfrom(obj);
    int notop = 0, nolegs = 0;
    if (!worn_on(obj, "body", 0) && (!wf || worn_on(obj, "body", 0)))
      notop = 1;
    if (!worn_on(obj, "legs", 0) && (!wf || worn_on(obj, "legs", 0)))
      nolegs = 1;
    if (notop && nolegs)
      s += "naked ";
    else if (notop)
      s += "topless ";
  }
  if (streq(b, "human")) {
    gender_t g = get_gender(obj);
    if (g==GENDER_MALE) s += "male ";
    if (g==GENDER_FEMALE) s += "female ";
  }
  s += b;
  string r = role(obj);
  if (r.length()) {
    s += " ";
    s += r;
  }
  if (col)
    s += "^n";
  return s;
}

string role(MudObject *who) {
  if (who->get_flag(FL_POLICE)) {
    return "constable";
  }
  if (is_player(who)) {
    string s = get_long_rank(who);
    for (size_t i=0;i<s.length();i++) {
      s[i] = tolower(s[i]);
    }
    return s;
  }
  const char *r = who->get("job");
  if (!r) r = who->get("role");
  if (!r && who->owner && who->owner->get_object("shopmob")==who)
    r = "shopkeeper";
  return r?r:"serf";
}


string owhere(MudObject *who, MudObject *room)
{
  if (!who->owner)
    return "is nowhere";

  if (!room) {
    room = who->owner;
    while (room && room->owner && !room->get_flag(FL_ROOM))
      room = room->owner;
    if (!room->get_flag(FL_ROOM)) {
      room = who->owner;
      while ((is_mobile(room) || is_player(room)) && room->owner) {
	room = room->owner;
      }
    }
  }

  MudObject *owner = who->owner;
  if (streq(owner->id, "empty"))
    return "in internal storage";

  string rn = format_roomname(room, who, "^{D", "^}");
  if (owner == room) {
    string s = "";
    s += rn;
    s += " ^d(";
    s += room->id;
    s += ")^n";
    return s;
  }
  if (is_mobile(owner) || is_player(owner)) {
    string s = "carried by ";
    if (iswornby(who, owner)) {
      s = "worn by ";
    }
    if (owner->get_object(KEY_WIELD)==who) {
      s = "wielded by ";
    }
    s += owner->get("name");
    s += " ";
    s += rn;
    return s;
  }
  if (owner->get_flag(FL_CONTAINER)) {
    string s = "inside ";
    s += owner->get("name");
    s += " ";
    s += owhere(owner, room);
    return s;
  }
  return "";
}

const char *name(const MudObject *o, int plural, bool islong, bool dontstate) 
{
  if (!islong) {
    if (plural) {
      if (!dontstate) {
	if (const char *q=o->get("!name.plural")) return q;
	string p = ssprintf("name.%i.plural", o->get_int(KEY_STATE, 0));
	if (const char *q = o->get(p.c_str())) return q;
      }
      if (const char *q=o->get("name.plural")) return q;
    } else {
      if (!dontstate) {
	if (const char *q=o->get("!name")) return q;
	string p = ssprintf("name.%i", o->get_int(KEY_STATE, 0));
	if (const char *q = o->get(p.c_str())) return q;
      }
      if (const char *q=o->get("name")) return q;
    }
  } else {
    if (plural) {
      if (!dontstate) {
	if (const char *q=o->get("!long.plural")) return q;
	string p = ssprintf("long.%i.plural", o->get_int(KEY_STATE, 0));
	if (const char *q = o->get(p.c_str())) return q;
      }
      if (const char *q=o->get("long.plural")) return q;
    } else {
      if (!dontstate) {
	if (const char *q=o->get("!long")) return q;
	string p = ssprintf("long.%i", o->get_int(KEY_STATE, 0));
	if (const char *q = o->get(p.c_str())) return q;
      }
      if (const char *q=o->get("long")) return q;
    }
    return name(o, plural, 0, dontstate);
  }
  return 0;
}

string mud_sprintf(MudObject *to, const char *fmt, PRINT_PARMS) {
  return formatprint(to, fmt, GET_PARMS());
}

int columns(const MudObject *who)
{
  const Player *p = dynamic_cast<const Player*>(who);
  if (!p)
    return 80;

  int c = p->get_int("columns", 80);

  if (p->p && p->p->col && !p->get_flag(FL_NONAWS))
    c = p->p->col;

  return c <? 320;
}

int rows(const MudObject *who)
{
  const Player *p = dynamic_cast<const Player*>(who);
  if (!p)
    return 24;

  int c = p->get_int("rows", 24);

  if (p->p && p->p->row && !p->get_flag(FL_NONAWS))
    c = p->p->row;

  return c <? 200;
}