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 - Object handling 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.
 * 
 */

#define BOOZE_FACTOR 12
#define BOOZE_MAX 6000

#include "musicmud.h"
#include "verbs.h"
#include "misc.h"
#include "util.h"
#include "events.h"
#include "trap.h"
#include "pflags.h"
#include "musictok.h"

#include "units.h"
#include "shared.h"

#include "match.h"
#include "death.h"
#include "hooks.h"
#include "emsg.h"

#include "nations.h"

#include <vector>
#include <algorithm>

static inline bool is_fillable(const TeleObject &o) {
  return o->get_flag(FL_WATERTIGHT);
}

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

static void consolidate(MudObject *what, bool anyway) 
{
  if (used_volume(what)==0 || anyway) {
    what->unset("$fill.0");
    what->unset("$fillamt.0");
    what->set("$fill.count", 0);
    what->set("$fillamt.count", 0);
  }
}

static void collapse_cash(MudObject *what) {
  MudObject *player = what->owner;
  if (!is_person(player))
    return;
  if (what->get_flag(FL_CASH)) {
    moveallcash(what, player);
    vanish(what);
  }
}

#define MODULE "objects"

static string fshort(MudObject *what)
{
  return subshort(what);
}


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

  int strength = strength_of(who), oldstrength = strength;
  int maxstrength = who->get_int("maxstrength");

  NewWorld empties;

  foreach((&what), o, i) {
    if (dotrap(E_ONCONSUME, who, o, 0, 0)) {
      /* ::: consume o1==what eaten; before everything, return 1 to abort */
      what.remove(*o);
      i--;
      continue;
    }
  }

  NewWorld inedible;
  NewWorld recs;

  foreach((&what), o, i) {
    if (o->get_flag(FL_WATERTIGHT)) {
      recs.add(*o);
      what.remove(*o);
      i--;
      continue;
    }

    if (o->get_object("fount")) {
      recs.add(*o);
      what.remove(*o);
      i--;
      continue;
    }

    if (o->get_flag(FL_DRINK)!=drink) {
      inedible.add(*o);
      what.remove(*o);
      i--;
      continue;
    }

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

  set<string> ins;
  foreach((&inedible), o, i) {
    if (o==who)
      ins.insert("yourself");
    else if (is_person(o))
      ins.insert("others");
    else
      ins.insert(plural_name(o));
  }

  set<string>::iterator init = ins.begin();
  string cant;
  while (init != ins.end()) {
    string s = *init;
    init++;
    
    if (init==ins.end() && cant.length())
      cant += " or ";
    else
      cant += ", ";

    cant += s;
  }

  if (cant.length()) {
    who->printf("You can't %s %s.\n", drink?"drink":"eat", cant.c_str()+2);
  }

  NewWorld drinks, eats;
  foreach((&what), o, i) {
    if (o->get_flag(FL_DRINK))
      drinks.add(*o);
    else
      eats.add(*o);
  }
  if (drinks.getsize()) {
    who->printf("You drink %s.\n", magiclist(drinks).c_str());
    who->oprintf(cansee, "%#M %[drinks/drink] %s.\n", who, magiclist(drinks).c_str());
  }
  if (eats.getsize()) {
    who->printf("You eat %s.\n", magiclist(eats).c_str());
    who->oprintf(cansee, "%#M %[eats/eat] %s.\n", who, magiclist(eats).c_str());
  }

  foreach((&what), o, i) {
    strength += o->get_int("food", 0);
    if (strength > maxstrength) {
      strength = maxstrength;
      if (oldstrength < maxstrength) {
	who->printf("You feel fully healed.\n");
      }
    }

    int booze = (abv(o)*serving(o)*BOOZE_FACTOR)/10000;
    if (booze>0) {
      who->set(KEY_PISSED, who->get_int(KEY_PISSED, now)+booze);
    }
    if (MudObject *em=o->get_object("empty")) {
      MudObject *e=0;
      if (o->get_object("cloneof"))
	e = clone_object(em, o->owner, NULL);
      else {
	set_owner(em, o->owner);
	e = em;
      }
      empties.add(e);
    }
    vanish(o);
  }

  NewWorld empty, sub, sdrink;

  foreach((&recs), o, i) {
    if (dotrap(E_BEFOREFOUNTAIN, who, o, 0, 0)) {
      /* ::: before_fountain o1==fountain, o2==container (if any); before someone uses a fountain. if on container, being drunken from. return 1 to abort. */
      recs.remove(*o);
      i--;
      continue;
    }
    MudObject *substance = o->get_object("fount");
    int amount = 100;
    if (!substance) {
      substance = o->array_get_object("$fill", 0);
      amount = used_volume(o);
    }
    if (!substance || !amount) {
      empty.add(*o);
      continue;
    }
    if (!real_is_edible(substance)) {
      who->printf("You can't drink %s.\n", subshort(substance));
      continue;
    }

    if (!sub.get(substance->id)) {
      sub.add(*substance);
    }
    sdrink.add(*o);

    strength += substance->get_int("food", 0);
    if (strength > maxstrength) {
      strength = maxstrength;
      if (oldstrength < maxstrength) {
	who->printf("You feel fully healed.\n");
      }
    }

    int booze = abv(substance) * amount /10000;
    if (booze > 0) {
      who->set(KEY_PISSED, who->get_int(KEY_PISSED, now)+(booze*BOOZE_FACTOR));
    }
    consolidate(o, true);
  }

  if (sdrink.getsize()) {
    who->printf("You drink %s from %s.\n", magiclist(sub, fshort).c_str(), magiclist(sdrink).c_str());
    who->oprintf(cansee, "%#M %[drinks/drink] from %s.\n", who, magiclist(sdrink).c_str());
  }

  empties += sdrink;
  set_prons(who, empties);

  if (what.getsize()==1 ^ sdrink.getsize()==1) {
    MudObject *ob = (what.getsize()?what:sub).get_nth(0);
    MudObject *subs = sub.getsize()?sub.get_nth(0):0;

    if (ob->get("taste")) {
      who->printf("%s\n", ob->get("taste"));
    } else {

      if (!subs)
	subs = ob->get_object("substance");
      if (!subs)
	subs = ob;
      
      int alc = abv(subs);
      
      if (alc>4000)
	who->printf("%|%[It tastes/They taste] very strongly of alcohol.\n", subs);
      else if (alc>2000)
	who->printf("%|%[It tastes/They taste] strongly of alcohol.\n", subs);
      else if (alc>200)
	who->printf("%|%[It tastes/They taste] of alcohol.\n", subs);
      else if (is_person(subs))
	who->printf("%|%[It tastes/They taste] of meat.\n", subs);
      else
	who->printf("%|%[It tastes/They taste] like %s.\n", subs, subs->get("short"));
    }
  }

  if (is_player(who)) {
    int booze = who->get_int(KEY_PISSED, now);
    if (booze - now > 300*12) {
      do_die(who, privs_of(who)*20, "alcohol poisoning");
      return;
    }
  }

  foreach((&recs), o, i) {
    if (dotrap(E_AFTERCONSUME, who, o, 0, 0)) {
      /* ::: after_consume o1==thing consumed; after taste, alcohol death, return 1 to abort. */
      recs.remove(*o);
      i--;
      continue;
    }
  }

  foreach((&what), o, i) {
    if (dotrap(E_AFTERCONSUME, who, o, 0, 0)) {
      what.remove(*o);
      i--;
      continue;
    }
  }

  if (empty.getsize()) {
    who->printf("%#s %s empty.\n", magiclist(empty).c_str(), plural(empty)?"are":"is");
  }

  set_strength(who, strength);

  if (strength < 0) {
    MudObject *cor = do_die(who, privs_of(who)*20, "food poisoning");
    if (cor) {
      cor->setf("desc" ,"%#M seems to have died from food poisoning.", who);
    }
  }
}

static bool can_seeinto(MudObject *player, MudObject *what) {
  if (what->get_flag(FL_TRANSPARENT)) return true;
  if (!what->get_flag(FL_CONTAINER)) return false;
  if (what->get_flag(FL_STATED)) return true;
  return state(what)==0;
}

static bool is_corpse(const TeleObject &what) 
{
  if (what->get_object("treatas") == MUD_CORPSE) {
    return 1;
  }
  return 0;
}

static void rmcard(MudObject *deck, int which)
{
  int s = deck->array_size("$card");
  for (int i=which;i<(s-1);i++) {
    deck->array_set("$card", i, deck->array_get_int("$card", i+1));
  }
  deck->array_unset("$card", s);
  deck->set("$card.count", s-1);
}

static void addcard(MudObject *from, int card, int hidden)
{
  int cc = from->array_size("$card");
  from->array_set("$card", cc, card+(hidden?256:0));
  from->set("$card.count", cc+1);
}

#define DEAL_ALLSEE 0
#define DEAL_TARSEE 1
#define DEAL_NONSEE 2

static void do_deal(MudObject *who, MudObject *deck, MudObject *target, int mode, int howmany)
{
  string cards;
  int dealt=0;

  for (int h=0;h<howmany;h++) {
    int s = deck->array_size("$card");
    if (s==0) {
      continue;
    }
    
    int card = deck->array_get_int("$card", 0);
    string cardst = describe_card(card, 1);
    const char *cardstr = cardst.c_str();
    const char *repstr = cardstr;
    
    if (cards.length()) {
      cards += " and ";
    }
    cards += cardstr;

    if (mode==DEAL_NONSEE)
      cardstr = repstr = "a card";
    if (mode==DEAL_TARSEE)
    cardstr = "a card";
    
#if 0
    if (who==target) {
      who->printf("You deal %s to yourself.\n", repstr);
      who->oprintf(cansee, "%#M %[deal/deals] %s to %sself.\n", who, cardstr, his_or_her(who));
    } else {
      who->printf("You deal %s to %M.\n", cardstr, target);
      who->oprintf(cansee, target, "%#M %[deals/deal] %s to %M.\n", who, cardstr, target);
      target->printf("%#M %[deals/deal] %s to you.\n", who, repstr);
    }
#endif    
    rmcard(deck, 0);
    addcard(target, card, mode==DEAL_NONSEE);
    dealt++;
  }

  string reps = cards;

  if (mode==DEAL_NONSEE) {
    if (dealt==1)
      cards = reps = "a card";
    else
      cards = reps = ssprintf("%s cards", numbertostring(dealt));
  }

  if (mode==DEAL_TARSEE) {
    if (dealt==1)
      cards = "a card";
    else
      cards = ssprintf("%s cards", numbertostring(dealt));
  }

  if (dealt) {
    if (target==who) {
      who->printf("You deal %s to yourself.\n", reps.c_str());
      who->oprintf(cansee, "%#M %[deals/deal] %s to %sself.\n", who, cards.c_str(), him_or_her(who));
    } else {
      who->printf("You deal %s to %M.\n", cards.c_str(), target);
      who->oprintf(cansee && avoid(target), "%#M %[deals/deal] %s to %M.\n", who, cards.c_str(), target);
      target->printf("%#M %[deals/deal] %s to you.\n", who, reps.c_str());
    }
  }
}

//! A playing card
struct card
{
  int suit;
  int val;
};

static struct card parsecard(const char *a) 
{
  struct card cd = {-1, -1};

  int &val = cd.val;
  int &suit = cd.suit;
  if (strchr(a, '*')) {
    val = -2;
    suit = -2;
  }
  if (streq(a, "all")) {
    val = -2;
    suit = -2;
    return cd;
  }
  if (strchr(a, 'A')||strchr(a,'a')) val= 0;
  if (strchr(a, '2')) val= 1;
  if (strchr(a, '3')) val= 2;
  if (strchr(a, '4')) val= 3;
  if (strchr(a, '5')) val= 4;
  if (strchr(a, '6')) val= 5;
  if (strchr(a, '7')) val= 6;
  if (strchr(a, '8')) val= 7;
  if (strchr(a, '9')) val= 8;
  if (strchr(a, 'T')||strchr(a,'t')) val= 9;
  if (strchr(a, 'J')||strchr(a,'j')) val= 10;
  if (strchr(a, 'Q')||strchr(a,'q')) val= 11;
  if (strchr(a, 'K')||strchr(a,'k')) val= 12;
  if (strchr(a, 'S')||strchr(a,'s')) suit = 0;
  if (strchr(a, 'H')||strchr(a,'h')) suit = 1;
  if (strchr(a, 'C')||strchr(a,'c')) suit = 2;
  if (strchr(a, 'D')||strchr(a,'d')) suit = 3;

  if (strchr(a, '?')) {
    suit = -3;
    val = -3;
  }

  if (val == -1 || suit == -1) {
    suit = -1;
    val = -1;
    return cd;
  }
  
  return cd;
}

static int matches(int a, struct card cd)
{
  int va = (a&0xff)%13, sa=(a&0xff)/13;
  int vb = cd.val,      sb=cd.suit;

  if (va == vb && sa == sb) 
    return 1;

  if (va == vb && sb == -2)
    return 1;

  if (sa == sb && vb == -2)
    return 1;

  if (sb == -2 && vb == -2)
    return 1;

  if (a & 256 && vb==-3)
    return 1;

  return 0;
}

static bool verb_discard(MudObject *who, int argc, const char **argv)
{
  if (argc < 2) {
    who->printf("Discard what?\n");
    return true;
  }
  struct card c = parsecard(argv[1]);
  if (c.val==-1) {
    who->printf("Can't decode that.\n");
    return true;
  }
  int count = 0;
  for (int i=0;i<who->array_size("$card");i++) {
    if (matches(who->array_get_int("$card", i), c)) {
      rmcard(who, i);
      i--;
      count++;
    }
  }
  if (count) {
    who->printf("You discard %i card%s.\n", count, count==1?"":"s");
    who->oprintf("%#M %[discards/discard] %i card%s.\n", who, count, count==1?"":"s");
    dotrap(E_AFTERDISCARDINROOM, who, who->owner);
  } else {
    who->printf("You have no matching cards.\n");
  }
  return true;
}

static bool verb_deal(MudObject *who, int argc, const char **argv)
{
  MudObject *deck=0;
  MudObject *o;
  int i;
  foreach(who->children, o, i) {
    if (streq(o->get("short"), "deck"))
      deck = o;
  }

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

  int mode = DEAL_TARSEE;
  int d = 1;

  int n = 1;

  if (streq(argv[1], "two")) {
    n = 2;
    d++;
  }

  if (streq(argv[d], "openly")) {
    mode = DEAL_ALLSEE;
    d++;
  }

  if (streq(argv[d], "privately")) {
    mode = DEAL_TARSEE;
    d++;
  }

  if (streq(argv[d], "secretly")) {
    mode = DEAL_NONSEE;
    d++;
  }

  NewWorld w = match(who, argc-d, argv+d, is_person, LOOK_BOTH|IGNORE_EXITS);
  if (w.getsize()==0) {
    who->printf("Deal to who?\n");
    return true;
  }

  foreach((&w), o, i) {
    do_deal(who, deck, o, mode, n);
    if (deck->array_size("$card")==0) {
      who->printf("Deck exhausted.\n");
      vanish(deck);
      return true;
    }
  }

  return true;
}

static bool verb_shuffle(MudObject *who, int argc, const char **argv)
{
  NewWorld w = match(who, argc-1, argv+1, 0, LOOK_BOTH|IGNORE_EXITS);
  MudObject *what = 0;
  
  if (w.getsize()>1) {
    who->printf("You can only shuffle one thing at a time.\n");
    return true;
  } else if (w.getsize()) {
    what = w.get_nth(0);
  }

  if (!what) {
    who->printf("Shuffle what?\n");
    return true;
  }
  
  int siz = what->array_size("$card");
  if (!siz) {
    return true;
  }

  int cards[siz];
  for (int i=0;i<siz;i++) 
    cards[i] = what->array_get_int("$card", i);

  for (int n=0;n<5;n++) {
    for (int i=0;i<siz;i++) {
      int r = random_number(siz);

      int tmp = cards[i];
      cards[i] = cards[r];
      cards[r] = tmp;
    }
  }

  for (int i=0;i<siz;i++) 
    what->array_set("$card", i, cards[i]);

  who->printf("You give %Y a good shuffle.\n", what);
  who->oprintf(cansee, "%#M shuffles %P.\n", who, what);

  return true;
}

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

    TeleWorld w = multi_match(player, argc-1, argv+1, 0, LOOK_BOTH|LOOK_MWIELD|ALLOW_ME);

    MudObject *what = 0;
    TeleObject rreal;
    const TeleObject *real=0;

    if (w.getsize()>1) {
      player->printf("You can only examine one thing at a time.\n");
      return true;
    } else if (w.getsize()) {
      what = w.get_nth(0);
      real = &w.get(0);
    }
    
    const char *tlink = 0;
    
    if (!what) {
      if (player->owner->get_flag(FL_LINKED)) {
        int i;
        MudObject *obj;
        foreach(player->owner->children, obj, i) {
          MudObject *o = linkedlink(obj);
	  if (o && o->get_flag(FL_LINKED)) {
	    w = multi_match(player, argc-1, argv+1, NULL, LOOK_SPECIAL|IGNORE_EXITS, NULL, o->children);
	    if (w.getsize()>1) {
	      player->printf("You can only examine one thing at a time.\n");
	      return true;
	    }
	    if (w.getsize()==1) {
	      MudObject *wh = w.get_nth(0);
	      if (!is_mobile(wh) && !is_player(wh))
		continue;
		
	      what = w.get_nth(0);
	      rreal = w.get(0);
	      real = &rreal;
	      tlink = obj->get("short");
	    }
          }
        }
      }
    }

    if (what && what->get_flag(FL_EXIT)) {
      MudObject *o = what->get_object("link");
      if (o) {
	string tostr = format_roomname(o, player, "", "", "to");
	// XXX : fix this for buildings
	player->printf("It leads %s.\n", tostr.c_str());
	return true;
      }
    }

    if (!what || what->get_flag(FL_EXIT)) {
	player->printf("You cannot locate '^o%s^n'.\n", w.txt.c_str());
	return true;
    }

    int oldstate = state(what);

    set_prons(player, *real);
    
    if (what->get_flag(FL_EXAMFLIPS)) 
      what->set(KEY_STATE, 0);

      /* ::: examine o1==thing examined; before "You examine..." messages, return 1 to abort */
    if (dotrap(E_ONEXAMINE, player, what, 0, 0)) return true;
    
    if (what->get("newsgroup")) {
	player->interpret("scan");
	return true;
    }
    
    if (player==what) {
      player->printf("You examine yourself.\n");
      player->oprintf(cansee, "%#M %[examines/examine] %s.\n", player, himself_or_herself(player));
    } else {
      if (tlink) {
	if (!is_person(what)) {
	  player->printf("You look %s and examine %Y.\n", tlink, real);
	  player->oprintf(cansee && avoid(what), "%#M %[looks/look] %s and %[examines/examine] %P.\n", 
			  player, tlink, what);
	} else {
	  player->printf("You look %s at %Y.\n", tlink, real);
	  player->oprintf(cansee && avoid(what), "%#M %[looks/look] %s at %P.\n", 
			  player, tlink, what);
	}
      } else {
	if (!what->get_flag(FL_NOEXAMTEXT)) {
	  if (is_person(what))
	    player->printf("You look at %Y.\n", real);
	  else
	    player->printf("You examine %Y.\n", real);
	}
	if (is_person(what))
	  player->oprintf(cansee && avoid(what), "%#M %[looks at/look at] %P.\n", player, real);
	else
	  player->oprintf(cansee && avoid(what), "%#M %[examines/examine] %P.\n", player, real);	  
      }
      if (is_person(what))
	what->printf("%#M %[looks/look] at you.\n", player);
      else
	what->printf("%#M %[examines/examine] you.\n", player);
      
      if (player->get_object(KEY_WIELD)==what)
	player->printf("You are wielding %s.\n", it_them(what));

      if (what->get_object(KEY_WORNBY)==player) {
	const char *w = what->get("$wornon");
	if (!w) w = what->get("wornon");
	player->printf("You are wearing %s %s your %s.\n", it_them(what), streq(w, "allover")?"over":"on", looknice(w));
      }

      if (player->get_object(KEY_SITON)==
	  what && player->get_int(KEY_SITONN, 0)==rreal.nth) {
	if (player->get_flag(FL_SITTING))
	  player->printf("You are sitting on %s.\n", it_them(what));
	else
	  player->printf("You are standing on %s.\n", it_them(what));
      }

      if (!player->get_object(KEY_SITON) && what->get_flag(FL_FLOOR)) {
	if (player->get_flag(FL_SITTING))
	  player->printf("You are sitting on %s.\n", it_them(what));
	else if (player->get_flag(FL_FLYING));
	else if (player->get_flag(FL_SLEEPING));
	else if (player->get_flag(FL_SWIMMING));
	else
	  player->printf("You are standing on %s.\n", it_them(what));
      }

      if (what->get("on_examine")) {
	player->ilc++;
	player->interpret(what->get("on_examine"));
	player->ilc--;
	return true;
      }
    }

    /* ::: before_examine o1==thing examined; after "You examine...", you are wearing messages, but before main desc ; return 1 to abort */
    if (dotrap(E_BEFOREEXAMINE, player, what, 0, 0)) 
      return true; 

    if (what->array_size("$fill") == 1) {
      describe_liquid_to(player, what);
    }

    /* ::: before_show_desc o1==thing examined; instead of printing the 'desc' when examined. returning 1 averts that, but will still print clothes etc. */
    if (!dotrap(E_BEFORESHOWDESC, player, what, 0, 0)) 
    {
      const char *d = what->array_get("desc", oldstate);
      if (!d) d = what->get("object_desc");
      if (!d) d = what->get("desc");
      if (!d) {
	MudObject *t = what->get_object("treatas");
	if (t) {
	  d = t->get("desc");
	}
      }

      if (streq(d, "This player has no description.") || streq(d, "A typical mobile..."))
	d = 0;

      if (d && !strchr(d, '\n')) 
	player->printf("  %s\n", d?d:"You notice nothing unusual.");
      else
	player->printf("%s\n", d?d:"You notice nothing unusual.");
    }

    if (shelf_low_enough(player, what)) {
      MudObject *o;
      int i;
      NewWorld stuff;

      foreach(what->owner->children, o, i) {
	if (o->get_object(KEY_SITON)==what &&
	    o->get_int(KEY_SITONN, 0)==real->nth) {
	  if (!is_person(o)) {
	    stuff.add(o);
	  }
	}
      }
      if (stuff)
	player->printf("Upon %s %s %W.\n", it_them(what), plural(stuff)?"are":"is",
		       &stuff);
    } else {
      player->printf("It's too high to see on top of %s.\n", it_them(what));
    }

    if (is_player(what) || is_mobile(what) || can_seeinto(player, what) || is_corpse(what)) { 
	TeleWorld cloth = clothes(what);

	MudObject *wornfrom = ::wornfrom(what);
	if (wornfrom == what)
	  wornfrom = 0;
	
	MudObject *wp = what->get_object(KEY_WIELD);
	if (!wp && wornfrom) wp = wornfrom->get_object(KEY_WIELD);
	if (wp) {
	  player->printf("%#H %[is/are] wielding %M.^n\n", what, wp);
	}

	MudObject *rid = mount(what);
	if (rid) {
	  const char *rv = rid->get("riding_verb");
	  if (!rv) rv = "riding";
	  player->printf("%#H %[is/are] %s %M.^n\n", what, rv, rid);
	}
	  
	NewWorld seencloth;

	MudObject *o;
	int i;
	foreach((&cloth), o, i) {
	  if (o->get("name")) {
	    int wl = o->get_int("wornlevel", 0);
	    const char *wn = wornon(o);

	    if (is_player(what) || is_mobile(what)) {
	      
	      StrTok t(wn);
	      int yes = 0;
	      while (const char *won=t.next(",")) {
		if (worn_on(what, won, wl+1)) {
		  continue;
		}

		if (wornfrom && worn_on(wornfrom, won, wl+1))
		  continue;

		yes = 1;
	      }

	      if (!yes)
		continue;
	    }

	    seencloth.add(o);
	  }
	}

	if (seencloth)
          player->printf("%#H %[is/are] wearing %w.\n", what, seencloth);

	NewWorld unworn;
	foreach(what->children, o, i) {
	  if (!iswornby(o, what) && o != wp && o != rid)
	    unworn.add(*o); 
	}

	NewWorld carrying;

	foreach((&unworn), o, i) {
	  if (o->get_flag(FL_EXIT))
	    continue;
	  
	  if (!o->get("name"))
	    continue;

	  if (o->get_flag(FL_EXIT))
	    continue;
	  
	  if (o == wp || o == rid)
	    continue;

	  carrying.add(o);
	}

	if (carrying)
          if (is_person(what))
            player->printf("%#H %[is/are] carrying %w.\n", what, carrying);
          else
            player->printf("%#H %[contains/contain] %w.\n", what, carrying);
    }

    if (what->get_flag(FL_CANOPEN)) {
      int stat = state(what);
      if (stat==1) {
	player->printf("%#s %s closed.\n", it_they(what), is_are(what));
      } else if (stat==2) {
	player->printf("%#s %s locked.\n", it_they(what), is_are(what));
      }
    }

    NewWorld find;

    if (!what->get_flag(FL_DISCOVERED)) {
      if (what->get("discover")) {
	MudObject *d = what->get_object("discover");
	if (d) {
	  find.add(d);
	  
	  set_owner(d, player->owner);
	  what->set_flag(FL_DISCOVERED, 1);
	  /*return true;*/	
	}
	else
	  player->printf("bug : invalid discover property on %s (%s).\n", what->id, what->get("discover"));
      }

      if (int s = what->array_size("discover")) {
	for (int i=0;i<s;i++) {
	  MudObject *d = what->array_get_object("discover", i);
	  if (d) {
	    find.add(d);
	    set_owner(d, player->owner);
	    what->set_flag(FL_DISCOVERED, 1);
	    /*return true;*/	
	  }
	}
      }
      
      if (int z = what->array_size("disc")) {
	for (int j=0;j<z;j++) {
	  MudObject *t = what->array_get_object("disc", j);
	  int c = what->array_get_int("disc", j, "count", 1);
	  if (c > 10)
	    c = 10;
	  if (t) {
	    while (c) {
	      MudObject *o2=clone_object(t, player->owner, 0);
	      o2->set_bflag(FL_DESTROYONRESET, 1);
	      o2->set_bflag(FL_NOSAVE, 1);
	      o2->set("zone", what->get("zone"));
	      o2->set("start", player->owner->id);
	      find.add(o2);
	      c--;
	    }
	  }
	}
	what->set_flag(FL_DISCOVERED, 1);
      }
    }

    if (find) {
      player->printf("Examining %P, you discover %W.\n", what, &find );
      player->oprintf(cansee && avoid(what), "%#M %[discovers/discover] %W.\n",
		      player, &find);

      set_prons(player, find);
    }


    /* ::: after_examine o1==thing examined; after everything */
    if (dotrap(E_AFTEREXAMINE, player, what, 0, 0)) return true;

    return true;
}

static MudObject *find_pot(MudObject *where) 
{
  MudObject *o;
  int i;
  foreach(where->children, o, i) {
    if (o->get_flag(FL_CASH))
      return o;
  }
  return 0;
}

bool is_droppable(const TeleObject &t)
{
  return is_droppable(t.what);
}

static bool verb_drop(MudObject *who, int argc, const char **argv) {
    if (argc == 1) {
        who->printf("Drop what?\n");
        return true;
    }

    NewWorld what = match(who, argc-1, argv+1, is_droppable, LOOK_INV|IGNORE_EXITS|IGNORE_MISSION);

    if (!who->owner) {
       set_owner(who, "@musicmud");
    }
    
    if (argc == 3) {
      {
	cashinfo c = curmatch(argc-1, argv+1);
	if (c.cur && (c.amt || c.all)) {
	  int have = cash(who, c.cur);
	  if (c.all)
	    c.amt = have;

	  if (c.amt > have) {
	    who->printf("You have only got %s.\n", formatcash(have, c.cur, 0));
	    return true;
	  }

	  if (c.amt <= 0) {
	    who->printf("Very funny.\n");
	    return true;
	  }

	  MudObject *where = who->owner;
	  MudObject *pot = find_pot(where);

	  if (!pot) 
	    pot = clone_object(MUD_CASH, who->owner, NULL);
	  
	  if (!pot)
	    throw emsg("Can't drop the cash.");

	  set_cash(pot, c.cur, cash(pot, c.cur)+c.amt);
	  set_cash(who, c.cur, cash(who, c.cur)-c.amt);

	  who->printf("You drop %s.\n", formatcash(c.amt, c.cur, 1));
	  who->oprintf("%M %[drops/drop] %s.\n", who, formatcash(c.amt, c.cur, 1));

	  describe_pot(pot);

	  return true;
	}
      }
    }

    if (what.getsize()) {
      drop_obs(who, what, 0, streq(argv[0], "put"));
      fixup_lapels(who, NULL);
      set_prons(who, what);      
    } else {
      if (who->get_flag(FL_LOGGEDIN)) {
	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("Drop any what?\n");
	else
	  who->printf("You aren't carrying anything you can drop.\n");
      }
    }

    return true;
}



static bool verb_throw(MudObject *who, int argc, const char **argv) {
    if (argc == 1) {
        who->printf("Throw what?\n");
        return true;
    }

    NewWorld what = match(who, argc-1, argv+1, is_droppable, LOOK_INV|IGNORE_EXITS|IGNORE_MISSION);
    TeleWorld what2 = multi_match(who, argc-1, argv+1, 0, LOOK_BOTH|IGNORE_EXITS|LOOK_LINKED, 0, 0, "at");
    if (what2.getsize()>1) {
      who->printf("You can only throw at one (or zero) things.\n");
      return true;
    }

    TeleObject tat = what2.getsize()?what2.get(0):0;
    if (!tat && streq(what2.prep, "at")) {
      who->printf("Throw at what?\n");
      return true;
    }

    if (!who->owner) {
       set_owner(who, "@musicmud");
    }

    if (what.getsize()) {
      drop_obs(who, what, true, 0, tat);
      set_prons(who, what);
    } else {
      if (what.all_nothing==1)
	who->printf("You aren't carrying anything.\n");
      else if (what.all_nothing==103)
	  who->printf("Throw any what?\n");
      else
	who->printf("You aren't carrying anything like that.\n");
    }

    return true;
}

static bool is_food(const TeleObject &o) {
  return is_edible(o.what) && !(o->get_flag(FL_DRINK) || o->get_flag(FL_WATERTIGHT));
}

static bool is_drink(const TeleObject &o) {
  return is_edible(o.what) && (o->get_flag(FL_DRINK) || o->get_flag(FL_WATERTIGHT));
}


static bool verb_eat(MudObject *who, int argc, const char **argv) {
    if (argc == 1) {
        who->printf("Eat what?\n");
        return true;
    }

    NewWorld what = match(who, argc-1, argv+1, is_food, LOOK_INV|LOOK_ROOM|IGNORE_EXITS|ALLOW_ME);

    if (!who->owner) {
       set_owner(who, "@musicmud");
    }

    if (what.getsize()) {
      consume_obs(who, what, 0);
      fixup_lapels(who, NULL);
    } else {
      if (what.all_nothing==2)
	who->printf("You aren't carrying anything edible.\n");
      else if (what.all_nothing==1)
	who->printf("You aren't carrying anything.\n");
      else
	who->printf("You aren't carrying anything like that.\n");
    }

    return true;
}

static bool verb_drink(MudObject *who, int argc, const char **argv) {
    if (argc == 1) {
        who->printf("Drink what?\n");
        return true;
    }

    NewWorld what = match(who, argc-1, argv+1, is_drink, LOOK_INV|LOOK_ROOM|IGNORE_EXITS|ALLOW_ME);

    if (!who->owner) {
       set_owner(who, "@musicmud");
    }

    if (what.getsize()) {
      consume_obs(who, what, 1);
      fixup_lapels(who, NULL);
    } else {
      if (what.all_nothing==2)
	who->printf("You aren't carrying anything you can drink.\n");
      else if (what.all_nothing==1)
	who->printf("You aren't carrying anything.\n");
      else
	who->printf("You aren't carrying anything like that.\n");
    }

    return true;
}

static bool is_takeable(const TeleObject &o) 
{
  if (o->get_flag(FL_FIXED)) return 0;
  if (o->get_flag(FL_EXIT)) return 0;
  if (is_player(o.what)) return 0;
  if (is_big_mobile(o.what)) return 0;
  return 1;
}

TeleObject upon(MudObject *o)
{
  return TeleObject(o->owner, o->get_object(KEY_SITON), o->get_int(KEY_SITONN));
}

static void take_obs(MudObject *player, NewWorld what, MudObject *fromwhat=0, int carehands=1) 
{
    NewWorld too_heavy, cantget;
    MudObject *o;
    int i;

    int hands = freehands(player);
    int wanthands = 0;

    int spare = mass_capacity_left_in_grams(player);

    NewWorld extra;

    map<MudObject*, MudObject*> trayed;
    set<MudObject*> tabled;
    map<MudObject*, MudObject*> tableof;

    foreach(&what, o, i) {
      MudObject *p;
      int j;
      foreach(o->owner->children, p, j) {
	if (p->get_object(KEY_SITON)==o) {
	  extra.add(p);
	  trayed[p] = o;
	}
      }
    }
    
    what += extra;


    foreach(&what, o, i) {
      if (o->get_object(KEY_SITON)) {
	tabled.insert(o);
	tableof[o] = o->get_object(KEY_SITON);
      }

      /* ::: get o1==object getting; before any checking, messaging. return 1 to abort. */
      if (dotrap(E_ONGET, player, o, 0, 0)) {
	what.remove(*o);
	i--;
	continue;
      }
      /* ::: before_getfrom o1==table o2==object being taken; return 1 to abort */
      if (tabled.find(o)!=tabled.end() 
	  && dotrap(E_BEFOREGETFROM, player, tableof[o], o)) {
	what.remove(*o);
	i--;
	continue;
      }
    }

    foreach(&what, o, i) {
      if (o==player || o == fromwhat) {
	player->printf("You cannot get yourself.\n");
	what.remove(*o);
	i--;
	continue;
      }

      if (!is_takeable(o)) {
	cantget.add(o);
	what.remove(o);
	i--;
	continue;
      }

      if (spare != SIZE_INFINITE) {
	int newspare = spare - mass_in_grams(o);
	if (newspare < 0) {
	  what.remove(*o);
	  i--;
	  too_heavy.add(*o);
	  continue;
	}
	spare = newspare;
      }

      if (carehands) {
	if (!hands) {
	  wanthands = 1;       
	  what.remove(*o);
	  i--;
	  continue;
	}
	hands--;
      }
    }

    if (what.getsize()) {
      if (!fromwhat) {
	player->printf("You get ^Z%s.\n", magiclist(what).c_str());
	player->oprintf(cansee, "%#M %[gets/get] ^Z%s.\n", player, magiclist(what).c_str());
      } else {
	player->printf("You get ^Z%s from %Y.\n", magiclist(what).c_str(), fromwhat);
	player->oprintf(cansee, "%#M %[gets/get] ^Z%s from %P.\n", player, magiclist(what).c_str(), fromwhat);
      }
    }

    foreach(&what, o, i) {
      o->printf("%#M gets you.\n", player);
      set_owner(o, player);
    }
    
    if (too_heavy.getsize()) {
      player->printf("%#s %s too heavy to carry.\n", magiclist(too_heavy).c_str(), plural(too_heavy)?"are":"is"); 
    }
    
    if (cantget) {
      player->printf("You cannot get %W.\n", cantget);
    }

    if (wanthands) {
      player->printf("You haven't enough hands/pockets to carry any more.\n");
    }

    foreach(&what, o, i) {
      collapse_cash(o);

      if (dotrap(E_ONHANDLE, player, o, 0, 0)) {
      /* ::: handle o1==object gotten; after an object has been picked up in any way. */
	what.remove(*o);
	i--;
	continue;
      }

      if (dotrap(E_AFTERGET, player, o, 0, 0)) {
      /* ::: after_get o1==object gotten; after everything */
	what.remove(*o);
	i--;
	continue;
      }

      /* ::: after_getfrom o1==table o2==object that was taken */
      if (tabled.find(o)!=tabled.end() && 
	  dotrap(E_AFTERGETFROM, player, tableof[o], o, 0)) {
	what.remove(*o);
	i--;
	continue;
      }

      if (o->get_flag(FL_GETFLIPS))
	o->set(KEY_STATE, 0);

      if (o->get_flag(FL_INVISIBLE)) 
	o->set_flag(FL_INVISIBLE, 0);
    }
    iforeach(i, trayed) {
      i->first->set(KEY_SITON, i->second);
      i->first->set(KEY_SITONN, 0);
    }
}

void do_steal(MudObject *who, TeleObject item, MudObject *victim)
{
  if (victim == who) {
    who->printf("Very funny.\n");
    return;
  }
  if (weapon(victim)==item) {
    who->printf("You can't steal a wielded weapon.\n");
    return;
  }
  if (item->get_object(KEY_WORNBY)==victim) {
    who->printf("You can't steal worn clothes.\n");
    return;
  }
  if (mount(victim)==item) {
    who->printf("You can't steal mounts.\n");
    return;
  }
  if (item->get_flag(FL_MISSION) || item->get_flag(FL_QUEST)) {
    who->printf("You can't steal missions.\n");
    return;
  }

  if (item.where != victim) {
    who->printf("You can't steal %Y.\n", &item);
    return;
  }

  if (dotrap(E_BEFORESTEAL, who, item, victim))
    /* ::: before_steal o1==object to steal, o2==victim; return 1 to abort */
    return;

  if (!is_person(victim)) {
    who->printf("You can't steal from %Y.\n", victim);
    return;
  }

  if (!is_player(victim)) {
    who->printf("%#Y is too alert.\n", victim);
    return;
  }

  if (mass_capacity_left_in_grams(who) < mass_in_grams(item) || !freehands(who)) {
    who->printf("You try to steal %M from %Y.\n", &item, victim);
    victim->printf("%#M %[tries/try] to steal %Y from you.\n", who, &item);
    who->oprintf(cansee && avoid(victim), "%#M %[tries/try] to steal %M from %M.\n", who, &item, victim);
  } else {
    who->printf("You steal %M from %Y.\n", &item, victim);
    victim->printf("%#M %[steals/steal] %Y from you.\n", who, &item);
    who->oprintf(cansee && avoid(victim), "%#M %[steals/steal] %M from %M.\n", who, &item, victim);

    set_owner(item, who);
    set_prons(who, item);
    
    dotrap(E_AFTERSTEAL, who, item, victim);
    /* ::: after_steal o1==object stolen, o2==from who? */
  }
  
}

static bool verb_steal(MudObject *who, int argc, const char **argv)
{
  NewWorld what2 = match(who, argc-1, argv+1, NULL, LOOK_BOTH|IGNORE_EXITS, "from");
  TeleWorld what;

  if (what2.getsize()==0) {
    if (!what2.prep) {

      what = multi_match(who, argc-1, argv+1, NULL, LOOK_BOTH|LOOK_MWIELD|IGNORE_EXITS);
      if (what.getsize()>1) {
	who->printf("You can only steal one thing at a time.\n");
	return true;
      }
      if (what.getsize()==0) {
	who->printf("Steal what?\n");
	return true;
      }

      TeleObject o = what.get(0);
      if (!is_person(o.where)) {
	who->printf("Er, you can only steal from people.\n");
	return true;
      }

      do_steal(who, o.what, o.where);
      return true;
    }
    who->printf("Steal from who?\n");
    return true;
  } else if (what2.getsize()>1) {
    who->printf("You can only steal from one person at a time.\n");
    return true;
  }
  
  MudObject *victim = what2.get(0);

  what = multi_match(who, argc-1, argv+1, is_droppable, LOOK_SPECIAL, NULL, victim->children);

  if (what.getsize()==0) {
    who->printf("Steal what?\n");
    return true;
  }
  if (what.getsize()>1) {
    who->printf("You can only steal one thing at a time.\n");
    return true;
  }

  MudObject *item = what.get_nth(0);

  do_steal(who, item, victim);

  return true;
}

static bool verb_loot(MudObject *who, int argc, const char *argv[]) {
  NewWorld what = match(who, argc-1, argv+1, is_corpse, LOOK_BOTH|IGNORE_EXITS);

  if (what.getsize()==0) {
    who->printf("Loot what?\n");
    return true;
  }
  if (what.getsize()>1) {
    who->printf("You can only loot one thing at a time.\n");
    return true;
  }

  MudObject *corpse = what.get_nth(0);
  if (!is_corpse(corpse)) {
    if (is_person(corpse)) {
      who->printf("%#Y %[isn't/aren't] dead.\n", corpse);
    } else {
      who->printf("%#Y %[isn't/aren't] a corpse.\n", corpse);
    }
    return true;
  }

  NewWorld what2;
  int i;
  MudObject *o;
  foreach(corpse->children, o, i) {
    what2.add(*o);
  }

  take_obs(who, what2, corpse);
  set_prons(who, what2);

  return true;
}

static bool verb_get(MudObject *who, int argc, const char *argv[]) {
  if (streq(argv[1], "off")) {
    Verb *v = verbs->get("remove");
    if (v)
      v->invoke(who, argc-1, argv+1);
    else
      who->printf("Remove not loaded.\n");
    return true;
  }


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

  if (argc == 3) {
    {
      cashinfo c = curmatch(argc-1, argv+1);
      if (c.cur) {
	MudObject *pot = find_pot(who->owner);
	if (!pot)
	  throw emsg("There is no cash here.");

	int avail = cash(pot, c.cur);

	if (c.all)
	  c.amt = avail;

	if (c.amt<0)
	  throw emsg("You can't take negative money.");
	
	if (avail<c.amt)
	  throw emsg("There isn't that much there.");
	
	set_cash(pot, c.cur, cash(pot, c.cur)-c.amt);
	set_cash(who, c.cur, cash(who, c.cur)+c.amt);
	
	if (totalcash(pot)==0)
	  vanish(pot);
	
	who->printf("You take %s.\n", formatcash(c.amt, c.cur, 1));
	who->oprintf("%M %[takes/take] %s.\n", who, formatcash(c.amt, c.cur, 1));

	describe_pot(pot);

	return true;
      }
    }
  }

  TeleObject container = NULL;

  if (strhas(argc, argv, "from")) {
      TeleWorld what2 = multi_match(who, argc-1, argv+1, NULL, LOOK_BOTH|IGNORE_EXITS, 0, 0, "from");

      if (what2.getsize()==0) {
	who->printf("Take from what?\n");
	return true;
      } else if (what2.getsize()>1) {
	who->printf("You can only take from one thing at a time.\n");
	return true;
      }
      
      container = what2.get(0);
      if (container->get_flag(FL_FLOOR))
	container = 0;
      else {
	if (!container->get_flag(FL_TABLE)) {
	  if (!is_container(container) && !is_corpse(container)) {
	    who->printf("%#Y %[isn't/aren't] a container.\n", &container);
	    return true;
	  }
	  
	  if (is_closed(container) && !is_corpse(container)) {
	    who->printf("%#Y %[isn't/aren't] open.\n", &container);
	    return true;
	  }
	}
      }
  }


  MudObjectWorld stuff;
  if (container) {
    stuff = *container->children;
    MudObject *o;
    int i;
    foreach(container.where->children, o, i) {
      if (low_enough(who, o) && (upon(o)==container)) {
	stuff.add(*o);
      }
    }
  }

  NewWorld what = match(who, argc-1, argv+1, is_takeable,
			container?(LOOK_SPECIAL|IGNORE_EXITS):
			          (LOOK_ROOM|IGNORE_EXITS),
			NULL, &stuff);
  
  
  if (what.prep) {
    if (container)
      who->printf("Take what from %Y?\n", &container);
    else
      who->printf("Take what?\n");
    return true;
  }    

  if (what.getsize()) {
    take_obs(who, what, container);
    set_prons(who, what);
    return true;
  } 

  if ((who->owner->get_flag(FL_ONFIRE)||!has_light(who->owner))&&!wf_wears_with_flag(who, FL_NIGHTVISION)) {
    if (who->owner->get_flag(FL_ONFIRE))
      who->printf("You can't see anything through the smoke and flames.\n");
    else
      who->printf("You can't see anything in the dark.\n");
    return true;
  }

  if (what.all_nothing==0) {
      who->printf("There is nothing like that %s.\n", container?"in there":"here");
      return true;
  } else if (what.all_nothing!=103) {
    who->printf("There is nothing to take %s.\n", container?"from there":"here");
    return true;
  } else {
    who->printf("Take any what?\n");
    return true;
  }

  return true;
}

static int strfind(int argc, const char **argv, const char *what) {
    for (int i = 0 ; i < argc ; i++) {
	if (streq(argv[i], what)) return i;
    }
    return 0;
}

static void do_pay(MudObject *who, MudObject *vic, cashinfo c)
{
  int have = cash(who, c.cur);

  if (c.all)
    c.amt = have;

  if (c.amt > have) {
    who->printf("You don't have that much.\n");
    return;
  }

  if (c.amt <= 0) {
    who->printf("Very funny.\n");
    return;
  }

  if (!is_person(vic)) {
    who->printf("You can't pay %M.\n", vic);
    return;
  }

  if (vic == who) {
    who->printf("Very funny.\n");
    return;
  }

  if (dotrap(E_BEFOREPAID, who, vic, c.cur, ssprintf("%i", c.amt).c_str()))
    return;
  /* ::: before_paid o1==mobile paid, o2==the currency, txt==amount to be paid; before messaging/cash transfer, return 1 to abort */

  string sum = formatcash(c.amt, c.cur, 1);

  who->printf("You give %M %s.\n", vic, sum);
  who->oprintf(cansee && avoid(vic), "%#M %[gives/give] %M %s.\n", who, vic, sum);
  vic->printf("%#M %[gives/give] you %s.\n", who, sum);

  set_cash(who, c.cur, cash(who, c.cur)-c.amt);
  set_cash(vic, c.cur, cash(vic, c.cur)+c.amt);

  if (dotrap(E_AFTERPAID, who, vic, c.cur, ssprintf("%i", c.amt).c_str()))
    return;
}

static bool verb_pay(MudObject *who, int argc, const char **argv) 
{
  int i = strfind(argc, argv, "to");
  if (i<2) {
    who->printf("Pay what to who?\n");
    return true;
  }

  NewWorld what = match(who, argc-i, argv+i, is_person, LOOK_ROOM|IGNORE_EXITS|ALLOW_ME);
  if (what.getsize()==0) {
    who->printf("Pay to who?\n");
    return true;
  }
  if (what.getsize()>1) {
    who->printf("You can only pay to one person at a time.\n");
    return true;
  }

  cashinfo c = curmatch(i-1, argv+1, currency(who));

  if (!c.cur) {
    who->printf("Pay what?\n");
    return true;
  }

  MudObject *vic = what.get(0);
  do_pay(who, vic, c);

  return 1;
}

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 void give_obs(MudObject *from, MudObject *to, NewWorld what) 
{

  MudObject *o;
  int i;

  if (from==to) {
    from->printf("You already have %s.\n", plural(what)?"them":"it");
    return;
  }

  NewWorld cantcarry;

  int spare = mass_capacity_left_in_grams(to);

  what = try_remove_obs(from, what);

  foreach((&what), o, i) {

    if (to==o) {
      what.remove(*o);
      i--;
      if (!what.getsize()) {
	from->printf("You can't give %Y to %sself!\n", o, his_or_her(o));
	return;
      }
      continue;
    }

    if (is_in(to, o)) {
      from->printf("Giving %Y to %Y would cause a paradox.\n", o, to);
      what.remove(*o);
      i--;
      continue;
    }

    from->spec_printf("You show %Y to %M.\n", o, to);
    if (dotrap(E_AFTERSHOWNTO, from, o, to)) {
    /* ::: after_shown_to o1==object, o2==mobile; return 1 if we saw it. */
      what.remove(*o);
      i--;
      continue;
    }
    from->cancel_printf();

    MudObject *ta = to->get_object("treatas")?:to;
    if (o->get(KEY_ACCEPTER) && (to->get("lua.shown_mission")||
	ta->get("lua.shown_mission"))) {
      from->spec_printf("You show %Y to %M.\n", o, to);
      /* ::: shown_mission o1==mobile, o2==mission; return 1 to not want to see the mission. */
      if (!dotrap(E_ONSHOWNMISSION, from, to, o)) {
	from->cancel_printf();
	from->printf("%#M doesn't want to see %Y.\n", to, o);
      }
      what.remove(*o);
      i--;
      continue;
    }

    from->spec_printf("You show %Y to %M.\n", o, to);
    int abort = !dotrap(E_AFTERSHOWN, from, to, o);
    /* ::: after_shown o1==mobile, o2==object; return 1 to not want to see. */
    MudObject *obj = o;
    if (MudObject *b=obj->get_object("cloneof")) obj = b;
    if (MudObject *b=obj->get_object("treatas")) obj = b;
    const char *i=0;
    if (!i) i = to->get(ssprintf("shown_%s.%i", obj->id, obj->get_int(KEY_STATE, 0)).c_str());
    if (!i) i = to->get(ssprintf("shown_%s", obj->id).c_str());
    if (i) {
      from->oprintf(cansee, "%#M %[shows/show] %P to %M.\n", from, o, to);
      to->interpret(i);
      abort = 0;
    }
    if (abort) {
      from->cancel_printf();
    } else {
      what.remove(*o);
      i--;
      continue;
    }

    if (o->get_flag(FL_NOGIVE)) {
      from->printf("%#M would self-destruct.\n", o);
      what.remove(*o);
      i--;
      continue;
    }

    if ((o->get_flag(FL_MISSION) || o->get_flag(FL_QUEST)) && is_player(to)
	&& is_player(from)) {
      from->printf("You can't give missions to players.\n");
      what.remove(*o);
      i--;
      continue;
    }

    if (is_player(o) || (is_mobile(o) && o->get_flag(FL_FIXED))) {
      from->printf("You can't get rid of %Y so easily.\n", o);
      what.remove(*o);
      i--;
      continue;
    }

    if (o->owner != from) {
      from->printf("You aren't carrying %Y.\n", o);
      what.remove(*o);
      i--;
      continue;
    }


    if (spare != SIZE_INFINITE) {
      int newspare = spare - mass_in_grams(o);
      if (newspare < 0) {
	what.remove(*o);
	i--;
	cantcarry.add(*o);
	continue;
      }
      spare = newspare;
    }

    if (dotrap(E_ONGIVEN, from, o, to)) {
    /* ::: given o1==gift, o2==mobile; after basic validity checking. return 1 to abort */
      what.remove(*o); i--; continue;
    }
    /* ::: give o1==mobile, o2==gift; after basic validity checking. return 1 to abort */
    if (dotrap(E_ONGIVE, from, to, o)) {
      what.remove(*o);
      i--;
      continue;
    }

  }

  string ml = magiclist(what);

  if (what.getsize()) {
    from->printf("You give ^Z%s to %Y.\n", ml.c_str(), to);
    from->oprintf(cansee && avoid(to), "%#M %[gives/give] ^Z%s to %Y.\n", from, ml.c_str(), to);
    to->printf("%#M %[gives/give] ^Z%s to you.\n", from, ml.c_str());
  }

  if (cantcarry.getsize()) {
    from->printf("%#Y can't carry ^Z%s.\n", to, magiclist(cantcarry).c_str());
  }

  foreach((&what), o, i) {
    o->printf("%#M gives you to %Y.\n", from, to);

    set_owner(o, to);

    if (dotrap(E_AFTERGIVEN, from, o, to)) {
    /* ::: after_given o1==gift, o2==mobile; after object given; return 1 to suppress an auto-give-back */
      what.remove(*o); i--; continue;
    }
    if (dotrap(E_AFTERGIVE, from, to, o)) {
    /* ::: after_give o1==mobile, o2==gift; after object given; return 1 to suppress an auto-give-back */
      what.remove(*o); i--; continue;
    }
  }

  if (is_mobile(to) && is_player(from) && to->get_int("aggr",0)==0 &&
      !to->get_flag(FL_CANTTALK)) {
    MudObject *only = 0;
    if (what.getsize()==1) {
      only = what.get(0);
    }

    if (what.getsize()) {
      const char *response="shake";
      if ((!only ||
	   (!only->get_flag(FL_QUEST)
	    && !only->get_flag(FL_MISSION))) &&
	  to->owner && to->owner->get_flag(FL_STORE) && to->owner->get_object("shopmob")==to) {
	response = lang("say If you want to sell it, use the '^WSELL^n' command.", to);
      }
      foreach((&what), o, i) if (o->get_flag(FL_CONTRABAND)) {
	response = lang("say No way...", to);
      }

      if (what.getsize()==1) {
	log(class can_affect(to->get("zone")), FL_NOTELLSPAM, PFL_SEEINFO, 0, "give", "%s didn't want %s", drillid(to), drillid(what.get_nth(0)));
      }

      to->interpret(response);
      to->oprintf(cansee && avoid(from), "%#Y %[gives/give] ^Z%s back to %Y.\n", to, magiclist(what).c_str(), from);
      from->printf("%#Y %[gives/give] ^Z%s back to you.\n", to, magiclist(what).c_str());
    }
    foreach((&what), o, i) {
      set_owner(o, from);
    }
  }
}

static bool verb_give(MudObject *who, int argc, const char **argv)
{
  TeleWorld what2 = multi_match(who, argc-1, argv+1, is_person, LOOK_ROOM|IGNORE_EXITS|ALLOW_ME|LOOK_LINKED, NULL,
				NULL, "to");

  MudObject *to;
  int i;
  int y = 0;

  foreach((&what2), to, i) {
    
    if (!is_person(to)) {
      continue;
    }

    y = 1;

    if (what2.get(i).where != who->owner) {
      who->printf("%#M %[is/are] too far away.\n", to);
      continue;
    }

    if (to->get_flag(FL_SLEEPING)) {
      who->printf("%#Y is asleep.\n", to);
      continue;
    }

    NewWorld what = match(who, argc-1, argv+1, is_droppable, LOOK_INV|IGNORE_EXITS);
    if (what.getsize()) {
      give_obs(who, to, what);
      fixup_lapels(who, NULL);
    } else {
      if (what.all_nothing==2) {
	who->printf("You aren't carrying anything you can give.\n");
        break;
      }
      else if (what.all_nothing==1) {
	who->printf("You aren't carrying anything.\n");
	break;
      }
      else if (what.all_nothing==0) {
	int i = strfind(argc, argv, "to");
	if (i>=2) {
	  cashinfo c = curmatch(i-1, argv+1, currency(who));
	  if (c.cur) {
	    do_pay(who, to, c);
	    continue;
	  }
	}
	who->printf("You aren't carrying anything like that.\n");
	break;
      }
      else if (what.all_nothing==-1) {
	who->printf("Give what?\n");
	break;
      }
    }
    y = 1;
  }

  if (!y) {
    who->printf("Give to who?\n");
  }

  return true;
}


static bool verb_drinkfind(MudObject *who, int argc, const char **argv) {
  MudObject *o;
  int i;
  foreach_alpha(planet, o, i) if (o->get_flag(FL_DRINK)) {
    MudObject *em = o->get_object("empty");
    if (em) {
      who->printf("%20s %-35M - %20M - %4i - %2i.%02i%%.\n", o->id, o, em, serving(em), abv(o)/100, abv(o)%100);
    } else {
      who->printf("%20s %-35M.\n", o->id, o);
    }
  }
  return true;
}



static bool verb_fill(MudObject *who, int argc, const char **argv) {
  if (argc < 2) {
    who->printf("%#s what?\n", argv[0]);
    return true;
  }

  if (streq(argv[0], "fill")) {
    if (!strhas(argc, argv, "from") && !strhas(argc, argv, "with")) {
      who->printf("Fill with what?\n");
      return true;
    }
  }

  if (streq(argv[0], "pour")) {
    if (!strhas(argc, argv, "into")) {
      who->printf("Pour into what?\n");
      return true;
    }
  }

  NewWorld first;
  NewWorld second;

  if (streq(argv[0], "fill")) {
    first = match(who, argc-1, argv+1, is_fillable, LOOK_BOTH|IGNORE_EXITS);
    second = match(who, argc-1, argv+1, is_fillable, LOOK_BOTH|IGNORE_EXITS, "from");
    if (second.getsize()==0)
      second = match(who, argc-1, argv+1, is_fillable, LOOK_BOTH|IGNORE_EXITS, "with");
  } else {
    second = match(who, argc-1, argv+1, is_fillable, LOOK_BOTH|IGNORE_EXITS);
    first = match(who, argc-1, argv+1, is_fillable, LOOK_BOTH|IGNORE_EXITS, "into");
  }
    
  if (first.getsize()==0) {
    if (first.all_nothing == 2) {
      who->printf("You aren't carrying anything you can %s.\n", argv[0]);
    } else {
      who->printf("%#s what?\n", argv[0]);
    }
    return true;
  }

  if (first.getsize()>1) {
    who->printf("You can only fill one thing at a time.\n");
    return true;
  }

  if (second.getsize()==0) {
    who->printf("%#s %Y from what?\n", argv[0], first.get(0));
    return true;
  }

  if (second.getsize()>1) {
    who->printf("You can only pour from one thing.\n");
    return true;
  }

  MudObject *what = first.get(0);
  MudObject *from = second.get(0);

  if (from == what) {
    who->printf("You can't fill %Y from itself.\n", what);
    return true;
  }

  if (!is_fillable(what) || (what->get_int("volume")<1)) {

    if (what->get_object("empty")) {
      who->printf("It is already full of %s.\n", subshort(what));
    } else {
      who->printf("You can't fill %Y from %Y.\n", what, from);
    }
    return true;
  }

  MudObject *sub = from->get_object("fount");
  int fromamt = -1;
  int replace = 0;

  if (streq(argv[0], "pour") && sub) {
    who->printf("You can't pour from %Y.\n", sub);
    return true;
  }

  if (!sub && from->array_size("$fill")) {
    sub = from->array_get_object("$fill", 0);
    fromamt = from->array_get_int("$fillamt", 0);
  }

  if (!sub && from->get_object("empty") && from->get_flag(FL_DRINK)) {
    sub = from->get_object("substance");
    if (!sub) {
      sub = from;
      if (sub->get_object("cloneof"))
	sub = sub->get_object("cloneof");
    }
    fromamt = serving(from);
    replace = 1;
  }

  if (!sub) {
    who->printf("You can't fill %Y from %Y.\n", what, from);
    return true;
  }

  MudObject *already = 0;
  int alvol = 0;

  if (what->array_size("$fill")!=0) {
    already = what->array_get_object("$fill", 0);
    alvol = what->array_get_int("$fillamt", 0, 0);

    if (already != sub) {
      if (!streq(subshort(already), subshort(sub))) {
	who->printf("%#Y already %[contains/contain] %s.\n", what, subshort(already));
      } else {
	who->printf("%#Y already %[contains/contain] a different type of %s.\n", what, subshort(already));
      }
      return true;
    }
  }

  if (already == sub) {
    if (used_volume(what) >= what->get_int("volume")) {
      who->printf("%#Y %[is/are] already full.\n", what);
      return true;
    }
  }

  if (dotrap(E_BEFOREFOUNTAIN, who, from, what, 0)) {
    return true;
  }

  const char *adjective = "";

  int wanted = what->get_int("volume") - used_volume(what);
  int amount = wanted;

  if (fromamt != -1 && wanted > fromamt) {
    adjective = "partially ";
    amount = fromamt;
  }

  what->array_set("$fill", 0, sub);
  what->array_set("$fillamt", 0, amount + alvol);

  who->printf("You %sfill %Y with %s from %Y.\n", adjective, what, subshort(sub), from);
  who->oprintf(cansee, "%#M %s%[fills/fill] %P with %s from %P.\n", who, adjective, what, subshort(sub), from);

  if (fromamt != -1) {
    if (replace) {
      MudObject *rw = replace_with_empty(from);
      if ((fromamt - amount)>0) {
	rw->array_set("$fill", 0, sub);
	rw->array_set("$fillamt", 0, fromamt - amount);
      }
    } else {
      from->array_set("$fillamt", 0, fromamt - amount);
      consolidate(from, false);
    }
  }

  /* ::: after_fill o1==object, o2==substance; after the object has been perhaps (partially) filled with substance */
  if (dotrap(E_AFTERFILL, who, what, sub, 0)) {
    return true;
  }


  return true;
}

static bool verb_pour(MudObject *who, int argc, const char **argv)
{
  return verb_fill(who, argc, argv);
}


static bool verb_taste(MudObject *who, int argc, const char **argv)
{
  NewWorld w = match(who, argc-1, argv+1, 0, LOOK_INV|IGNORE_EXITS|ALLOW_ME);
  if (w.getsize()==0) {
    who->printf("Taste what?\n");
    return true;
  } else if (w.getsize()>1) {
    who->printf("You can only taste one thing at a time.\n");
    return true;
  }
  
  MudObject *what = w.get_nth(0);

  if (!what) {
    who->printf("Taste what?\n");
    return true;
  }

  set_prons(who, what);

  if (!is_edible(what) && !what->get("taste")) {
    if (who==what)
      who->printf("Tastes like meat.\n");
    else
      who->printf("You can't taste %Y.\n", what);
    return true;
  }

  if (what->get("taste")) {
    who->oprintf(cansee, "%#Y %[tastes/taste] %P.\n", who, what);
    who->printf("%s\n", what->get("taste"));
    return true;
  }

  MudObject *sub = what->array_get_object("$fill", 0);
  if (!sub)
    sub = what->get_object("substance");
  if (!sub)
    sub = what;

  int alc = abv(sub);

  if (sub==what)
    who->oprintf(cansee, "%#M %[tastes/taste] %P.\n", who, what);
  else
    who->oprintf(cansee, "%#M %[tastes/taste] the contents of %P.\n", who, what);

  if (alc>4000)
    who->printf("%|%[It tastes/They taste] very strongly of alcohol.\n", what);
  else if (alc>2000)
    who->printf("%|%[It tastes/They taste] strongly of alcohol.\n", what);
  else if (alc>200)
    who->printf("%|%[It tastes/They taste] of alcohol.\n", what);
  else
    who->printf("%|%[It tastes/They taste] like %s.\n", what, sub->get("short"));

  return true;
}

bool prep(const char *o);

static bool verb_empty(MudObject *player, int argc, const char*argv[]) {
    if (argc < 2) {
	player->printf("Empty what?\n");
	return true;
    }
    
    NewWorld w = match(player, argc-1, argv+1, 0, LOOK_BOTH|IGNORE_EXITS|ALLOW_ME);
    if (w.getsize()==0) {
      player->printf("Empty what?\n");
      return true;
    } else if (w.getsize()>1) {
      player->printf("You can only empty one thing at once.\n");
      return true;
    }

    for (int i=1;i<argc;i++) {
      if (prep(argv[i])) {
	player->printf("You can't empty %s things.\n", argv[i]);
	return true;
      }
    }

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

    MudObject *what = w.get_nth(0);
    
    if (!what) {
	player->printf("It's not here.\n");
	return true;
    }
    
    set_prons(player, what);
      
    /* ::: empty o1==object; before openness checking; return 1 to abort */
    if (dotrap(E_ONEMPTY, player, what, 0, 0)) 
      return true;

    if (!is_container(what) && !is_fillable(what) && !(what->get_object("empty") && what->get_object("empty")->
						       get_flag(FL_WATERTIGHT))) {
	player->printf("%#Y %[is/are] not a container.\n", what);
	return true;
    }
    
    if (is_closed(what)) {
	player->printf("%#Y %[is/are] closed.\n", what);
	return true;
    }

    if (is_fillable(what)) {
      if (!used_volume(what)) {
	player->printf("%#Y %[is/are] already empty.\n", what);
	return true;
      }

      MudObject *stuff = what->array_get_object("$fill", 0);
      if (stuff) {
	if (fl) {
	  player->printf("You pour the %s from %Y onto %Y.\n", subshort(stuff), what, fl);
	  player->oprintf(cansee, "%#M %[pours/pour] the %s from %P onto %Y.\n", player, subshort(stuff), what, fl);
	} else {
	  player->printf("You pour the %s from %Y.\n", subshort(stuff), what);
	  player->oprintf(cansee, "%#M %[pours/pour] the %s from %P.\n", player, subshort(stuff), what);
	}
      }
      consolidate(what, true);
      return true;
    }

    if (what->get_object("empty")) {
      if (fl) {
	player->printf("You empty %Y onto %Y.\n", what, fl);
	player->oprintf(cansee, "%#M %[empties/empty] %P onto %Y.\n", player, what, fl);
      } else {
	player->printf("You empty %Y.\n", what);
	player->oprintf(cansee, "%#M %[empties/empty] %P.\n", player, what);
      }
      replace_with_empty(what);
      return true;
    }
    
    bool done = false;
    
    int i;
    MudObject *which;
    
    NewWorld emptied;

    foreach(what->children, which, i) {
      if (!which->get_flag(FL_FIXED)) {
	emptied.add(*which);
	set_owner(which, player->owner);
        dotrap(E_ONHANDLE, player, which, 0, 0);
	i--;
      }
      done = true;
    }

    if (emptied.getsize()) {
      player->printf("You empty ^Z%s from %Y.\n", magiclist(emptied).c_str(), what);
      player->oprintf(cansee, "%#M %[empties/empty] ^Z%s from %Y.\n", player, magiclist(emptied).c_str(), what);
      set_prons(player, emptied);
    }
    
    if (!done) {
      player->printf("%#Y %[is/are] empty already.\n", what);
      return true;
    }

    return true;
}

static MudObject *has_onfire(MudObject *who) {
  int i;
  MudObject *o;
  foreach(who->children, o, i) {
    if (o->get_flag(FL_ONFIRE)) {
      return o;
    }
  }
  return 0;
}


static bool verb_light(MudObject *who, int argc, const char **argv) {

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

  NewWorld w = match(who, argc-1, argv+1, 0, LOOK_BOTH|IGNORE_EXITS|ALLOW_ME);
  if (w.getsize()==0) {
    who->printf("Light what?\n");
    return true;
  } else if (w.getsize()>1) {
    who->printf("You can only light one thing at once.\n");
    return true;
  }

  MudObject *what = w.get_nth(0);
  set_prons(who, what);
  
  /* ::: light o1==object; before anything; return 1 to abort */
  if (dotrap(E_ONLIGHT, who, what)) return true;

  set_prons(who, what);

  if (is_player(what) || is_mobile(what)) {
    who->printf("You can't light %s.\n", him_or_her(what));
    return true;
  }

  MudObject *source = has_onfire(who);
  if (!source) source = has_onfire(who->owner);
  if (!source) {
    who->printf("You don't have anything to light %s from.\n", it_them(what));
    return true;
  }

  if (!what->get_flag(FL_CANLIGHT)) {
    who->printf("You can't light that.\n"); // XXXX that/those?
    return true;
  }

  if (is_lit(what)) {
    who->printf("%#Y %[is/are] already alight.\n", what);
    return true;
  }

  who->printf("You light %Y from %Y.\n", what, source);
  who->oprintf(cansee, "%#M %[lights/light] %P from %P.\n", who, what, source);
  what->set_flag(FL_ONFIRE, 1);

  if (what->get_int("burnsfor", 0)) {
    what->set("!burnout", now+what->get_int("burnsfor", 0));
  }

  return true;
}

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

    NewWorld w = match(who, argc-1, argv+1, 0, LOOK_BOTH|IGNORE_EXITS|ALLOW_ME);
    if (w.getsize()==0) {
      who->printf("Extinguish what?\n");
      return true;
    } else if (w.getsize()>1) {
      who->printf("You can only extinguish one thing at once.\n");
      return true;
    }
  
    MudObject *what = w.get_nth(0);

  /* ::: extinguish o1==object; before anything; return 1 to abort */
  if (dotrap(E_ONEXTINGUISH, who, what)) return true;

  if (is_player(what) || is_mobile(what)) {
    who->printf("You can't extinguish %s.\n", him_or_her(what));
    return true;
  }

  if (!is_lit(what)) {
    who->printf("%#Y %[is/are] not alight.\n", what);
    return true;
  }

  if (!what->get_flag(FL_ONFIRE)) {
    who->printf("%#Y %[isn't/aren't] on fire.\n", what);
    return true;
  }

  if (!what->get_flag(FL_CANLIGHT)) {
    who->printf("You can't extinguish that.\n");
    return true;
  }

  who->printf("You extinguish %Y.\n", what);
  who->oprintf(cansee, "%#M %[extinguishes/extinguish] %P.\n", who, what);
  what->set_flag(FL_ONFIRE, 0);

  return true;
}


static void puton_obs(MudObject *who, NewWorld &what, TeleObject on) 
{
  if (!shelf_low_enough(who, on)) {
    who->printf("%#P %[is/are] too high for you.\n", &on);
    return;
  }

  MudObject *o;
  int i;

  foreach((&what), o, i) {
    /* ::: before_puton o1==table, o2==object you are putting on it; return 1 to abort */
    if (dotrap(E_BEFOREPUTON, who, on, o)) {
      what.remove(o);
      i--;
      continue;
    }

    /* ::: before_putupon o1==object, o2==table; return 1 to abort */
    if (dotrap(E_BEFOREPUTUPON, who, o, on)) {
      what.remove(o);
      i--;
      continue;
    }
  }

  foreach((&what), o, i) {
    set_owner(o, on.where);
    o->set(KEY_SITON, on.what->id);
    o->set(KEY_SITONN, on.nth);
  }

  if (what) {
    who->printf("You put ^Z%s onto %Y.\n", magiclist(what).c_str(), &on);
    who->oprintf(cansee && avoid(on), "%#M %[puts/put] ^Z%s onto %P.\n", who, magiclist(what).c_str(), &on);
    on->printf("%#M %[puts/put] ^Z%s onto you.\n", who, magiclist(what).c_str());
  }

  foreach((&what), o, i) {
    /* ::: after_puton o1==table, o2==object you have put on it */
    if (dotrap(E_AFTERPUTON, who, on, o)) {
      what.remove(o);
      i--;
      continue;
    }

    /* ::: after_putupon o1==object you have put on it, o2==table */
    if (dotrap(E_AFTERPUTUPON, who, o, on)) {
      what.remove(o);
      i--;
      continue;
    }
  }

}

static void put_obs(MudObject *who, NewWorld &what, MudObject *in) 
{
  what = try_remove_obs(who, what);

  if (!is_container(in)) {
    MudObject *o;
    int i;
    foreach((&what), o, i) {
      /* ::: put o1==object, o2==container; called whether or not o2 is a container, return 1 to abort */
      if (dotrap(E_ONPUT, who, o, in, 0)) {
	what.remove(*o);
	i--;
	continue;
      }
      
      /* ::: contain o1==container, o2==object; called whether or not o1 is a container, return 1 to abort */
      if (dotrap(E_ONCONTAIN, who, in, o, 0)) {
	what.remove(*o);
	i--;
	continue;
      }
    }

    if (what.getsize()) {
      if (in->get_flag(FL_MOBILE)) {
        who->printf("%#M wouldn't appreciate it.\n", in);
        return;
      }
      who->printf("%#Y %[is/are] not a container.\n", in);
    }
    return;
  }

  if (is_closed(in)) {
    MudObject *o;
    int i;
    foreach((&what), o, i) {
      if (dotrap(E_ONPUT, who, o, in, 0)) {
	what.remove(*o);
	i--;
	continue;
      }
      
      if (dotrap(E_ONCONTAIN, who, in, o, 0)) {
	what.remove(*o);
	i--;
	continue;
      }
    }

    if (what.getsize())
      who->printf("%#Y %[is/are] closed.\n", in);
    return;
  }

  NewWorld justcant, selfdestruct, fixed, wontfit;

  MudObject *o;
  int i;

  int spare = mass_capacity_left_in_grams(in);
  int sparevol = in->get_int("volume", SIZE_INFINITE) - used_volume(in);

  foreach((&what), o, i) {
    if (is_in(in, o)) {
      justcant.add(*o);
      what.remove(*o);
      i--;
      continue;
    }

    if (o->get_flag(FL_NOGIVE)) {
      selfdestruct.add(*o);
      what.remove(*o);
      i--;
      continue;
    }

    if (o->get_flag(FL_FIXED) || is_player(o) || is_big_mobile(o)) {
      fixed.add(*o);
      what.remove(*o);
      i--;
      continue;
    }

    if (MudObject *cs=in->get_object("contains")) {
      MudObject *ta = o->get_object("treatas");
      if (!ta) {
	ta = o;
      }
      if (cs != ta) {
	what.remove(*o);
	i--;
	wontfit.add(*o);
	continue;
      }
    }

    if (spare != SIZE_INFINITE) {
      int newspare = spare - mass_in_grams(o);
      if (newspare < 0) {
	what.remove(*o);
	i--;
	wontfit.add(*o);
	continue;
      }
      spare = newspare;
      }

    if (sparevol != SIZE_INFINITE) {
      int newspare = sparevol - (o)->get_int("ovolume", 0);
      if (newspare < 0) {
	what.remove(*o);
	i--;
	wontfit.add(*o);
	continue;
      }
      sparevol = newspare;
      }
    
    if (dotrap(E_ONHANDLE, who, o, 0, 0)) {
      what.remove(*o);
      i--;
      continue;
    }
   
    if (dotrap(E_ONPUT, who, o, in, 0)) {
      what.remove(*o);
      i--;
      continue;
    }

    if (dotrap(E_ONCONTAIN, who, in, o, 0)) {
      what.remove(*o);
      i--;
      continue;
    }
  }

  foreach((&what), o, i) {
    set_owner(o, in);
  }

  if (what.getsize()) {
    who->printf("You put ^Z%s in %Y.\n", magiclist(what).c_str(), in);
    who->oprintf(cansee && avoid(in), "%#M %[puts/put] ^Z%s in %P.\n", who, magiclist(what).c_str(), in);
    in->printf("%#M %[puts/put] ^Z%s in you.\n", who, magiclist(what).c_str());
  }

  if (selfdestruct.getsize()) {
    who->printf("%#s would self-destruct.\n", magiclist(selfdestruct).c_str(), in);
  }

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

  if (fixed.getsize()) {
    who->printf("You can't remove %s to put it in %P.\n", magiclist(fixed).c_str(), in);
  }

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

  foreach((&what), o, i) {
    dotrap(E_AFTERPUT, who, o, in, 0);
    /* ::: after_put o1==object, o2==container; called after everything */
    dotrap(E_AFTERCONTAIN, who, in, o, 0);
    /* ::: after_contain o1==container, o2==object; called after everything */
  }
 
  /* AFTERPUT trigger on everything */
}


static bool is_puttable(const TeleObject &what) {
  MudObject *w = what.what;
  if (w->get_flag(FL_FIXED))
      return 0;
  if (is_big_mobile(w))
    return 0;
  if (is_player(w))
    return 0;
  if (!is_person(what.where))
    return 0;
  return 1;
}

static bool verb_put(MudObject *who, int argc, const char *argv[]) {

  if (streq(argv[1], "on")) {
    Verb *v = verbs->get("wear");
    if (v)
      v->invoke(who, argc-1, argv+1);
    else
      who->printf("Wear not loaded.\n");
    return true;
  }

    if (argc < 4) { 
	who->printf("Put what in what?\n");
	return true;
    }

    NewWorld from = match(who, argc-1, argv+1, NULL, LOOK_BOTH|IGNORE_EXITS|ALLOW_ME, "from");

    if (from.prep) {
      NewWorld what2 = match(who, argc-1, argv+1, NULL, LOOK_BOTH|IGNORE_EXITS|ALLOW_ME, "in");
      
      if (from.getsize()==0) {
	who->printf("Put from what?\n");
	return true;
      }

      if (from.getsize()!=1) {
	who->printf("You can only put stuff from one thing at a time.\n");
	return true;
      }

      if (what2.getsize()==0) {
 	who->printf("Put in what?\n");	
	return true;
      }
      
      if (what2.getsize()!=1) {
	who->printf("You can only put stuff into one thing at a time.\n");
	return true;
      }

      MudObject *cont = from.get_nth(0);
      NewWorld what = match(who, argc-1, argv+1, 0, LOOK_SPECIAL|IGNORE_EXITS, 
			    0,
			    cont->children);

      if (what.getsize()==0) {
	who->printf("Put what?\n");
	return true;
      }

      if (cont == what2.get_nth(0)) {
	who->printf("%#s already there.\n", plural(what)?"they're":"it's");
	return true;
      }

      take_obs(who, what, cont, 0);

      NewWorld w2;
      MudObject *o;
      int i;
      foreach((&what), o, i) {
	w2.add(o);
      }

      put_obs(who, w2, what2.get_nth(0));
      set_prons(who, w2);

      return true;
    }

    int next = 1;
    NewWorld what = match(who, argc-1, argv+1, is_puttable, LOOK_INV|IGNORE_EXITS|IGNORE_MISSION, &next);

    int on = streq(argv[next], "on");
    TeleWorld what2 = multi_match(who, argc-1, argv+1, NULL, LOOK_BOTH|IGNORE_EXITS|ALLOW_ME, 0, 0,
				  on?"on":"in");

    if (!on && what2.getsize()==0) {
      what2 = multi_match(who, argc-1, argv+1, NULL, LOOK_BOTH|IGNORE_EXITS|ALLOW_ME, 0, 0, "into");
    }
    
    if (what.getsize()==0) {
      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("Put any what?\n");
      else
	who->printf("Put what?\n");
      return true;
    }

    if (what2.getsize()==0) {
       who->printf("Put in what?\n");
       return true;
    }

    if (what2.getsize()!=1) {
       who->printf("You can only put stuff %sto one thing at a time.\n", what.prep);
       return true;
    }

    TeleObject container = what2.get(0);
    if (container->get_flag(FL_FLOOR)) {
      verb_drop(who, argc, argv);
      return true;
    }
    
    if (what.getsize()==1 && what.get(0)==container) {
      who->printf("You can't put %M in %sself.\n", what.get(0), him_or_her(container));
      return true;
    }
    
    if (what.get(container->id)) {
      what.remove(*container);
    }
    
    if (container->get_flag(FL_PIT)) {
      drop_obs(who, what, 0, 0);
      return true;
    }
    
    //    on = 0;
    /* DISABLE TABLES for the moment */
    
    if (on && (container->get_flag(FL_CANSITON) || container->get_flag(FL_CANSLEEPON) ||
	       container->get_flag(FL_TABLE))) {
      puton_obs(who, what, container);
      set_prons(who, what);
    } else {
      put_obs(who, what, container);
      set_prons(who, what);
    }
    fixup_lapels(who, NULL);
    
    return true;
}

 #include "verbmodule.h"

void startup() {

  //AUTO_VERB(image, 3, 0, PFL_AWAKE);

AUTO_VERB(eat, 3, 0, PFL_AWAKE);
AUTO_VERB(drink, 3, 0, PFL_AWAKE);

AUTO_VERB(taste, 2, 0, PFL_AWAKE);

AUTO_VERB(examine, 2, 0, PFL_AWAKE);
ADD_ALIAS(x, 1, examine);

ADD_ALIAS(read, 3, examine);

AUTO_VERB(drop, 2, 0, PFL_AWAKE);
AUTO_VERB(throw, 2, 0, PFL_AWAKE);

AUTO_VERB(empty, 3, 0, PFL_AWAKE);
AUTO_VERB(fill, 3, 0, PFL_AWAKE);
AUTO_VERB(pour, 3, 0, PFL_AWAKE);

AUTO_VERB(put,   3, 0,           PFL_AWAKE);
ADD_ALIAS(insert, 3, put);

AUTO_VERB(get,   1, 0,           PFL_AWAKE);
ADD_ALIAS(take, 1, get);

AUTO_VERB(steal, 5, 0, PFL_AWAKE);

AUTO_VERB(give,    2, 0, PFL_AWAKE);

AUTO_VERB(loot,    4, 0, PFL_AWAKE);

AUTO_VERB(pay,    2, 0, PFL_AWAKE);

AUTO_VERB(shuffle,  2, 0, PFL_AWAKE);
AUTO_VERB(deal,     2, 0, PFL_AWAKE);
AUTO_VERB(discard,     5, 0, PFL_AWAKE);

AUTO_VERB(light, 2, 0, PFL_AWAKE);
AUTO_VERB(extinguish, 3, 0, PFL_AWAKE);

ADD_ALIAS(quench, 4, extinguish);
ADD_ALIAS(unlight, 4, extinguish);

AUTO_VERB(drinkfind, 6, 0, PFL_AWAKE);
}