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

/* The wonderful message subsitition routine ! */

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <ctype.h>

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

#include "units.h"
#include "match.h"
#include "body.h"

#include "nations.h"

void fixup_lookplace(MudObject *who, int &lookplace)
{
  if (!cansee(who))
    lookplace = 0;

  if (!who->owner) {
    lookplace = 0;
    return;
  }

  if (who->owner->get_flag(FL_ONFIRE) && !wf_wears_with_flag(who, FL_NIGHTVISION)) {
    lookplace = 0;
    return;
  }

  if (!has_light(who->owner) && !wf_wears_with_flag(who, FL_NIGHTVISION)) {
    lookplace = 0;
    return;
  }
}


int
yes (const char *what)
{
	return streq (what, "yes") ||
		streq (what, "true") ||
		streq (what, "on") ||
                streq (what, "t") ||
		streq (what, "1") || (*what == 'y') || (*what=='Y');
}

int
no (const char *what)
{
	return streq (what, "no") ||
		streq (what, "false") ||
		streq (what, "off") ||
                streq (what, "f") ||
		streq (what, "0") ||  (*what == 'N') || (*what=='n');
}

bool sstrstr(const char *haystack, const char*needle) {
	if (haystack && needle) return (strcasestr(haystack, needle)!=NULL);
		else return false;
}

static MudObject *find_pot(MudObject *where) 
{
  iforeach(o, *where->children) {
    if (o->get_flag(FL_CASH))
      return o;
  }
  if (MudObject *o = clone_object(MUD_CASH, where)) {
    o->set("value", 0);
    return o;
  }
  return 0;
}

string describe_cash(MudObject *pot);

void dumpstuff(MudObject *what, MudObject *where) {
  NewWorld dumped;
  MudObject *empty = planet->get("empty");

  string c;

  if (anycash(what)) {
    c = describe_cash(what);

    if (empty != where) {
      MudObject *p = find_pot(where);
      moveallcash(what, p);
    }
    wipe_cash(what);
  }

  while (what->children->getsize()) {
    MudObject *o = what->children->get(0);
    dumped.add(*o);
    set_owner(o, where);
    
    if (where == empty || o->get_flag(FL_FRAGILE) || o->get_flag(FL_NOGIVE)) {
      vanish(o);
    }
  }

  if (c.length()) {
    if (dumped) {
      what->printf("You drop %W and %s.\n", &dumped, c);
      what->oprintf("%#M %[drops/drop] %W and %s.\n", what, &dumped, c);
    } else {
      what->printf("You drop %s.\n", c);
      what->oprintf("%#M %[drops/drop] %s.\n", what, c);
    }
  } else {
    if (dumped) {
      what->printf("You drop %s.\n", &dumped);
      what->oprintf("%#M %[drops/drop] %s.\n", what, &dumped);
    }
  }
}

void set_owner(MudObject * what, MudObject *dest) {
  if (dest && is_in(dest, what)) {
    log(PFL_SEEINFO, 0, "debug", "attempt at moving %s into %s", what->id, dest->id);
    return;
  }
  if (what->owner && what->owner->get_object(KEY_WIELD)==what) {
    what->owner->unset(KEY_WIELD);
  }
  what->set("owner", dest?dest->id:0);
  if (what->get(KEY_WORNBY)) {
    what->unset(KEY_WORNBY);
  }
  if (what->get(KEY_SITON)) what->unset(KEY_SITON);
  what->unset("$camefrom");
  what->unset("$camefromloc");
}

void set_owner(MudObject *what, const char *dest) {
  set_owner(what, planet->get(dest));
}

MudObject *get_player(MudObject *me, const char *who) {
  if (!me) {
    return planet->get(who);
  }

    if (streq(who, "me")) return me;

    if (me->get_int(KEY_BUNNIED)>now&&(streq(who, "bunnies") || streq(who, "bunny"))) {
      return MUD_BUNNIES;
    }
    MudObject *o = planet->get(who);
    if (o && ((is_player(o) && o->get_flag(FL_LOGGEDIN)) || is_mobile(o)) && visible_to(me, o)) return o;
    int i;
    if (me->owner) {
      foreach(me->owner->children, o, i) {
	if ((is_player(o) || is_mobile(o))  
	    && (names_match(o->get("short"), who) ||
		names_match(o->get("altshort"), who) ||
		names_match(name(o), who)) && visible_to(me, o)) return o;
      }
    }
    foreach(players, o, i) {
      if (o->get_flag(FL_LOGGEDIN) && 
	  (names_match(o->get("short"), who) ||
	   names_match(o->get("altshort"), who)) && 
	  visible_to(me, o))
	
	return o;
    }
    return 0;
}

bool visible_to(const MudObject *to, const MudObject *what) {
  if (to==what) return true;
  int privs = privs_of(to);
  int ilev = invis(what);

  if (is_player(what))
    {
      int sight = to->get_int("sight", 1);
      if (sight < 1)
	return false;
    }

  if (ilev == -1) return true;
  if (privs == -1) privs = 0;
  return privs >= ilev;
}

int invis(const MudObject *of) {
  int i = of->get_int("invis");
  if (i!=-1) return i;
  return 0;
}

int strength_of(const MudObject *player)
{
  return player->get_int(KEY_STRENGTH);
}

void set_strength(MudObject *player, int strength)
{
  player->set(KEY_STRENGTH, strength);
}

int privs_of(const MudObject *who, int def)  {
  int privs = who->get_int("privs", def);
  int level = who->get_int("level", def);
  int remorted = who->get_int("remort", def);
  if (remorted>0) return level>=LEV_COMMANDER?LEV_COMMANDER-1:level;
  if (privs > level) return privs;
  return level;
}


MudObject *saton(MudObject *who) {
  if (!who->get_flag(FL_SITTING)) {
    return 0;
  }

  MudObject *chair = who->get_object(KEY_SITON);
  if (!chair)
    return 0;

  return chair;
}


MudObject *sleepon(MudObject *who) {
  if (!who->get_flag(FL_SLEEPING)) {
    return 0;
  }

  MudObject *chair = who->get_object(KEY_SITON);
  if (!chair)
    return 0;

  return chair;
}



bool random_chance(long odds) 
{
	long foo = (random() >> 3);
	return (foo % odds) == 0;
}

long random_number(long limit)
{
	if (limit < 1) return 0;
	long foo = (random() >> 3);
	return (foo % limit);
}

void vanish_contents(MudObject *who) 
{
  MudObject *o;
  int i;
  foreach(who->children, o, i)
    {
      vanish(o);
      if (o->owner != who) i--;
    }
}


bool is_big_mobile(MudObject *what)
{
  if (!what)
    return false;

  if (!is_mobile(what)) 
    return false;

  if (!mass_in_grams(what))
    return true;

  return false;
}


MudObject *zoneof(MudObject *where) {
  if (!where)
    return 0;

  const char *txt = where->get("zone");
  if (!txt)
    return 0;

  return planet->get(ssprintf("%s_zone", txt).c_str());
}

int state(const MudObject *what) {
    return what->get_int(KEY_STATE, 0);
}

MudObject *find_ship(MudObject *dock) {
  if (!dock) return 0;
  MudObject *o;
  int i;
  foreach(dock->children, o, i) {
    if (o->get_flag(FL_SHIP)) return o;
  }
  return 0;
}

bool has_ship_space(const char *where, MudObject *ignore=0) {
  MudObject *d = planet->get(where);
  if (!d) return false;
  if (d->get_flag(FL_PRIVATE)) 
    return 0;

  if (d->get_object("reserved") && d->get_object("reserved") != ignore) {
    return 0;
  }

  int goodness = d->get_int("docksize", 0);
  int needed = ignore?ignore->get_int("dockneeded", 0):0;

  if (goodness!=needed)
    return 0;

  MudObject *ship = find_ship(d);
  if (!ship || (!ignore && ship == ignore))
    return 1;
  return 0;
}

MudObject *find_dock(MudObject *docklist, MudObject *ignore) {
  char name[256];
 
  if (docklist->get_object("dock"))
    docklist = docklist->get_object("dock");

  int max = docklist->get_int("dock.count");

  int i;
  for (i=0;i<max;i++) {
    sprintf(name, "dock.%i", i);
    MudObject *d = docklist->get_object(name);
    if (d && d->get_object("reserved") && ignore) {
      if (d->get_object("reserved")==ignore)
	return d;
    }
  }

  for (i=0;i<max;i++) {
    sprintf(name, "dock.%i", i);
    const char *d = docklist->get(name);
    if (!d) return 0;
    if (has_ship_space(d, ignore)) 
      return planet->get(d);
  }
  return 0;
}

bool is_droppable(MudObject *o) 
{
  if (is_player(o->owner) && o->owner && !o->owner->get_flag(FL_LOGGEDIN))
    return 1;
  if (o->get_flag(FL_FIXED) || o->get_flag(FL_EXIT))
    return 0;
  if (mount(o->owner)==o)
    return 0;

  if (o->owner && o->owner->get_flag(FL_NOINVWORN)) {
    /*    I really don't like this behaviour  -- Daz */
  if (o->get_object("$wornby")==o->owner)
    return 0;
  if (o->owner && o->owner->get_object(KEY_WIELD)==o)
    return 0;
  }
  return 1;
}

static void rec_pit(MudObject *who, MudObject *pit, World<MudObject> *blah) 
{
  MudObject *o;
  int i;
  foreach(blah, o, i) {
    dotrap(E_ONDROPINPIT, who, o, pit);
    /* ::: drop_in_pit o1==object being pitted, o2==the pit; after messaging. called on contents of containers too. */
    vanish(o);

    rec_pit(who, pit, o->children);
  }
}

NewWorld try_remove_obs(MudObject *who, NewWorld stuff) 
{
  MudObject *o;
  int i;

  NewWorld what = stuff;

  NewWorld notworn, already;

  for (int wl=5;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());
  }

  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 */
  }

  NewWorld ok;

  foreach((&stuff), o, i) {
    if (!iswornby(o, who)) {
      ok.add(o);
    }
  }

  return ok;
}

void drop_obs(MudObject *who, NewWorld what, bool dothrow, bool docareful, TeleObject thrownat)
{
  MudObject *o;
  int i;
  NewWorld autodie;

  MudObject * where = thrownat?thrownat.where:who->owner;

  MudObject *pit = !dothrow ? find_pit(where) : 0;
  MudObject *fl = floor(where);

  if (docareful)
     pit = 0;

  what = try_remove_obs(who, what);

  foreach((&what), o, i) {
    if (!is_droppable(o)) {
      who->printf("You cannot drop %Y.\n", o);
      what.remove(*o);
      i--;
      continue;
    }

    if (!dothrow && dotrap(E_ONDROPINHERE, who, where, o)) {
      /* ::: drop_in_here o1==this room, o2==object being dropped; before anything other than droppability testing, return 1 to abort */
      what.remove(*o);
      i--;
      continue;
    }

    if (!pit) {
      if (!dothrow && dotrap(E_ONDROP, who, o, where)) {
      /* ::: drop o1==object being dropped, o2==this room; return 1 to abort */
	what.remove(*o);
	i--;
      } else if (dothrow && (dotrap(E_ONTHROW, who, o, where)||
			     thrownat && (dotrap(E_BEFORETHROWAT, who, o, thrownat)
				        ||dotrap(E_BEFORETHROWNWITH, who, thrownat, o)))) {
      /* ::: throw o1==missile, o2==this room; return 1 to abort */

      /* ::: before_throw_at o1==missile, o2==target; return 1 to abort */
      /* ::: before_thrown_with o1==target, o2==missile; return 1 to abort */
	what.remove(*o);
	i--;
      } 
      else
	set_owner(o, where);

    } else {

      if (dotrap(E_BEFOREPIT, who, o, pit)) {
   /* ::: before_pit o1==object about to be pitted, o2==the pit; return 1 to abort. not recursive - doesn't get called on objects inside containers that are to be pitted. */
	what.remove(*o);
	i--;
      } else
	set_owner(o, "empty");
    }
  }

  if (pit) {    
    if (what.getsize()) {
      who->printf("You drop ^Z%s into %Y.\n", magiclist(what).c_str(), pit);
      who->oprintf(cansee, "%#M %[drops/drop] ^Z%s into %Y.\n", who, magiclist(what).c_str(), pit);

      rec_pit(who, pit, &what);
    }
    goto minis;
    return;  
  }

    if (what.getsize()) {
      if (docareful && fl) {
	who->printf("You put ^Z%s on %P.\n", magiclist(what).c_str(), fl);
	who->oprintf(cansee, "%#M %[puts/put] ^Z%s on %P.\n", who, magiclist(what).c_str(), fl);
      } else if (dothrow) {
	if (thrownat) {
	  if (thrownat!=who) {
	    who->printf("You throw ^Z%s at %M.\n", magiclist(what).c_str(), &thrownat);
	    thrownat->printf("%#M %[throws/throw] ^Z%s at you.\n", who, magiclist(what).c_str());
	    who->oprintf(cansee && avoid(thrownat), "%#M %[throws/throw] ^Z%s at %M.\n", who, magiclist(what).c_str(), &thrownat);
	  } else {
	    who->printf("You throw ^Z%s at yourself.\n", magiclist(what).c_str());	    
	    who->oprintf(cansee, "%#M %[throws/throw] ^Z%s at %s.\n", who, magiclist(what).c_str(), himself_or_herself(who));
	  }
	} else {
	  who->printf("You throw ^Z%s.\n", magiclist(what).c_str());
	  who->oprintf(cansee, "%#M %[throws/throw] ^Z%s.\n", who, magiclist(what).c_str());
	}
      } else {
	who->printf("You drop ^Z%s.\n", magiclist(what).c_str());
	who->oprintf(cansee, "%#M %[drops/drop] ^Z%s.\n", who, magiclist(what).c_str());
      }
    }

  foreach((&what), o, i) {
    if (!dothrow) 
      dotrap(E_AFTERDROP, who, o);
      /* ::: after_drop o1==object dropped; after messaging, before specials */
    else {
      /* ::: after_throw o1==missile; after messaging, before specials */
      dotrap(E_AFTERTHROW, who, o);
      if (thrownat) {
	/* ::: after_throw_at o1==missile, o2==target; after messaging before specials */
	dotrap(E_AFTERTHROWAT, who, o, thrownat);
	/* ::: after_thrown_with o1==target, o2==missile; after messaging before specials */
	dotrap(E_AFTERTHROWNWITH, who, thrownat, o);
      }
    }
  }

  if (where->get_flag(FL_ONWATER) || where->get_flag(FL_UNDERWATER) ||
      (where->get_flag(FL_BYWATER)&&who->get_flag(FL_SWIMMING))) {
    NewWorld floats;
    NewWorld sink;
    MudObject *seabed = where->get_object("seabed");
    MudObject *surface = where->get_object("surface");

    foreach((&what), o, i) {
      if (o->get_flag(FL_CANFLOAT)) {
	floats.add(*o);
      } else {
	sink.add(*o);
      }
    }
    if (floats.getsize()) {
      floats.get(0)->oprintf(cansee, "%#s %s%s.\n", magiclist(floats).c_str(),
			      plural(floats)?"float": "floats",
			     surface?" up to the surface of the water":
			     where->get_flag(FL_ONWATER)?" on the surface of the water":
			     "");
    }

    if (sink.getsize()) {
      if (where->get_flag(FL_UNDERWATER))
	sink.get(0)->oprintf(cansee, "%#s %s%s.\n", magiclist(sink).c_str(),
			      plural(sink)?"sink": "sinks", seabed?"":" without a trace");
      else
	sink.get(0)->oprintf(cansee, "%#s %s beneath the water%s.\n", magiclist(sink).c_str(),
			   plural(sink)?"sink": "sinks", seabed?"":" without a trace");
    }    

    foreach((&floats), o, i) {
      if (surface)
	set_owner(o, surface);
    }

    foreach((&sink), o, i) {
      if (seabed)
	set_owner(o, seabed);
      else
	vanish(o);
    }
  }
  
  if (dothrow && where->get_flag(FL_NOGRAVITY) && who->owner->get_flag(FL_OUTDOORS) &&
      who->owner->get_flag(FL_AIRLESS)) {
    if (what.getsize()) {
      what.get(0)->oprintf(cansee, "%#s %s off into space.\n", magiclist(what).c_str(), plural(what)?"drift":"drifts");
    }
    foreach((&what), o, i) {
      vanish(o);
    }
  }

  foreach((&what), o, i) {
    if (o->get_flag(FL_NOGIVE)) {
      autodie.add(*o);
    }
  }
  if (autodie.getsize()) {
    autodie.get(0)->oprintf(cansee, "%#s self-destruct%s.\n", magiclist(what).c_str(), plural(what)?"":"s");
  }
  foreach((&autodie), o, i) {
    vanish(o);
  }

  if (!docareful) {
    NewWorld frag;

    if (thrownat && thrownat->get_flag(FL_FRAGILE)) {
      frag.add(*thrownat);
    }

    foreach((&what), o, i) {
      if (o->get_flag(FL_FRAGILE)) {
	frag.add(*o);
      }
    }
    if (frag.getsize()) {
      frag.get(0)->oprintf(cansee, "%#s %s apart.\n", magiclist(frag).c_str(), plural(frag)?"smash":"smashes");
    }
    foreach((&frag), o, i) {
      int j;
      MudObject *o2;
      foreach(o->children, o2, j) {
	set_owner(o2, o->owner);
      }
      vanish(o);
    }
  }

 minis:
  foreach((&what), o, i) {
    if (is_mission(o)) {
      o->oprintf(cansee, "%#Y %[vanishes/vanish].\n", o);
      set_owner(o, "@musicmud");
      o->nuke_me = 0;
      o->unset(KEY_ACCEPTER);
      who->unset("mission");
      return;
    }
  }
}

static bool strceq(const char *a, const char *b) {
    if (a==b) return true;
    if (!a || !b) return false;
    return strcasecmp(a,b)==0;
}

MudObject *find_object(MudObject *context, 
		       const char *name, 
		       bool shortcut) {
    if (!name) return 0;

    if (shortcut && strceq(name, "me")) return context;
    if (shortcut && strceq(name, "here")) return context->owner;
    if (shortcut && strceq(name, "home")) return context->get_object("start");
    
    if (MudObject *m=planet->get(name))
      return m;

    return 0;
}

MudObject *find_exit(MudObject *room, 
		     const char *name) {
    if (!name) return 0;

    MudObject *o;
    int i;

    foreach(room->children, o, i) {
      if (streq(o->get("short"), name) &&
	  (o->get_flag(FL_EXIT) || o->get_flag(FL_PARTEXIT)))
	return o;
    }

    return 0;
}

MudObject *saveloc(MudObject *who)
{
  MudObject *where = who->owner;

  if (MudObject *h=where->get_object("home"))
    return h;
  
  if (where->get_flag(FL_STICKYROOM))
    return where;

  if (where->owner && where->owner->get_flag(FL_STICKYROOM))
    return where;

  if (MudObject* h=get_zonepropobj(where, "home"))
    return h;

  return MUD_DEFHOME;
}

MudObject *wornfrom(const MudObject *who)
{
  if (!is_mobile(who))
    return 0;
  MudObject *wf = archetype(who);
  if (who->get("wornfrom"))
    wf = who->get_object("wornfrom");
  return wf;
}

MudObject *weapon(MudObject *who)
{
  if (MudObject *o=who->get_object(KEY_WIELD)) {
    if (o->owner == who)
      return o;
  } else if (MudObject *wf=wornfrom(who)) {
    if (MudObject *o = wf->get_object(KEY_WIELD))
      return o;
  }
  return 0;
}

MudObject *floor(MudObject *where)
{
  if (!where->get_flag(FL_ROOM) && !where->get_flag(FL_SHIP))
    return 0;
  MudObject *o;
  int i;
  foreach(where->children, o, i) {
    if (o->get_flag(FL_FLOOR))
      return o;
  }
  for (i=0;i<where->array_size("tele");i++) {
    MudObject *o = where->array_get_object("tele", i);
    if (o && o->get_flag(FL_FLOOR))
      return o;
  }
  if (where->get("floor")) {
    return where->get_object("floor");
  }
  if (MudObject *fz = zoneof(where)) {
    if (fz->get("floor"))
	return fz->get_object("floor");
  }
  if (where->get_flag(FL_NOGRAVITY) && where->get_flag(FL_OUTDOORS))
    return 0;
  if (where->get_flag(FL_OUTDOORS)) {
    if (where->get_int("terrain")==6)
      return MUD_SURFACE;
    else
      return MUD_GROUND;
  }
  return MUD_FLOOR;
}

string abilities(MudObject *o) {
  wtype_t wty = (wtype_t)o->get_int("wtype");
  int dam = o->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";
  
  string wtstr="";

  if (wty == W_IMPACT) wtstr = "impact weapon; does";
  if (wty == W_CUTTING) wtstr = "cutting weapon; does";
  if (wty == W_STABBING) wtstr = "stabbing weapon; does";
  
  string provides="";
  string protect="";
  string ability="";
  
  if (o->get_flag(FL_RADIATIONSUIT))
    protect += " radiation,";
  if (o->get_flag(FL_COLDPROTECT))
    protect += " extreme cold,";
  if (o->get_flag(FL_HEATPROTECT))
    protect += " extreme heat,";
  
  if (o->get_flag(FL_GILLS))
    ability += " to breathe underwater,";
  if (o->get_flag(FL_SPACESUIT))
    ability += " to survive in a vacuum,";
  if (o->get_flag(FL_NIGHTVISION)) 
    ability += " to see in the dark,";
  
  if (protect.length()) {
    provides = "protects from" + protect;
  }
  if (ability.length()) {
    if (provides.length()) {
      provides += " and ";
    }
    provides += "allows user";
    provides += ability;
  }
  
  int arm = o->get_int("armour", 0);
  if (arm>0) {
    if (provides.length()) {
      provides += " and ";
    } 
    const char *how = "minimal";
    if (arm>2) how = "some";
    if (arm>5) how = "fair";
    if (arm>10) how = "good";
    if (arm>20) how = "very good";
    provides += how;
    provides += " protection in combat,";
  }

  if (wtstr.length()) {
    if (provides.length())
      provides = ssprintf("%s%s, %s", wtstr.c_str(), damstr, provides.c_str());
    else
      provides = ssprintf("%s%s", wtstr.c_str(), damstr);
  }
  
  return provides;
}

const char *looknice(const char *c, NewWorld *blah) { 
  if (!c)
    return "nowhere";

  for (int i=0;i<pairs_n;i++)
    {
      if (streq(pairs[i].left, c)) return pairs[i].lnice;
      if (streq(pairs[i].right, c)) return pairs[i].rnice;
    }

  if (strncmp(c, "finger.", 7)==0) {
    int which = atoi(c+7);
    if (which >= 0 && which < 8) {
      return fingers[which];
    }
    return "unknown finger";
  }

  if (streq(c, "body,legs")) {
    return "body and legs";
  }
  if (streq(c, "body,arms")) {
    return "body and arms";
  }
  
  if (streq(c, "allover")) {
    if (!blah || blah->getsize()>1) {
      return "clothes";
    } else {
      return "body";
    }
  }

  return c;
}

const char *looknice(MudObject *o, NewWorld *blah) {
  return looknice(wornon(o), blah);
}


int serving(MudObject *what) {
  int sv = what->get_int("serving");
  if (sv != -1)
    return sv;
  MudObject *em = what->get_object("empty");
  if (em)
    return em->get_int("volume");
  return -1;
}

int abv(MudObject *what)
{
  if (what->get_object("substance"))
    what = what->get_object("substance");

  int abv = what->get_int("abv");
  if (abv != -1)
    return abv;

  int al = what->get_int("alcohol", 0);
  MudObject *em = what->get_object("empty");
  if (!em) 
    return 0;

  int vol = serving(what);

  if (vol < 1)
    return 0;

  return (al * 10000 / vol);
}



TeleWorld clothes(MudObject *who)
{
  TeleWorld w;
  MudObject *o;
  int i;
  foreach(who->children, o, i) {
    if (iswornby(o, who))
      w.add(*o, who);
  }

  MudObject *wf = wornfrom(who);
  if (wf)
    foreach(wf->children, o, i) {
    if (iswornby(o, wf) && !worn_on(who, wornon(o), o->get_int("wornlevel", 0))) {
      w.add(*o, who);
    }
  }
  
  return w;
}



void itell(MudObject *who, const char *str) 
{
  who->ilc++;
  char *a = strdup(str);
  char *p;
  char *b = strtok_r(a, ";", &p) ;
  while (b) {
    who->interpret(b);
    b = strtok_r(NULL, ";", &p);
  }
  strfree(a);
  who->ilc--;
}


const char *get_zoneprop(MudObject *orig, 
			 MudObject *where, 
			 const char *prop, 
			 NewWorld *sofar=0)
{
  NewWorld s;
  if (!sofar) sofar = &s;
  
  sofar->add(where);

  if (where->get(prop)) {
    return where->get(prop);
  }

  if (MudObject *f=where->get_object("fwd")) {
    if (!sofar->get(f->id)) {
      return get_zoneprop(orig, f, prop, sofar);
    }
  }

  if (MudObject *s=where->get_object("ship")) {
    if (!sofar->get(s->id)) {
      return get_zoneprop(orig, s, prop, sofar);
    }
  }

  if (where->get_flag(FL_SHIP) && !sofar->get(where->owner->id)) {
    return get_zoneprop(orig, where->owner, prop, sofar);
  }

  MudObject *r = where;
  while (r && !r->get_flag(FL_ROOM)) {
    r = r -> owner;
  }
  if (r != where && r && !sofar->get(r->id)) {
    return get_zoneprop(r, r, prop, sofar);
  }

  if (const char *z=where->get("zone")) {
    MudObject *zo=getzone(z);
    if (zo && !sofar->get(zo->id))
      return get_zoneprop(orig, zo, prop, sofar);
  }

  return where->get(prop);
}

int get_zonepropint(MudObject *orig, 
		    MudObject *where, 
		    const char *prop,
		    int def,
		    NewWorld *sofar=0)
{
  NewWorld s;
  if (!sofar) sofar = &s;
  
  sofar->add(where);

  if (where->ints.find(prop)!=where->ints.end()) {
    return where->get_int(prop);
  }

  if (MudObject *f=where->get_object("fwd")) {
    if (!sofar->get(f->id)) {
      return get_zonepropint(orig, f, prop, def, sofar);
    }
  }

  if (MudObject *s=where->get_object("ship")) {
    if (!sofar->get(s->id)) {
      return get_zonepropint(orig, s, prop, def, sofar);
    }
  }

  if (where->get_flag(FL_SHIP) && !sofar->get(where->owner->id)) {
    return get_zonepropint(orig, where->owner, prop, def, sofar);
  }

  MudObject *r = where;
  while (r && !r->get_flag(FL_ROOM)) {
    r = r -> owner;
  }
  if (r != where && r && !sofar->get(r->id)) {
    return get_zonepropint(orig, r, prop, def, sofar);
  }

  if (const char *z=where->get("zone")) {
    MudObject *zo=getzone(z);
    if (zo && !sofar->get(zo->id))
      return get_zonepropint(orig, zo, prop, def, sofar);
  }

  return def;
}

int get_zonepropint(MudObject *where, 
		    const char *prop,
		    int def)
{
  return get_zonepropint(where, where, prop, def);
}

const char *get_zoneprop(const char *o,
			 const char *prop,
			 const char *def)
{
  MudObject *wh = planet->get(o);
  const char *r = get_zoneprop(wh, wh, prop);
  return r?r:def;
}

const char *get_zoneprop(MudObject *wh,
			 const char *prop,
			 const char *def)
{
  const char *r = get_zoneprop(wh, wh, prop);
  return r?r:def;
}

bool has_rights(MudObject *who, MudObject *where) {
  return !dotrap(E_ONFORBIDDOCKING, who, where);
  /* ::: forbid_docking o1==which dock; return 1 to forbid /pl/ from docking a ship at /o1/ */
}



int gid = 1;
string genid(MudObject *fr) {
  if (fr)
    return ssprintf("@ship_%s_%i", fr->id, gid++);
  else
    return get_clone_name();
}

void recurse_clone(MudObject *player, MudObject *from, MudObject *to, MudObject *ship,
			  Mission *m) {
  MudObject *o;
  int i;

  from->set("!fakeid", to->id);

  foreach(from->children, o, i) if (!is_player(o) && (o->get_object("start")==from)) {
    MudObject *r = clone_object(o, to, player?genid(player).c_str():0);
    r->set("ship", ship->id);
    if (m)
      r->set_mission(m);
    else
      r->set("zone", "@ship");
    recurse_clone(player, o, r, ship, m);
  }
}

static void fixup_prop(MudObject *from, const char *prop) 
{
  const char *ln2 = from->get(prop);
  if (ln2 && ln2[0]==':') {
    MudObject *ln = planet->get(ln2+1);
    if (ln && ln->get_object("!fakeid")) {
      from->setf(prop, ":%s", ln->get_object("!fakeid")->id);
    }
  } else {
    MudObject *ln = from->get_object(prop);
    if (ln && ln->get_object("!fakeid")) {
      from->set(prop, ln->get_object("!fakeid")->id);
    } 
  }

}

void fixup_exits(MudObject *from) 
{
  fixup_prop(from,  "link");
  fixup_prop(from,  "door");
  fixup_prop(from,  "doorsat");
  fixup_prop(from,  "ship");

  for (int i=0;i<from->array_size("room");i++) {
    fixup_prop(from, ssprintf("room.%i", i).c_str());
  }

  MudObject *o;
  int i;
  foreach(from->children, o, i)
    fixup_exits(o);
}

void remove_fakeids(MudObject *from) 
{
  from->unset("!fakeid");

  MudObject *o;
  int i;
  foreach(from->children, o, i)
    remove_fakeids(o);
}