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 - Equipment 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 <stdio.h>

#include "musicmud.h"
#include "verbs.h"
#include "State.h"
#include "events.h"
#include "flags.h"
#include "misc.h"
#include "pflags.h"
#include "vsprintf.h"
#include "trap.h"
#include "shared.h"
#include "match.h"
#include "msi.h"
#include "href.h"
#include "body.h"
#include "nations.h"

#include <list>
#include <vector>
#include <algorithm>

#define WORNLEVEL 5

#define MODULE "equipment"

static inline bool is_fillable(MudObject *o) {
  return o->get_flag(FL_WATERTIGHT);
}

static string tobodyloc(string a) {
  for (int i=0;i<pairs_n;i++) {
    if (streq(pairs[i].lnice, a.c_str())) return pairs[i].left;
    if (streq(pairs[i].rnice, a.c_str())) return pairs[i].right;
  }
  for (int i=0;i<8;i++) {
    if (streq(fingers[i], a.c_str())) {
      return ssprintf("finger.%i", i);
    }
  }
  return a;
}

static const char *fwornon(MudObject *o) {
  const char *a = wornon(o);
  if (streq(a, "body,legs"))
    return "body";
  return a;
}

static void show_containers(MudObject *who, MudObject *qui, string spaces, int brief) {
  NewWorld empties;
  MudObject *o;
  int i;
  foreach(qui->children, o, i) {
    if (!o->get_flag(FL_CONTAINER))
      continue;

    if ((!o->get_flag(FL_STATED) && !o->get_flag(FL_TRANSPARENT)) && state(o)!=0)
      continue;

    NewWorld blah;
    int j;
    MudObject *p;
    foreach(o->children, p, j) if (!p->get_flag(FL_EXIT)) {
      blah.add(*p);
    }
    if (blah.getsize()) {
      if (brief)
	who->printf("^Z%#M %[holds/hold] %W.\n", o, &blah);
      else
	who->printf("%s^Z%M %[holds/hold] %W.\n", spaces.c_str(), o, &blah);
    }
    else {
      if (o->array_size("$fill") == 1) {
	MudObject *fill = o->array_get_object("$fill", 0);
	if (fill) {
	  if (brief)
	    who->printf("^Z%#M %[holds/hold] %s.\n", o, fill->get("short"));
	  else
	    who->printf("%s^Z%M %[holds/hold] %s.\n", spaces.c_str(), o, fill->get("short"));
	  continue;
	}
      }
      empties.add(*o);
    }
    show_containers(who, o, spaces+"  ", brief);
  }
  if (empties.getsize()) {
    if (!brief)
      who->printf("%s^Z%W %s empty.\n", spaces.c_str(), &empties, plural(empties)?"are":"is");
    else
      who->printf("^Z%#W %s empty.\n", &empties, plural(empties)?"are":"is");
  }
}

string andize(const vector<string> &v)
{
  string tmp;
  size_t n = 0;
  iforeach(s, v) {
    n++;
    if (tmp.length())
      if (n==v.size())
	tmp += " and ";
      else
	tmp += ", ";
    tmp += *s;
  }
  return tmp;
}

static bool verb_equipment(MudObject *who, int argc, const char **argv) {
  MudObject *o;
 int i;
  int count=0;
  int brief = 0, isinv = 0;

  MudObject *qui = who;

  //  int isinv = argv[0][0]=='i';
  if (argv[0][0]=='i') {
    isinv = 1;
  }

  if (argv[0][0]=='q') {
    isinv = 1;
    brief = 1;
  }

  fixup_lapels(who, NULL);

  if (who==qui)
    if (who->get_flag(FL_SLEEPING)) {
      who->printf("^YIt's all gone!! ^nOh no, you were dreaming.\n");
      return true;
    }
  
  if (who->get_priv(PFL_GOTO) && argc > 1) {
    qui = get_player(who, argv[1]);
    if (!qui) {
      who->printf("View the equipment of who?\n");
      return true;
    }
  }
  
  if (!isinv || !who->get_flag(FL_NOINVWORN)) {

  MudObject *m = mount(qui);
  
  const char *white = brief?"":"^W";
  
  if (m) {
    const char *riding = m->get("riding_verb");
    if (!riding)
      riding = "riding";
    string c, e;
    make_href(who, m, c, e);
    who->printf("%s%#P%s %[is/are] %s %s%M%s%s.^n\n", white, qui, white,
		riding, c.c_str(), m, e.c_str(), white); 
  }

  if (!brief) {
    who->spec_printf("^W%#P^W %[is/are] wearing...^n\n", qui);
#define ARRAY_SIZE(a) (sizeof(a)/(sizeof((a)[0])))
    static const char *order[] = {
      "allover",
      "head","ears","leftear","rightear","eyes","lefteye","righteye","neck",
      "back","body","lapel",
      "arms","leftarm","rightarm",
      "wrists","leftwrist","rightwrist",
      "hands","lefthand","righthand",
      "thumbs","leftthumb","rightthumb",
      "finger.0", "finger.2","finger.4","finger.6",
      "finger.1","finger.3","finger.5","finger.7",
      "legs","leftleg","rightleg",
      "feet","leftfoot","rightfoot","*",
    };
    
    TeleWorld worn2 = clothes(qui);
    NewWorld realworn;
    for (i=0;i<worn2.getsize();i++)
      realworn.add(*worn2.get_nth(i));
    NewWorld worn = realworn;
    
    for (size_t j=0;j<ARRAY_SIZE(order);j++) {
      for (int k=0;k<=WORNLEVEL;k++) {
	foreach((&worn), o, i) {
	  
	  if (!streq(fwornon(o), order[j]))
	    continue;
	  if (o->get_int("wornlevel", 0)!=k)
	    continue;
	  
	  string provides;
	  
	  if (!isinv) {
	    provides = abilities(o);
	    if (provides.length()) {
	      provides = " (^Z" + provides;
	      provides.erase(provides.length()-1);
	      provides += ")";
	    }
	  }
	  
	  const char *nam = o->get("name");
	  if (nam && strncmp(nam, "a pair of ", strlen("a pair of "))==0) {
	    nam += strlen("a pair of ");
	  }
	  if (nam && strncmp(nam, "some ", strlen("some "))==0) {
	    nam += strlen("some ");
	  }
	  
	  string c, e;
	  make_href(who, o, c, e);
	  
	  const char *on = streq(looknice(o, &realworn), "clothes")?"over":"on";
	  who->printf("  ^Z%s%s%s %s %s %s%s\n", 
		      c.c_str(),
		      nam,
		      e.c_str(),
		      on,
		      who==qui?"your":his_or_her(qui),
		      looknice(o, &realworn),
		      provides.c_str());
	  worn.remove(*o);
	  i--;
	  count++;
	}
      }
    }
    
    foreach((&worn), o, i) {
      string provides;
      
      if (!isinv) {
	provides = abilities(o);
	if (provides.length()) {
	  provides = " (^Z" + provides;
	  provides.erase(provides.length()-1);
	  provides += ")";
	}
      }

      const char *on = streq(looknice(o, &realworn), "clothes")?"over":"on";
      who->printf("  ^Z%M %s %s %s%s\n", 
		  o,
		  on,
		  who==qui?"your":his_or_her(qui),
		  looknice(o, &realworn),
		  provides.c_str());
    }
    
    if (!count) {
      who->cancel_printf();
      
      if (!isinv) {
	who->printf("^W%#P^W %[is/are] not wearing anything.^n\n", qui);
      }
    }
  } else {
    TeleWorld worn2 = clothes(qui);
    NewWorld realworn;
    for (i=0;i<worn2.getsize();i++)
      realworn.add(*worn2.get_nth(i));
    NewWorld worn = realworn;
    if (worn.getsize())
      who->printf("%#P %[is/are] wearing %w.\n", qui, &worn);
    else
      who->printf("%#P %[is/are] not wearing anything.\n", qui);
  }

  if (MudObject *wielded = qui->get_object(KEY_WIELD)) {
    if (wielded->owner == qui) {

      string wtstr="";
      if (!isinv) {
	wtype_t wty = (wtype_t)wielded->get_int("wtype");
	int dam = wielded->get_int("damage");
	const char *damstr = " little damage";
	if (dam>5) damstr = " some damage";
	if (dam>10) damstr = " a fair bit of damage";
	if (dam>15) damstr = " quite a bit of damage";
	if (dam>20) damstr = " a lot of damage";
	if (dam>25) damstr = " lots of damage";
	   
	if (wty == W_IMPACT) wtstr = " (^Zimpact weapon; does";
	if (wty == W_CUTTING) wtstr = " (^Zcutting weapon; does";
	if (wty == W_STABBING) wtstr = " (^Zstabbing weapon; does";
	if (wtstr.length()) {
	  wtstr += damstr;
	  wtstr += ")";
	}
      }

      string c, e;
      make_href(who, wielded, c, e);
      who->printf("%s%#P%s %[is/are] wielding %s%M%s%s^n.\n", white, qui, white,
		  c.c_str(), wielded, e.c_str(), wtstr.c_str());
      count++;
    }
  }
  }
 
  if (isinv) {
    NewWorld stuff;

    MudObject *wi = qui->get_object(KEY_WIELD);
    MudObject *mo = mount(qui);

    foreach(qui->children, o, i) {
      if (!iswornby(o, qui) && o != wi && o != mo) {
	  stuff.add(*o);
      }
    }

    if (brief) {
      list<MudObject*> l;

      World<MudObject> nat = nations(0);
      iforeach(n, nat) 
	if (hascurr(n)) {
	  int amt = cash(qui, n);
	  if (amt) {
	    MudObject *nu = new MudObject("^tmp");
	    nu->set("name", formatcash(amt, n, 0));
	    l.push_back(nu);
	    stuff.add(nu);
	  }
	}

      if (stuff.getsize()) {
	who->printf("%#P %[is/are] carrying %w.\n", qui, &stuff);
      } else {
	who->printf("%#P %[is/are] not carrying anything.\n", qui, &stuff);
      }

      iforeach(m, l)
	delete *m;

    } else {    
      who->spec_printf("^W%#P^W %[is/are] carrying...^n\n", qui);
      
      if (int s=qui->array_size("$card")) {
	extern int unisuits[];
	vector<int> str[4];
	for (int i=0;i<s;i++) {       
	  int card = qui->array_get_int("$card", i);
	  if (card>=0 && card<=51) {
	    int suit = card/13;
	    str[suit].insert(str[suit].end(), card%13);
	  }
	}
	for (int i=0;i<4;i++) {
	  if (str[i].size()) {
	    sort(str[i].begin(), str[i].end());
	    string rst = "";
	    vector<int>::iterator it;
	    const char *val = "A23456789TJQK";
	    for (it=str[i].begin();it!=str[i].end();it++) {
	      rst += val[*it];
	    }
	    who->printf("  ^Z^%c^#%i;^n %s\n", i&1?'R':'W',unisuits[i], 
			rst.c_str());
	  }
	}
	count++;
      }

      map<string, int> m;
      map<string, string> plu;
      foreach((&stuff), o, i) {
	const char *n = name(o);
	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);
	}
	
	who->printf("  ^Z%s%s%s\n", 
		    num, *num?" ":"",
		    ph.c_str());
	it++;
	count++;
      }

      World<MudObject> nat = nations(0);
      vector<string> s;
      iforeach(n, nat) if (hascurr(n)) {
	int amt = cash(qui, n);
	if (amt)
	  s.push_back(formatcash(amt, n, 0));
      }

      if (s.size()) {
	string a = andize(s);
	who->printf("  ^Z%s in cash\n", a);
	count++;
      }

      who->cancel_printf();
    }

    if (!brief) {
      if (who==qui)
	who->spec_printf("^WYour containers...^n\n");
      else
	who->spec_printf("^W%#P^W's containers...^n\n", qui);
    }
    show_containers(who, qui, "  ", brief);
    who->cancel_printf();

    if (!brief && !count) {
      who->printf("^W%#P^W %[is/are] not carrying anything.^n\n", qui);
    }

#ifdef INVSLOTS
    who->printf("%#P %[has/have] %i free inventory slots.\n", qui, freehands(qui));
#endif
  }

  return true;
}

extern MudObject *worn_on(MudObject *by, const char *what, int wol);
extern MudObject *worn_provides(MudObject *who, const char *what);

static int havelimb(MudObject *who, const char *whichlimb)
{
  return who->get_int(ssprintf("b.%s", whichlimb).c_str(), 1);
}

static void wear_obs(MudObject *who, NewWorld what, string loc) 
{
  NewWorld already;
  NewWorld cant, cantthere;
  NewWorld already2, a3;

  MudObject *o;
  int i;

  int healing = 0;

  for (int wl=0;wl<=WORNLEVEL;wl++) {
    foreach((&what), o, i) {
      int wol = o->get_int("wornlevel", 0);
      if (wol != wl)
	continue;

      if (is_worn(o)) {
	already.add(*o);
	what.remove(*o);
	i--;
	continue;
      }

      if (dotrap(E_BEFOREWEAR, who, o)) {
	/* ::: before_wear o1==clothing; after already wearing check. return 1 to abort. */
	what.remove(*o);
	i--;
	continue;
      }

      if (who->get_object(KEY_WIELD)==o && o->get_int("damage")==-1) {
	who->printf("You can't wear %Y as you are already wielding %s.\n", o, it_them(o));
	what.remove(*o);
	i--;
	continue;
      }
      
      const char *wo = o->get("wornon");
      
      if (!wo) {
	cant.add(*o);
	what.remove(*o);
	i--;
	continue;
      }

      if (loc.length()) {
	// If we specified a place to wear it.

	const char *can = wo;
	wo = 0;
	if (streq(can, loc.c_str())) {
	  wo = can;
	}
	 
	for (int j=0;j<pairs_n;j++) {
	  if (streq(can, pairs[j].single) && streq(loc.c_str(), pairs[j].left))
	    wo = pairs[j].left;
	  
	  if (streq(can, pairs[i].single) && streq(loc.c_str(), pairs[j].right))
	    wo = pairs[j].right;
	}
	
	if (streq(can, "limb")) {
	  if (streq(loc.c_str(), "leftarm")) wo = "leftarm";
	  if (streq(loc.c_str(), "leftleg")) wo = "leftleg";
	  if (streq(loc.c_str(), "rightarm")) wo = "rightarm";
	  if (streq(loc.c_str(), "rightleg")) wo = "rightleg";
	}

	if (streq(can, "legs") && wol==0 && streq(loc.c_str(), "head")) {
	  wo = "head";
	  /* NB : Underpants can be worn on the head */
	}
	
	if (streq(can, "finger")) {
	  for (int fn=0;fn<8;fn++) {
	    static char f[10];
	    sprintf(f, "finger.%i", fn);
	    if (streq(f, loc.c_str())) {
	      wo = f;
	      break;
	    }
	  }
	}
	
	if (!wo) {
	  cantthere.add(*o);
	  what.remove(*o);
	  i--;
	  continue;
	}
      }

      if (streq(wo, "limb")) {
	if (!worn_on(who, "leftarm", wol)) wo = "leftarm";
	else if (!worn_on(who, "rightarm", wol)) wo = "rightarm";
	else if (!worn_on(who, "leftleg", wol)) wo = "leftleg";
	else if (!worn_on(who, "rightleg", wol)) wo = "rightleg";
      }

      for (int p = 0;p<pairs_n;p++) {
	if (streq(wo, pairs[p].single)) {
	  if (!worn_on(who, pairs[p].left, wol) && havelimb(who, wo))
	    wo = pairs[p].left;
	  else 
	    wo = pairs[p].right;
	}
	
	if (streq(wo, pairs[p].both)) {
	  int hl = havelimb(who, pairs[p].left);
	  int hr = havelimb(who, pairs[p].right);

	  if (!hl && !hr) {
	    who->printf("You have no %s.\n", looknice(pairs[p].both));
	    what.remove(*o);
	    i--;
	    goto dontwear;
	  }

	  if (!hl) {
	    who->printf("You have no %s.\n", looknice(pairs[p].left));
	    what.remove(*o);
	    i--;
	    goto dontwear;
	  }

	  if (!hr) {
	    who->printf("You have no %s.\n", looknice(pairs[p].right));
	    what.remove(*o);
	    i--;
	    goto dontwear;
	  }
	}
      }
      
      char f[10];
      
      if (streq(wo, "finger")) {
	for (int fn=0;fn<8;fn++) {
	  sprintf(f, "finger.%i", fn);
	  if (!worn_on(who, f, wol) && havelimb(who, wo)) {
	    wo = f;
	    break;
	  }
	}
	if (streq(wo, "finger")) {
	  wo = "finger.0";
	}
      }

      MudObject *b = worn_on(who, "body", 0);
      if (streq(wo, "lapel") && !b) {
	cant.add(*o);
	what.remove(*o);
	i--;
	continue;
      }

      const char *needs = o->get("needs");
      if (needs) {
	b = worn_provides(who, needs);
	if (!b) {
	  cant.add(*o);
	  what.remove(*o);
	  i--;
	  continue;
	}
      }

      if ((!streq(wo, "lapel") && worn_on(who, wo, wol))) {
	already2.add(*o);
	what.remove(*o);
	MudObject *o = worn_on(who, wo, wol);
	if (!a3.get(o->id)) {
	  a3.add(*o);
	}
	i--;
	continue;
      }
      
      int h = o->get_int("heal", 0);
      healing += h;
      if (h != 0) {
	vanish(o);
      } else {
	o->set(KEY_WORNBY, who->id);    
	o->set("$wornon", wo);
      }
    }
  dontwear:;
  }

  if (what.getsize()) {
    if (loc.length()) {
      who->printf("You put %s on your %s.\n", magiclist(what).c_str(), looknice(loc.c_str()));
      who->oprintf(cansee, "%#M %[puts/put] %s on %s %s.\n", who, magiclist(what).c_str(), his_or_her(who), looknice(loc.c_str()));
    } else {
      who->printf("You put on %s.\n", magiclist(what).c_str());
      who->oprintf(cansee, "%#M %[puts on/put on] %s.\n", who, magiclist(what).c_str());
    }
    set_prons(who, what);
  }

  foreach((&what), o, i) {
    dotrap(E_AFTERWEAR, who, o);
    /* ::: after_wear o1==clothing; after worn and messaging */
  }

  if (cant.getsize()) {
    who->printf("You can't wear %s.\n", magiclist(cant).c_str());
  }

  if (cantthere.getsize()) {
    who->printf("You can't wear %s on your %s.\n", magiclist(cantthere).c_str(), looknice(loc.c_str()));
  }

  if (already.getsize()) {
    who->printf("You are already wearing %s.\n", magiclist(already).c_str());
  }

  if (already2.getsize() && a3.getsize()) {
    if (loc.length()) {
      who->printf("You are already wearing %s on your %s.\n", magiclist(a3).c_str(), looknice(loc.c_str()));
    } else {
      who->printf("You are already wearing %s where you would put %s.\n", magiclist(a3).c_str(), magiclist(already2).c_str());
    }
  }

#define MIN(a,b) ((a<b)?(a):(b))
#define KEY_MAXSTRENGTH "maxstrength"
  if (healing) {
    set_strength(who, MIN(who->get_int(KEY_STRENGTH, 0)+healing,
			  who->get_int(KEY_MAXSTRENGTH)));
    who->printf("You feel slightly healed.\n");
  }
}

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

static bool is_wearable(const TeleObject &o) 
{
  if (o->get("wornon") && !is_worn(o) && o->get_int("heal",0)==0) {
    return 1;
  }
  return 0;
}

static bool verb_wear(MudObject *who, int argc, const char **argv) {
    if (wf_wears_with_flag(who, FL_HANDSTIED)) {
        who->printf("Your hands are tied!\n");
        return true;
    }

    if (argc < 2) {
	who->printf("Wear what?\n");
	return true;
    }

    int next=0;

    NewWorld what = match(who, argc-1, argv+1, is_wearable, LOOK_INV|IGNORE_EXITS, &next);
    
    string where = "";

    if (streq(argv[next+1], "on")) {
      where = tobodyloc(the_rest(argc, argv, next+2));
    }

    if (!what.getsize()) {
      if (what.all_nothing==1) {
	who->printf("You aren't carrying anything.\n");
      } else if (what.all_nothing==0) {
        who->printf("You aren't carrying anything like that.\n");
      } else if (what.all_nothing==103) {
	who->printf("Wear any what?\n");
      } else {
	who->printf("You have nothing to wear.\n");
      }
      return true;
    }
    wear_obs(who, what, where);
    return true;
}

static void remove_obs(MudObject *who, NewWorld what) 
{
  MudObject *o;
  int i;

  NewWorld notworn, already;

  for (int wl=WORNLEVEL;wl>=0;wl--) {
    foreach((&what), o, i) {
      if (o->get_int("wornlevel",0)!=wl) {
	continue;
      }

      if (!is_worn(o)) {
	notworn.add(*o);
	what.remove(*o);
	i--;
	continue;
      }

      if (worn_on(who, wornon(o), o->get_int("wornlevel",0)+1)) {
	already.add(*o);
	what.remove(*o);
	i--;
	continue;
      }

      if (dotrap(E_BEFOREREMOVE, who, o)) {
	/* ::: before_remove o1==clothing; after it is known to be worn, and not under any other clothing that needs removing first. return 1 to abort */
	what.remove(*o);
	i--;
	continue;
      }
      
      o->unset(KEY_WORNBY);
    }
  }

  fixup_lapels(who, &what);
    
  if (what.getsize()) {
    who->printf("You take off %s.\n", magiclist(what).c_str());
    who->oprintf(cansee, "%#M %[takes off/take off] %s.\n", who, magiclist(what).c_str());
    set_prons(who, what);
  }

  if (notworn.getsize()) {
    who->printf("You aren't wearing %s.\n", magiclist(notworn).c_str());
  }

  if (already.getsize()) {
    who->printf("You are wearing clothes on top of %s.\n", magiclist(already).c_str());
  }

  foreach((&what), o, i) {
    dotrap(E_AFTERREMOVE, who, o);
    /* ::: after_remove o1==clothing; after worn and messaging */
  }
}

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

    if (wf_wears_with_flag(who, FL_HANDSTIED)) {
        who->printf("Your hands are tied!\n");
        return true;
    }

    NewWorld what = match(who, argc-1, argv+1, is_worn, LOOK_INV|IGNORE_EXITS);
    if (!what.getsize()) {
      if (what.all_nothing==1) {
	who->printf("You aren't wearing anything.\n");
      } else if (what.all_nothing==0) {
        who->printf("You aren't wearing anything like that.\n");
      } else if (what.all_nothing==103) {
	who->printf("Remove any what?\n");
      } else {
	who->printf("You aren't wearing anything.\n");
      }
      return true;
    }
    remove_obs(who, what);
    return true;
}

TeleObject tsaton(MudObject *o) {
  MudObject *s = o->get_object(KEY_SITON);
  int n = o->get_int(KEY_SITONN, 0);

  return TeleObject(o->owner, s, n);
}

static NewWorld sitters(const TeleObject &what)
{
  NewWorld w;
  if (!what)
    return w;

  MudObject *o;
  int i;
  foreach(what.where->children, o, i) {
    if (tsaton(o)==what)
      w.add(*o);
  }
  return w;
}

static int sitters_n(const TeleObject &what)
{
  return sitters(what).getsize();
}

static int sitters_max(MudObject *what)
{
  if (what->get_flag(FL_ONEPERSON))
    return 1;
  if (what->get_flag(FL_TWOPERSON))
    return 2;
  return -1;
}

static bool is_sittable(const TeleObject &what)
{
  return sitters_n(what) < sitters_max(what.what);
}

static bool verb_sit(MudObject *player, int argc, const char **argv) {
        if (player->get("_fighting")) {
            player->printf("You can't sit during a fight!\n");
            return true;
        }
	if (player->get_flag(FL_NOLEGS)==1) {
	  player->printf("You can't sit as you have no legs.\n");
	  return true;
	}
	if (player->get_flag(FL_SITTING)==1) {
	  player->printf("You are already sitting.\n");
	  return true;
	}
 	if (MudObject *m=mount(player)) {
	  if (m->get_flag(FL_NOSADDLE)) {
	    player->interpret("dismount");
	  } else {
	    player->printf("You are already sitting %s %Y.\n", m->get("saddleof")?m->get("saddleof"):"in the saddle of", m);
	    return true;
	  }
	}

	if (player->get_flag(FL_SLEEPING)==1) {
		player->printf("You toss and turn in your sleep.\n");
		return true;
	}

	if (player->get_flag(FL_FLYING)) {
	  player->printf("Try landing first.\n");
	  return true;
	}

	if (dotrap(E_ONSITINROOM, player, player->owner, 0, 0))
        /* ::: sit_in_room o1==room; before anything; return 1 to abort */
	  return true;
	
	MudObject *o=0;
	int n=0;
	MudObject *fl = floor(player->owner);

	if (argc>1) {
	  TeleWorld m = multi_match(player, argc-1, argv+1, is_sittable, LOOK_ROOM|IGNORE_EXITS);
	  if (m.getsize()>1) {
	    player->printf("You can only sit on one thing at a time.\n");
	    return true;
	  } else if (m.getsize()==0) {
	    player->printf("Sit on what?\n");
	    return true;
	  }
	  o = m.get_nth(0);
	  n = m.get_nthnth(0);
	}

	if (o && o->get_flag(FL_FLOOR)) {
	  o = 0;
	  n = 0;
	}

	if (player->owner->get_flag(FL_NOGRAVITY)) {
	  player->printf("You can't sit in 0g.\n");
	  return true;
	}

	if (player->owner->get_flag(FL_ONWATER)) {
	  player->printf("You can't sit on water.\n");
	  return true;
	}

	if (player->owner->get_flag(FL_UNDERWATER)) {
	  player->printf("You can't sit under water.\n");
	  return true;
	}

	if (!o && !fl) {
	  player->printf("You can't sit on nothing.\n");
	  return true;
	}

	const char *and_y = "";
	const char *and_s = "";

	if (player->get_flag(FL_SWIMMING)) {
	  if (!canwalk(player)) {
	    player->printf("You can't get out of the water.\n");
	    return true;
	  }

	  and_y = "get up out of the water and ";
	  and_s = "gets up out of the water and ";
	  player->set_flag(FL_SWIMMING, 0);
	}

	if (!o) {
	  player->printf("You %ssit down on %Y.\n", and_y, fl);
	  player->oprintf(cansee, "%#M %ssits down on %Y.\n", player, and_s, fl);
	  player->unset(KEY_SITON);
	  player->unset(KEY_SITONN);
	} else {
	  if (sitters_max(o) != -1 && sitters_n(TeleObject(player->owner, o, n))>=sitters_max(o)) {
	    NewWorld sit = sitters(TeleObject(player->owner, o, n));
	    player->printf("%#Y is already occupied by %s.\n", o, magiclist(sit).c_str());
	    return true;
	  } else if (!o->get_flag(FL_CANSITON) && !o->get_flag(FL_CANSLEEPON) &&
              !o->get_flag(FL_FRAGILE)) {
	    player->printf("You can't sit on %Y!\n", o);
	    return true;
	  } else if (o->owner == player || is_in(o, player)) {
	    player->printf("You can't sit on something you are carrying.\n");
            return true;
	  } else if (o == player) {
	    player->printf("You can't sit on yourself.\n");
            return true;
	  }
	  else
	    {
	      if (dotrap(E_BEFORESIT, player, o, 0, 0))
              /* ::: before_sit o1==the seat; before messaging, return 1 to abort */
		return true;

	      player->printf("You %ssit down on %Y.\n", and_y, o);
	      player->oprintf(cansee, "%#M %ssits down on %P.\n", player, and_s, o);
	      
	      if (dotrap(E_ONSIT, player, o, 0, 0))
              /* ::: sit o1==the seat; after messaging, return 1 to abort */	
		return true;
	    
	      if (o->get_flag(FL_FRAGILE)) {
		o->oprintf(cansee, "%#Y breaks.\n", o);
		vanish(o);
	      } else {
		player->set(KEY_SITON, o->id);
		player->set(KEY_SITONN, n);
	      }
	  }
	}

	player->set_flag(FL_SITTING, 1);
	return true;
}

static bool verb_swim(MudObject *player, int argc, const char **argv) {
        if (player->get("_fighting")) {
            player->printf("You can't swim during a fight!\n");
            return true;
        }
	if (player->get_flag(FL_SWIMMING)) {
	  player->printf("You are already swimming.\n");
	  return true;
	}
	if (MudObject *m=mount(player)) {
	  player->printf("You can't swim whilst you are mounted.\n", m);
	  return true;
	}

	if (player->get_flag(FL_SLEEPING)==1) {
	  player->printf("You toss and turn in your sleep.\n");
	  return true;
	}

	if (!player->owner->get_flag(FL_BYWATER) && !player->owner->get_flag(FL_ONWATER) &&
	    !player->owner->get_flag(FL_UNDERWATER)) {
	  player->printf("You can't swim here.\n");
	  return true;
	}

	if (!canswim(player)) {
	  player->printf("You can't swim.\n");
	  return true;
	}

	player->printf("You start swimming.\n");
	player->oprintf(cansee, "%#M starts swimming.\n", player);

	player->set_flag(FL_SITTING, 0);
	player->set_flag(FL_SWIMMING, 1);
	player->set_flag(FL_FLYING, 0);
	return true;
}

static bool verb_sleep(MudObject *player, int argc, const char **argv) {
        if (player->get("_fighting")) {
            player->printf("You can't sleep during a fight!\n");
            return true;
        }
	if (player->get_flag(FL_SLEEPING)==1) {
		player->printf("You are already sleeping.\n");
		return true;
	}
	if (MudObject *m=mount(player)) {
	  player->printf("You can't sleep whilst riding %Y.\n", m);
	  return true;
	}
	if (player->owner) {
	  if (player->owner->get_flag(FL_NOGRAVITY)) {
	    player->printf("You can't sleep in 0g.\n");
	    return true;
	  }
	  if (player->owner->get_flag(FL_ONWATER) && !player->get_flag(FL_GILLS)) {
	    player->printf("You can't sleep on water.\n");
	    return true;
	  }
	  if (player->owner->get_flag(FL_UNDERWATER) && !player->get_flag(FL_GILLS)) {
	    player->printf("You can't sleep under water.\n");
	    return true;
	  }
	  if (player->get_flag(FL_FLYING)) {
	    player->printf("You can't sleep on the wing.\n");
	    return true;
	  }
	}

	TeleObject o;
	MudObject *fl = floor(player->owner);

	if (argc>1) {
	  TeleWorld m = multi_match(player, argc-1, argv+1, NULL, LOOK_ROOM);
	  if (m.getsize()>1) {
	    player->printf("You can only sleep on one thing at a time.\n");
	    return true;
	  } else if (m.getsize()==0) {
	    player->printf("Sleep on what?\n");
	    return true;
	  }
	  o = m.get(0);
	}

	if (o && o->get_flag(FL_FLOOR)) {
	  o = 0;
	}

	if (player->get_flag(FL_SWIMMING) && player->get_flag(FL_GILLS)) {
	  player->set_flag(FL_SLEEPING, 1);
	  player->set_flag(FL_SITTING, 0);
	  player->set_flag(FL_SWIMMING, 1);

	  player->printf("You fall asleep.\n");
	  player->oprintf(cansee, "%#M falls asleep.\n", player);
	  return true;
	}

	if (player->get_flag(FL_SWIMMING)) {
	  player->printf("You get up out of the water.\n");
	  player->oprintf(cansee, "%#M gets up out of the water.\n", player);
	}

	if (o && !o->get_flag(FL_CANSLEEPON)) {
	  player->printf("You can't sleep on %Y.\n", *o);
	  return true;
	} else if (o && o.where != player->owner) {
	  player->printf("You can't sleep on something you are carrying.\n");
	  return true;
	} else if (o.what == player) {
	  player->printf("You can't sleep on yourself.\n");
	  return true;
	}

	if (!o && !fl) {
	  player->printf("You can't sleep on nothing.\n");
	  return true;
	}

	if (!o) {
	  if (!player->get_flag(FL_NOLEGS)) {
	    player->printf("You roll out a sleeping bag and fall asleep.\n");
	    player->oprintf(cansee, "%#M falls asleep.\n", player);
	  } else {
	    player->printf("You fall asleep.\n");
	    player->oprintf(cansee, "%#M falls asleep.\n", player);
	  }
	} else {
	    player->printf("You fall asleep on %Y.\n", *o);
	    player->oprintf(cansee, "%#M falls asleep on %P.\n", player, *o);
	}

        if (o) {
	 player->set(KEY_SITON, o.what->id);
	 player->set(KEY_SITONN, o.nth);
        } else {
	  player->unset(KEY_SITON);
	  player->unset(KEY_SITONN);
	}
	
	player->set_flag(FL_SLEEPING, 1);
	player->set_flag(FL_SITTING, 0);
	player->set_flag(FL_SWIMMING, 0);
	/* ::: after_sleep o1==room slept in; after everything */	
	if (dotrap(E_AFTERSLEEP, player, player->owner)) return true;
	return true;
}

static bool verb_wake(MudObject *player, int, const char **argv) 
{
  if (!player->get_flag(FL_SLEEPING)) {
    player->printf("You are already quite awake.\n");
    return true;
  }
  MudObject *fl = floor(player->owner);
  if (player->get_flag(FL_SWIMMING) || player->get_flag(FL_NOLEGS) || !fl) {
    player->printf("You wake up.\n");
    player->oprintf(cansee, "%#M wakes up.\n", player);
  } else {
    player->printf("You wake up and clamber to your feet.\n");
    player->oprintf(cansee, "%#M wakes up and clambers to %s feet.\n", player, his_or_her(player));
  }
  player->set_bflag(FL_SLEEPING, 0);
  return true;
}

//! the position and disposition of an object
struct Pos {
  MudObject *o;
  int n;

  enum stat {
    P_STANDING,
    P_SITTING,
    P_LYING,
    P_SWIMMING,    
    P_FLYING,
  } p;

  int asleep;

  Pos () {
    o = 0;
    n = 0;
    p = P_STANDING;
    asleep = 0;
  }
  
  Pos (MudObject *who) {
    o = who->get_object(KEY_SITON);
    n = who->get_int(KEY_SITONN, 0);
    p = P_STANDING;
    if (who->get_flag(FL_SITTING)) p = P_SITTING;
    if (who->get_flag(FL_SLEEPING)) p = P_LYING;
    if (who->get_flag(FL_SWIMMING)) p = P_SWIMMING;
    if (who->get_flag(FL_FLYING)) p = P_FLYING;

    if (who->get_flag(FL_SLEEPING)) asleep = 1;
    else asleep = 0;
  }

  bool operator==(const struct Pos &p) {
    return (o == p.o && n == p.n);
  }

  void set(MudObject *player) {
    if (o) {
      player->set(KEY_SITON, o->id);
      player->set(KEY_SITONN, n);
    } else {
      player->unset(KEY_SITON);
      player->unset(KEY_SITONN);
    }    
  }
};

static bool verb_fly(MudObject *who, int argc, const char **argv)
{
  const char *b = body(who);
  if (streq(b, "avian")) {
    if (who->get_flag(FL_FLYING)) {
      who->printf("You are already flying.\n");
      return true;
    }
    who->printf("You take off.\n");
    who->oprintf(secret(who) && cansee, "%#M %[takes/take] off.\n", who);
    who->set_flag(FL_FLYING, 1);
    who->set_flag(FL_SWIMMING, 0);
    who->set_flag(FL_SITTING, 0);
    who->unset(KEY_SITON);
    who->unset(KEY_SITONN);
    return true;
  }
  
  who->interpret("flys");
  return true;
}

static bool verb_land(MudObject *who, int argc, const char **argv)
{
  if (!who->get_flag(FL_FLYING)) {
    who->printf("You aren't flying.\n");
    return true;
  }

  MudObject *where = who->owner;
  if (where->get_flag(FL_UNDERWATER) || where->get_flag(FL_ONWATER)) {
    who->printf("Nothing to land on.\n");
    return true;
  }

  who->set_flag(FL_FLYING, 0);
  who->printf("You land.\n");
  who->oprintf(secret(who) && cansee, "%#M %[lands/land].\n", who);
  return true;
}

static bool verb_stand(MudObject *player, int argc, const char **argv) {
	if (MudObject *m=mount(player)) {
	  player->printf("You can't stand up whilst riding %Y.\n", m);
	  return true;
	}
	MudObject *where = player->owner;
	if (!where) {
	  player->printf("You aren't anywhere.\n");
	  return true;
	}
	if (player->get_flag(FL_NOLEGS)) {
	  player->printf("You have no legs.\n");
	  return true;
	}

	if (where->get_flag(FL_NOGRAVITY)) {
	  player->printf("There's no gravity here.\n");
	  return true;
	}

	Pos p;

	MudObject *fl = floor(player->owner);

	if (argc>1) {
	  TeleWorld m = multi_match(player, argc-1, argv+1, NULL, LOOK_ROOM);
	  if (m.getsize()>1) {
	    player->printf("You can only stand on one thing at a time.\n");
	    return true;
	  } else if (m.getsize()==0) {
	    player->printf("Stand on what?\n");
	    return true;
	  }
	  p.o = m.get_nth(0);
	  p.n = m.get_nthnth(0);
	}

	if (p.o && p.o->get_flag(FL_FLOOR)) {
	  p.o = 0;
	  p.n = 0;
	}

	if (!p.o && !fl) {
	  player->printf("There's nowhere to stand.\n");
	  return true;
	}

	if (dotrap(E_BEFORESTAND, player, p.o?:fl))
	  /* ::: before_stand o1==the floor/other object we are about to stand on; return 1 to abort */
	  return true
;
	Pos oldpos(player);

	if (!player->get_flag(FL_SITTING) && 
	    !player->get_flag(FL_SWIMMING) && 
	    !player->get_flag(FL_SLEEPING) && 
	    !player->get_flag(FL_FLYING) && oldpos == p) {
	  player->printf("You are already standing on %P.\n", p.o?p.o:fl);
	  return true;
	}

	if (p.o && !p.o->get_flag(FL_CANSLEEPON) && !p.o->get_flag(FL_CANSITON)
	    && !p.o->get_flag(FL_TABLE) && !p.o->get_flag(FL_FRAGILE)) {
	  player->printf("You can't stand on %M.\n", p.o);
	  return true;
	}

	if (player->get_flag(FL_SWIMMING)) {
	  if (!where->get_flag(FL_BYWATER)) {
	    player->printf("There's nowhere to stand.\n");
	    return true;
	  }
	  if (!canwalk(player)) {
	    player->printf("You can't get out of the water.\n");
	    return true;
	  }
	  player->printf("You get up out of the water.");
	  player->oprintf(cansee, "%#M %[gets/get] up out of the water.\n", player);
	  player->set_flag(FL_SWIMMING, 0);
	  player->set_flag(FL_SLEEPING, 0);
	}

	if (oldpos.p == Pos::P_FLYING) {
	  player->printf("You land on %M.\n", p.o?p.o:fl);
	  player->oprintf(secret(player) && cansee, "You land on %M.\n", p.o?p.o:fl);
	} else {
	  if (oldpos.asleep) {
	    if (p.o) {
	      player->printf("You wake up and stand on %P.\n", p.o?p.o:fl);
	      player->oprintf(cansee, "%#M %[wakes/wake] up and %[stands/stand] on %P.\n", player, p.o?p.o:fl);
	    } else {
	      player->printf("You wake and stand up.\n");
	      player->oprintf(cansee, "%#M %[wakes and stands up/wake and stand up].\n", player);
	    }
	  } else {
	    if (p.o || (oldpos.o && oldpos.p==Pos::P_STANDING)) {
	    player->printf("You stand on %P.\n", p.o?p.o:fl);
	    player->oprintf(cansee, "%#M %[stands/stand] on %P.\n", player, p.o?p.o:fl);
	    } else {
	      player->printf("You stand up.\n");
	      player->oprintf(cansee, "%#M %[stands/stand] up.\n", player);
	    }
	  }
	}
	  
	if (p.o && p.o->get_flag(FL_FRAGILE)) {
	  p.o->oprintf(cansee, "%#Y %[breaks/break].\n", p.o);
	  vanish(p.o);
	  Pos flr;
	  flr.set(player);
	} else {
	  p.set(player);	  
	}
	
	player->set_flag(FL_SLEEPING, 0);
	player->set_flag(FL_FLYING, 0);
	player->set_flag(FL_SITTING, 0);

	if (dotrap(E_AFTERSTAND, player, p.o?:fl))
	  /* ::: after_stand o1==the floor/other object we have stood on. */
	  return true;

        return true;
}

#include "verbmodule.h"

void startup() {

AUTO_VERB(sit, 3, 0, PFL_NONE);
AUTO_VERB(sleep, 3, 0, PFL_NONE);
 ADD_ALIAS(rest, 3, sleep);
AUTO_VERB(stand, 3, 0, PFL_NONE);
AUTO_VERB(wake, 2, 0, PFL_NONE);
AUTO_VERB(swim, 3, 0, PFL_NONE);
AUTO_VERB(fly, 3, 0, PFL_AWAKE);
AUTO_VERB(land, 3, 0, PFL_AWAKE);

#define verb_inventory verb_equipment
#define verb_qinv verb_equipment

AUTO_VERB(equipment, 2, 0, PFL_NONE);
AUTO_VERB(inventory, 3, 0, PFL_NONE);
AUTO_VERB(qinv, 4, 0, PFL_NONE);

ADD_ALIAS(i, 1, inventory);

AUTO_VERB(remove, 4, 0, PFL_AWAKE);
AUTO_VERB(wear, 4, 0, PFL_AWAKE);

ADD_ALIAS(don, 3, wear);
ADD_ALIAS(doff, 3, remove);

}