 /* $Header: c:/tads/tads2/RCS/adv.t 1.3 93/01/08 04:36:27 mroberts Prod Locker: mroberts $ */
/* Copyright (c) 1988, 1993 by Michael J. Roberts.  All Rights Reserved. */
/*
   adv.t  - standard adventure definitions for TADS games
   Version 2.0

   This file is part of TADS:  The Text Adventure Development System.
   Please see the file LICENSE.DOC (which should be part of the TADS
   distribution) for information on using this file, and for information
   on reaching High Energy Software, the developers of TADS.

   This file defines the basic classes and functions used by most TADS
   adventure games.  It is generally #include'd at the start of each game
   source file.
*/

/*
 *   Define compound prepositions.  Since prepositions that appear in
 *   parsed sentences must be single words, we must define any logical
 *   prepositions that consist of two or more words here.  Note that
 *   only two words can be pasted together at once; to paste more, use
 *   a second step.  For example,  'out from under' must be defined in
 *   two steps:
 *
 *     compoundWord 'out' 'from' 'outfrom';
 *     compoundWord 'outfrom' 'under' 'outfromunder';
 *
 *   Listed below are the compound prepositions that were built in to
 *   version 1.0 of the TADS run-time.
 */
compoundWord 'on' 'to' 'onto';           /* on to --> onto */
compoundWord 'in' 'to' 'into';           /* in to --> into */
compoundWord 'in' 'between' 'inbetween'; /* and so forth */
compoundWord 'down' 'in' 'downin';
compoundWord 'down' 'on' 'downon';
compoundWord 'up' 'on' 'upon';
compoundWord 'out' 'of' 'outof';
compoundWord 'off' 'of' 'offof';
;

/*
 *   Format strings:  these associate keywords with properties.  When
 *   a keyword appears in output between percent signs (%), the matching
 *   property of the current command's actor is evaluated and substituted
 *   for the keyword (and the percent signs).  For example, if you have:
 *
 *      formatstring 'you' fmtYou;
 *
 *   and the command being processed is:
 *
 *      fred, pick up the paper
 *
 *   and the "fred" actor has fmtYou = "he", and this string is output:
 *
 *      "%You% can't see that here."
 *
 *   Then the actual output is:  "He can't see that here."
 *
 *   The format strings are chosen to look like normal output (minus the
 *   percent signs, of course) when the actor is Me.
 */
formatstring 'you' fmtYou;
formatstring 'your' fmtYour;
formatstring 'you\'re' fmtYoure;
formatstring 'youm' fmtYoum;
formatstring 'you\'ve' fmtYouve;
formatstring 's' fmtS;
formatstring 'es' fmtEs;
formatstring 'have' fmtHave;
formatstring 'do' fmtDo;
formatstring 'are' fmtAre;
;

/*
 *   Special Word List: This list defines the special words that the
 *   parser needs for input commands.  If the list is not provided, the
 *   parser uses the old defaults.  The list below is the same as the old
 *   defaults.  Note - the words in this list must appear in the order
 *   shown below.
 */
specialWords
   'of',                        /* used in phrases such as "piece of paper" */
   'and',             /* conjunction for noun lists or to separate commands */
   'then',                              /* conjunction to separate commands */
   'all' = 'everything',               /* refers to every accessible object */
   'both',      /* used with plurals, or to answer disambiguation questions */
   'but' = 'except',                      /* used to exclude items from ALL */
   'one',                       /* used to answer questions:  "the red one" */
   'ones',                        /* likewise for plurals:  "the blue ones" */
   'it',                        /* refers to last single direct object used */
   'them',                             /* refers to last direct object list */
   'him',                       /* refers to last masculine actor mentioned */
   'her'                         /* refers to last feminine actor mentioned */
;
  

/*
 *   Forward-declare functions.  This is not required in most cases,
 *   but it doesn't hurt.  Providing these forward declarations ensures
 *   that the compiler knows that we want these symbols to refer to
 *   functions rather than objects.
 */
checkDoor: function;
checkReach: function;
itemcnt: function;
listcont: function;
listcontcont: function;
turncount: function;
addweight: function;
addbulk: function;
incscore: function;
darkTravel: function;
scoreRank: function;
terminate: function;
goToSleep: function;
initSearch: function;
reachableList: function;
initRestart: function;
;

/*
 *   initRestart - flag when a restart has occurred by setting a flag
 *   in global.
 */
initRestart: function(parm)
{
    global.restarting := true;
}

/*
 *   checkDoor:  if the door d is open, this function silently returns
 *   the room r.  Otherwise, print a message ("The door is closed.") and
 *   return nil.
 */
checkDoor: function( d, r )
{
    if ( d.isopen ) return( r );
    else
    {
	setit( d );
	caps(); d.thedesc; " is closed. ";
	return( nil );
    }
}

/*
 *   checkReach:  determines whether the object obj can be reached by
 *   actor in location loc, using the verb v.  This routine returns true
 *   if obj is a special object (numObj or strObj), if obj is in actor's
 *   inventory or actor's location, or if it's in the 'reachable' list
 *   for loc.
 */
checkReach: function( loc, actor, v, obj )
{
    if ( obj=numObj or obj=strObj ) return;
    if ( not ( actor.isCarrying( obj ) or obj.isIn( actor.location )))
    {
	if (find( loc.reachable, obj ) <> nil ) return;
	"%You% can't reach "; obj.thedesc; " from "; loc.thedesc; ". ";
	exit;
    }
}

/*
 *  itemcnt: function( list )
 *
 *  Returns a count of the "listable" objects in list.  An
 *  object is listable (that is, it shows up in a room's description)
 *  if its isListed property is true.  This function is
 *  useful for determining how many objects (if any) will be listed
 *  in a room's description.
 */
itemcnt: function( list )
{
    local cnt, tot, i;
    tot := length( list );
    cnt := 0;
    i := 1;
    while ( i <= tot )
    {
	if ( list[i].isListed ) cnt := cnt+1;
	i := i+1;
    }
    return( cnt );
}

/*
 *  listcont: function( obj )
 *
 *  This function displays the contents of an object, separated by
 *  commas.  The thedesc properties of the contents are used.
 *  It is up to the caller to provide the introduction to the list
 *  (usually something to the effect of "The box contains" is
 *  displayed before calling listcont) and finishing the
 *  sentence (usually by displaying a period).  An object is listed
 *  only if its isListed property is true.
 */
listcont: function( obj )
{
    local i, count, tot, list, cur, disptot;
    count := 0;
    list := obj.contents;
    tot := length( list );
    disptot := itemcnt( list );
    i := 1;
    while ( i <= tot )
    {
	cur := list[i];
	if ( cur.isListed )
	{
	    if ( count > 0 )
	    {
		if ( count+1 < disptot )
		    ", ";
		else if (count = 1)
		    " and ";
		else
		    ", and ";
	    }
	    count := count + 1;
	    cur.adesc;               // list this object
	    if ( cur.isworn ) " (being worn)";
	    if ( cur = Me.usedGun) " (being wielded)";
	    if ( cur.islamp and cur.islit ) " (providing light)";
	}
	i := i + 1;
    }
}

/*
 *   showcontcont:  list the contents of the object, plus the contents of
 *   an fixeditem's contained by the object.  A complete sentence is shown.
 *   This is an internal routine used by listcontcont and listfixedcontcont.
 */
showcontcont: function( obj )
{
    if (itemcnt( obj.contents ))
    {
	if ( obj.issurface and not obj.isqsurface )
	{
	    "Sitting on "; obj.thedesc;" is "; listcont( obj );
	    ". ";
	}
	else if ( obj.contentsVisible and not obj.isqcontainer )
	{
	    caps();
	    obj.thedesc; " seems to contain ";
	    listcont( obj );
	    ". ";
	}
    }
    if ( obj.contentsVisible and not obj.isqcontainer )
	listfixedcontcont( obj );
}

/*
 *  listfixedcontcont: function( obj )
 *
 *  List the contents of the contents of any fixeditem objects
 *  in the contents list of the object obj.  This routine
 *  makes sure that all objects that can be taken are listed somewhere
 *  in a room's description.  This routine recurses down the contents
 *  tree, following each branch until either something has been listed
 *  or the branch ends without anything being listable.  This routine
 *  displays a complete sentence, so no introductory or closing text
 *  is needed.
 */
listfixedcontcont: function( obj )
{
    local list, i, tot, thisobj;

    list := obj.contents;
    tot := length( list );
    i := 1;
    while ( i <= tot )
    {
	thisobj := list[i];
	if ( thisobj.isfixed and thisobj.contentsVisible and
	  not thisobj.isqcontainer )
	    showcontcont( thisobj );
	i := i + 1;
    }
}

/*
 *  listcontcont: function( obj )
 *
 *  This function lists the contents of the contents of an object.
 *  It displays full sentences, so no introductory or closing text
 *  is required.  Any item in the contents list of the object
 *  obj whose contentsVisible property is true has
 *  its contents listed.  An Object whose isqcontainer or
 *  isqsurface property is true will not have its
 *  contents listed.
 */
listcontcont: function( obj )
{
    local list, i, tot;
    list := obj.contents;
    tot := length( list );
    i := 1;
    while ( i <= tot )
    {
	showcontcont( list[i] );
	i := i + 1;
    }
}

/*
 *  scoreStatus: function(points, turns)
 *
 *  This function updates the score on the status line.  This implementation
 *  simply calls the built-in function setscore() with the same information.
 *  The call to setscore() has been isolated in this function to make it
 *  easier to replace with a customized version; to replace the status line
 *  score display, simply replace this routine.
 */
scoreStatus: function(points, turns)
{
    setscore(points, turns);
}

/*
 *  turncount: function( parm )
 *
 *  This function can be used as a daemon (normally set up in the init
 *  function) to update the turn counter after each turn.  This routine
 *  increments global.turnsofar, and then calls setscore to
 *  update the status line with the new turn count.
 */
turncount: function( parm )
{
    incturn();
    global.turnsofar := global.turnsofar + 1;
    scoreStatus( global.score, global.turnsofar );
}

/*
 *  addweight: function( list )
 *
 *  Adds the weights of the objects in list and returns the sum.
 *  The weight of an object is given by its weight property.  This
 *  routine includes the weights of all of the contents of each object,
 *  and the weights of their contents, and so forth.
 */
addweight: function( l )
{
    local tot, i, c, totweight;

    tot := length( l );
    i := 1;
    totweight := 0;
    while ( i <= tot )
    {
	c := l[i];
	totweight := totweight + c.weight;
	if (length( c.contents ))
	    totweight := totweight + addweight( c.contents );
	i := i + 1;
    }
    return( totweight );
}

/*
 *  addbulk: function( list )
 *
 *  This function returns the sum of the bulks (given by the bulk
 *  property) of each object in list.  The value returned includes
 *  only the bulk of each object in the list, and not of the contents
 *  of the objects, as it is assumed that an object does not change in
 *  size when something is put inside it.  You can easily change this
 *  assumption for special objects (such as a bag that stretches as
 *  things are put inside) by writing an appropriate bulk method
 *  for that object.
 */
addbulk: function( list )
{
    local i, tot, totbulk, rem, cur;

    tot := length( list );
    i := 1;
    totbulk := 0;
    while( i <= tot )
    {
	cur := list[i];
	if ( not cur.isworn )
	    totbulk := totbulk + cur.bulk;
	i := i + 1;
    }
    return( totbulk );
}

/*
 *  incscore: function( amount )
 *
 *  Adds amount to the total score, and updates the status line
 *  to reflect the new score.  The total score is kept in global.score.
 *  Always use this routine rather than changing global.score
 *  directly, since this routine ensures that the status line is
 *  updated with the new value.
 */
incscore: function( amount )
{
    global.score := global.score + amount;
    if (global.score <= 0)
    {                        //minimum possible health is 0
       global.score := 0;
       die();                //and if 0, you die.
    }
    scoreStatus( global.score, global.turnsofar ); //update status line
}

/*
 *  initSearch: function
 *
 *  Initializes the containers of objects with a searchLoc, underLoc,
 *  and behindLoc by setting up searchCont, underCont, and
 *  behindCont lists, respectively.  You should call this function once in
 *  your preinit (or init, if you prefer) function to ensure that
 *  the underable, behindable, and searchable objects are set up correctly.
 *  
 *  As a bonus, we take this opportunity to initialize global.floatingList
 *  with a list of all objects of class floatingItem.  It is necessary to
 *  initialize this list, so that validDoList and validIoList include objects
 *  with variable location properties.  Note that, for this to work,
 *  all objects with variable location properties must be declared
 *  to be of class floatingItem.
 */
initSearch: function
{
    local o;
    
    o := firstobj(hiddenItem);
    while (o <> nil)
    {
	if (o.searchLoc)
	    o.searchLoc.searchCont := o.searchLoc.searchCont + o;
	else if (o.underLoc)
	    o.underLoc.underCont := o.underLoc.underCont + o;
	else if (o.behindLoc)
	    o.behindLoc.behindCont := o.behindLoc.behindCont + o;
	o := nextobj(o, hiddenItem);
    }
    
    global.floatingList := [];
    for (o := firstobj(floatingItem) ; o ; o := nextobj(o, floatingItem))
	global.floatingList += o;
}

/*
 *  reachableList: function
 *
 *  Returns a list of all the objects reachable from a given object.
 *  That is, if the object is open or is not an openable, it returns the
 *  contents of the object, plus the reachableList result of each object;
 *  if the object is closed, it returns an empty list.
 */
reachableList: function(obj)
{
    local ret := [];
    local i, lst, len;
    
    if (not isclass(obj, openable)
	or (isclass(obj, openable) and obj.isopen))
    {
	lst := obj.contents;
	len := length(lst);
	ret += lst;
	for (i := 1 ; i <= len ; ++i)
	    ret += reachableList(lst[i]);
    }

    return(ret);
}

/*
 *  visibleList: function
 *
 *  This function is similar to reachableList, but returns the
 *  list of objects visible within a given object.
 */
visibleList: function(obj)
{
    local ret := [];
    local i, lst, len;
    
    if (not isclass(obj, openable)
	or (isclass(obj, openable) and obj.isopen)
	or obj.contentsVisible)
    {
	lst := obj.contents;
	len := length(lst);
	ret += lst;
	for (i := 1 ; i <= len ; ++i)
	    ret += visibleList(lst[i]);
    }

    return(ret);
}

/*
 *  nestedroom: room
 *
 *  A special kind of room that is inside another room; chairs and
 *  some types of vehicles, such as inflatable rafts, fall into this
 *  category.  Note that a room can be within another room without
 *  being a nestedroom, simply by setting its location property
 *  to another room.  The nestedroom is different from an ordinary
 *  room, though, in that it's an "open" room; that is, when inside it,
 *  the actor is still really inside the enclosing room for purposes of
 *  descriptions.  Hence, the player sees "Laboratory, in the chair."
 *  In addition, a nestedroom is an object in its own right,
 *  visible to the player; for example, a chair is an object in a
 *  room in addition to being a room itself.  The statusPrep
 *  property displays the preposition in the status description; by
 *  default, it will be "in," but some subclasses and instances
 *  will want to change this to a more appropriate preposition.
 *  outOfPrep is used to report what happens when the player
 *  gets out of the object:  it should be "out of" or "off of" as
 *  appropriate to this object.
 */
class nestedroom: room
    statusPrep = "in"
    outOfPrep = "out of"
    islit =
    {
	if ( self.location ) return( self.location.islit );
	return( nil );
    }
    statusLine =
    {
	"<<self.location.sdesc>>, <<self.statusPrep>> <<self.thedesc>>\n\t";
    }
    lookAround( verbosity ) =
    {
	self.statusLine;
	self.location.nrmLkAround( verbosity );
    }
    roomDrop( obj ) =
    {
	if ( self.location = nil or self.isdroploc ) pass roomDrop;
	else self.location.roomDrop( obj );
    }
;

/*
 *  chairitem: fixeditem, nestedroom, surface
 *
 *  Acts like a chair:  actors can sit on the object.  While sitting
 *  on the object, an actor can't go anywhere until standing up, and
 *  can only reach objects that are on the chair and in the chair's
 *  reachable list.  By default, nothing is in the reachable
 *  list.  Note that there is no real distinction made between chairs
 *  and beds, so you can sit or lie on either; the only difference is
 *  the message displayed describing the situation.
 */
class chairitem: fixeditem, nestedroom, surface
    reachable = ([] + self) // list of all containers reachable from here;
			    //  normally, you can only reach carried items
			    //  from a chair, but this makes special allowances
    ischair = true          // it is a chair by default; for beds or other
			    //  things you lie down on, make it false
    outOfPrep = "out of"
    roomAction( actor, v, dobj, prep, io ) =
    {
	if ( dobj<>nil and v<>inspectVerb )
	    checkReach( self, actor, v, dobj );
	if ( io<>nil and v<>askVerb and v<>tellVerb )
	    checkReach( self, actor, v, io );
	pass roomAction;
    }
    enterRoom( actor ) = {}
    noexit =
    {
	"%You're% not going anywhere until %you% get%s% out of ";
	self.thedesc; ". ";
	return( nil );
    }
    verDoBoard( actor ) = { self.verDoSiton( actor ); }
    doBoard( actor ) = { self.doSiton( actor ); }
    verDoSiton( actor ) =
    {
	if ( actor.location = self )
	{
	    "%You're% already on "; self.thedesc; "! ";
	}
    }
    doSiton( actor ) =
    {
	"Okay, %you're% now sitting on "; self.thedesc; ". ";
	actor.travelTo( self );
    }
    verDoLieon( actor ) =
    {
	self.verDoSiton( actor );
    }
    doLieon( actor ) =
    {
	self.doSiton( actor );
    }
;

/*
 *  beditem: chairitem
 *
 *  This object is the same as a chairitem, except that the player
 *  is described as lying on, rather than sitting in, the object.
 */
class beditem: chairitem
    ischair = nil
    isbed = true
    sdesc = "bed"
    statusPrep = "on"
    outOfPrep = "out of"
;
    
/*
 *  floatingItem: object
 *
 *  This class doesn't do anything apart from mark an object as having a
 *  variable location property.  It is necessary to mark all such
 *  items by making them a member of this class, so that the objects are
 *  added to global.floatingList, which is necessary so that floating
 *  objects are included in validDoList and validIoList values (see
 *  the deepverb class for a description of these methods).
 */
class floatingItem: object
;

/*
 *  thing: object
 *
 *  The basic class for objects in a game.  The property contents
 *  is a list that specifies what is in the object; this property is
 *  automatically set up by the system after the game is compiled to
 *  contain a list of all objects that have this object as their
 *  location property.  The contents property is kept
 *  consistent with the location properties of referenced objects
 *  by the moveInto method; always use moveInto rather than
 *  directly setting a location property for this reason.  The
 *  adesc method displays the name of the object with an indefinite
 *  article; the default is to display "a" followed by the sdesc,
 *  but objects that need a different indefinite article (such as "an"
 *  or "some") should override this method.  Likewise, thedesc
 *  displays the name with a definite article; by default, thedesc
 *  displays "the" followed by the object's sdesc.  The sdesc
 *  simply displays the object's name ("short description") without
 *  any articles.  The ldesc is the long description, normally
 *  displayed when the object is examined by the player; by default,
 *  the ldesc displays "It looks like an ordinary sdesc."
 *  The isIn(object) method returns true if the
 *  object's location is the specified object or the object's
 *  location is an object whose contentsVisible property is
 *  true and that object's isIn(object) method is
 *  true.  Note that if isIn is true, it doesn't
 *  necessarily mean the object is reachable, because isIn is
 *  true if the object is merely visible within the location.
 *  The thrudesc method displays a message for when the
 *  player looks through the object (objects such as windows would
 *  use this property).  The moveInto(object) method
 *  moves the object to be inside the specified object.
 *  To make an object disappear, move it into nil.
 */
class thing: object
    isListed = true         // shows up in room/inventory listings
    contents = []           // set up automatically by system - do not set
    verGrab( obj ) = {}
    Grab( obj ) = {}
    adesc =
    {
	"a "; self.sdesc;   // default is "a <name>"; "self" is current object
    }
    thedesc =
    {
	"the "; self.sdesc; // default is "the <name>"
    }
    ldesc = { "It looks like an ordinary "; self.sdesc; " to me."; }
    readdesc = { "%You% can't read "; self.adesc; ". "; }
    actorAction( v, d, p, i ) =
    {
	"You have lost your mind. ";
	exit;
    }
    contentsVisible = { return( true ); }
    contentsReachable = { return( true ); }
    isIn( obj ) =
    {
	local myloc;

	myloc := self.location;
	if ( myloc )
	{
	    if ( myloc = obj ) return( true );
	    if ( myloc.contentsVisible ) return( myloc.isIn( obj ));
	}
	return( nil );
    }
    thrudesc = { "%You% can't see much through "; self.thedesc; ".\n"; }
    moveInto( obj ) =
    {
	local loc;

	/*
	 *   For the object containing me, and its container, and so forth,
	 *   tell it via a Grab message that I'm going away.
	 */
	loc := self.location;
	while ( loc )
	{
	    loc.Grab( self );
	    loc := loc.location;
	}

	if ( self.location )
	    self.location.contents := self.location.contents - self;
	self.location := obj;
	if ( obj ) obj.contents := obj.contents + self;
    }
    verDoSave( actor ) =
    {
	"Please specify the name of the game to save in double quotes,
	for example, SAVE \"GAME1\". ";
    }
    verDoRestore( actor ) =
    {
	"Please specify the name of the game to restore in double quotes,
	for example, RESTORE \"GAME1\". ";
    }
    verDoScript( actor ) =
    {
	"You should type the name of a file to write the transcript to
	in quotes, for example, SCRIPT \"LOG1\". ";
    }
    verDoSay( actor ) =
    {
	"You should say what you want to say in double quotes, for example,
	SAY \"HELLO\". ";
    }
    verDoPush( actor ) =
    {
	"Pushing "; self.thedesc; " doesn't do anything. ";
    }
    verDoWear( actor ) =
    {
	"%You% can't wear "; self.thedesc; ". ";
    }
    verDoTake( actor ) =
    {
	if ( self.location = actor )
	{
	    "%You% already %have% "; self.thedesc; "! ";
	}
	else self.verifyRemove( actor );
    }
    verifyRemove( actor ) =
    {
	/*
	 *   Check with each container to make sure that the container
	 *   doesn't object to the object's removal.
	 */
	local loc;

	loc := self.location;
	while ( loc )
	{
	    if ( loc <> actor ) loc.verGrab( self );
	    loc := loc.location;
	}
    }
    isVisible( vantage ) =
    {
	local loc;

	loc := self.location;
	if ( loc = nil ) return( nil );

	/* if it's in the vantage, it's visible */
	if ( loc = vantage ) return( true );

	/*
	 *   if its location's contents are visible, and its location is
	 *   itself visible, it's visible
	 */
	if ( loc.contentsVisible and loc.isVisible( vantage )) return( true );

	/*
	 *   If the vantage has a location, and the vantage's location's
	 *   contents are visible (if you can see me I can see you), and
	 *   the object is visible from the vantage's location, the object
	 *   is visible
	 */
	if ( vantage.location <> nil and vantage.location.contentsVisible and
	 self.isVisible( vantage.location ))
	    return( true );

	/* all tests failed:  it's not visible */
	return( nil );
    }
    cantReach( actor ) =
    {
	if ( self.location = nil )
	{
	    if ( actor.location.location )
	       "%You% can't reach that from << actor.location.thedesc >>. ";
	    return;
	}
	if ( not self.location.isopenable or self.location.isopen )
	    self.location.cantReach( actor );
	else "%You%'ll have to open << self.location.thedesc >> first. ";
    }
    isReachable( actor ) =
    {
	local loc;

	/* if the object is in the room's 'reachable' list, it's reachable */
	if (find( actor.location.reachable, self ) <> nil )
	    return( true );

	/*
	 *   If the object's container's contents are reachable, and the
	 *   container is reachable, the object is reachable.
	 */
	loc := self.location;
	if (find( actor.location.reachable, self ) <> nil )
	    return( true );
	if ( loc = nil ) return( nil );
	if ( loc = actor or loc = actor.location ) return( true );
	if ( loc.contentsReachable )
	    return( loc.isReachable( actor ));
	return( nil );
	return( nil );
    }
    doTake( actor ) =
    {
	local totbulk, totweight;

	totbulk := addbulk( actor.contents ) + self.bulk;
	totweight := addweight( actor.contents );
	if ( not actor.isCarrying( self ))
	    totweight := totweight + self.weight;

	if ( totweight > actor.maxweight )
	    "%Your% load is too heavy. ";
	else if ( totbulk > actor.maxbulk )
	    "%You've% already got %your% hands full. ";
	else
	{
	    self.moveInto( actor );
	    "Taken. ";
	}
    }
    verDoDrop( actor ) =
    {
	if ( not actor.isCarrying( self ))
	{
	    "%You're% not carrying "; self.thedesc; "! ";
	}
	else self.verifyRemove( actor );
    }
    doDrop( actor ) =
    {
	actor.location.roomDrop( self );
    }
    verDoUnwear( actor ) =
    {
	"%You're% not wearing "; self.thedesc; "! ";
    }
    verIoPutIn( actor ) =
    {
	"%You% can't put anything into "; self.thedesc; ". ";
    }
    verDoPutIn( actor, io ) =
    {
	if ( io = nil ) return;

	if ( self.location = io )
	{
	    caps(); self.thedesc; " is already in "; io.thedesc; "! ";
	}
	else if ( io = self or io.isIn( self ))
	{
	    "%You% can't put "; self.thedesc; " in itself! ";
	}
	else self.verifyRemove( actor );
    }
    doPutIn( actor, io ) =
    {
	self.moveInto( io );
	"Done. ";
    }
    verIoPutOn( actor ) =
    {
	"There's no good surface on "; self.thedesc; ". ";
    }
    verDoPutOn( actor, io ) =
    {
	if ( io = nil ) return;

	if ( self.location = io )
	{
	    caps(); self.thedesc; " is already on "; io.thedesc; "! ";
	}
	else if ( io = self or io.isIn( self ))
	{
	    "%You% can't put "; self.thedesc; " on itself! ";
	}
	else self.verifyRemove( actor );
    }
    doPutOn( actor, io ) =
    {
	self.moveInto( io );
	"Done. ";
    }
    verIoTakeOut( actor ) = {}
    ioTakeOut( actor, dobj ) =
    {
	dobj.doTakeOut( actor, self );
    }
    verDoTakeOut( actor, io ) =
    {
	if ( io <> nil and not self.isIn( io ))
	{
	    caps(); self.thedesc; " isn't in "; io.thedesc; ". ";
	}
	self.verDoTake(actor);         /* ensure object can be taken at all */
    }
    doTakeOut( actor, io ) =
    {
	self.doTake( actor );
    }
    verIoTakeOff( actor ) = {}
    ioTakeOff( actor, dobj ) =
    {
	dobj.doTakeOff( actor, self );
    }
    verDoTakeOff( actor, io ) =
    {
	if ( io <> nil and not self.isIn( io ))
	{
	    caps(); self.thedesc; " isn't on "; io.thedesc; "! ";
	}
	self.verDoTake(actor);         /* ensure object can be taken at all */
    }
    doTakeOff( actor, io ) =
    {
	self.doTake( actor );
    }
    verIoPlugIn( actor ) =
    {
	"%You% can't plug anything into "; self.thedesc; ". ";
    }
    verDoPlugIn( actor, io ) =
    {
	"%You% can't plug "; self.thedesc; " into anything. ";
    }
    verIoUnplugFrom( actor ) =
    {
	"It's not plugged into "; self.thedesc; ". ";
    }
    verDoUnplugFrom( actor, io ) =
    {
	if ( io <> nil ) { "It's not plugged into "; io.thedesc; ". "; }
    }
    verDoLookin( actor ) =
    {
	"There's nothing in "; self.thedesc; ". ";
    }
    verDoLookthru( actor ) =
    {
	"%You% can't see anything through "; self.thedesc; ". ";
    }
    verDoLookunder( actor ) =
    {
	"There's nothing under "; self.thedesc; ". ";
    }
    verDoInspect( actor ) = {}
    doInspect( actor ) =
    {
	self.ldesc;
    }
    verDoRead( actor ) =
    {
	"I don't know how to read "; self.thedesc; ". ";
    }
    verDoLookbehind( actor ) =
    {
	"There's nothing behind "; self.thedesc; ". ";
    }
    verDoTurn( actor ) =
    {
	"Turning "; self.thedesc; " doesn't have any effect. ";
    }
    verDoTurnWith( actor, io ) =
    {
	"Turning "; self.thedesc; " doesn't have any effect. ";
    }
    verDoTurnTo( actor, io ) =
    {
	"Turning "; self.thedesc; " doesn't have any effect. ";
    }
    verIoTurnTo( actor ) =
    {
	"I don't know how to do that. ";
    }
    verDoTurnon( actor ) =
    {
	"I don't know how to turn "; self.thedesc; " on. ";
    }
    verDoTurnoff( actor ) =
    {
	"I don't know how to turn "; self.thedesc; " off. ";
    }
    verIoAskAbout( actor ) = {}
    ioAskAbout( actor, dobj ) =
    {
	dobj.doAskAbout( actor, self );
    }
    verDoAskAbout( actor, io ) =
    {
	"Surely, %you% can't think "; self.thedesc; " knows anything
	about it! ";
    }
    verIoTellAbout( actor ) = {}
    ioTellAbout( actor, dobj ) =
    {
	dobj.doTellAbout( actor, self );
    }
    verDoTellAbout( actor, io ) =
    {
	"It doesn't look as though "; self.thedesc; " is interested. ";
    }
    verDoUnboard( actor ) =
    {
	if ( actor.location <> self )
	{
	    "%You're% not in "; self.thedesc; "! ";
	}
	else if ( self.location=nil )
	{
	    "%You% can't get out of "; self.thedesc; "! ";
	}
    }
    doUnboard( actor ) =
    {
	if ( self.fastenitem )
	{
	    "%You%'ll have to unfasten "; actor.location.fastenitem.thedesc;
	    " first. ";
	}
	else
	{
	    "Okay, %you're% no longer in "; self.thedesc; ". ";
	    self.leaveRoom( actor );
	    actor.moveInto( self.location );
	}
    }
    verDoAttackWith( actor, io ) =
    {
	"Attacking "; self.thedesc; " doesn't appear productive. ";
    }
    verIoAttackWith( actor ) =
    {
	"It's not very effective to attack with "; self.thedesc; ". ";
    }
    verDoEat( actor ) =
    {
	caps(); self.thedesc; " doesn't appear appetizing. ";
    }
    verDoDrink( actor ) =
    {
	caps(); self.thedesc; " doesn't appear appetizing. ";
    }
    verDoGiveTo( actor, io ) =
    {
	if ( not actor.isCarrying( self ))
	{
	    "%You're% not carrying "; self.thedesc; ". ";
	}
	else self.verifyRemove( actor );
    }
    doGiveTo( actor, io ) =
    {
	self.moveInto( io );
	"Done. ";
    }
    verDoPull( actor ) =
    {
	"Pulling "; self.thedesc; " doesn't have any effect. ";
    }
    verDoThrowAt( actor, io ) =
    {
	if ( not actor.isCarrying( self ))
	{
	    "%You're% not carrying "; self.thedesc; ". ";
	}
	else self.verifyRemove( actor );
    }
    doThrowAt( actor, io ) =
    {
	"%You% miss%es%. ";
	self.moveInto( actor.location );
    }
    verIoThrowAt( actor ) =
    {
	if ( actor.isCarrying( self ))
	{
	    "%You% could at least drop "; self.thedesc; " first. ";
	}
    }
    ioThrowAt( actor, dobj ) =
    {
	dobj.doThrowAt( actor, self );
    }
    verDoThrowTo( actor, io ) =
    {
	if ( not actor.isCarrying( self ))
	{
	    "%You're% not carrying "; self.thedesc; ". ";
	}
	else self.verifyRemove( actor );
    }
    doThrowTo( actor, io ) =
    {
	"%You% miss%es%. ";
	self.moveInto( actor.location );
    }
    verDoThrow( actor ) =
    {
	if ( not actor.isCarrying( self ))
	{
	    "%You're% not carrying "; self.thedesc; ". ";
	}
	else self.verifyRemove( actor );
    }
    doThrow( actor ) =
    {
	"Thrown. ";
	self.moveInto( actor.location );
    }
    verDoShowTo( actor, io ) =
    {
    }
    doShowTo( actor, io ) =
    {
	if ( io <> nil ) { caps(); io.thedesc; " isn't impressed. "; }
    }
    verIoShowTo( actor ) =
    {
	caps(); self.thedesc; " isn't impressed. ";
    }
    verDoClean( actor ) =
    {
	caps(); self.thedesc; " looks a bit cleaner now. ";
    }
    verDoCleanWith( actor, io ) = {}
    doCleanWith( actor, io ) =
    {
	caps(); self.thedesc; " looks a bit cleaner now. ";
    }
    verDoMove( actor ) =
    {
	"Moving "; self.thedesc; " doesn't reveal anything. ";
    }
    verDoMoveTo( actor, io ) =
    {
	"Moving "; self.thedesc; " doesn't reveal anything. ";
    }
    verIoMoveTo( actor ) =
    {
	"That doesn't get us anywhere. ";
    }
    verDoMoveWith( actor, io ) =
    {
	"Moving "; self.thedesc; " doesn't reveal anything. ";
    }
    verIoMoveWith( actor ) =
    {
	caps(); self.thedesc; " doesn't seem to help. ";
    }
    verDoTypeOn( actor, io ) =
    {
	"You should say what you want to type in double quotes, for
	example, TYPE \"HELLO\" ON KEYBOARD. ";
    }
    verDoTouch( actor ) =
    {
	"Touching "; self.thedesc; " doesn't seem to have any effect. ";
    }
    verDoPoke( actor ) =
    {
	"Poking "; self.thedesc; " doesn't seem to have any effect. ";
    }
    genMoveDir = { "%You% can't seem to do that. "; }
    verDoMoveN( actor ) = { self.genMoveDir; }
    verDoMoveS( actor ) = { self.genMoveDir; }
    verDoMoveE( actor ) = { self.genMoveDir; }
    verDoMoveW( actor ) = { self.genMoveDir; }
    verDoMoveNE( actor ) = { self.genMoveDir; }
    verDoMoveNW( actor ) = { self.genMoveDir; }
    verDoMoveSE( actor ) = { self.genMoveDir; }
    verDoMoveSW( actor ) = { self.genMoveDir; }
    verDoSearch( actor ) =
    {
	"%You% find%s% nothing of interest. ";
    }
;

/*
 *  item: thing
 *
 *  A basic item which can be picked up by the player.  It has no weight
 *  (0) and minimal bulk (1).  The weight property should be set
 *  to a non-zero value for heavy objects.  The bulk property
 *  should be set to a value greater than 1 for bulky objects, and to
 *  zero for objects that are very small and take essentially no effort
 *  to hold---or, more precisely, don't detract at all from the player's
 *  ability to hold other objects (for example, a piece of paper).
 */
class item: thing
    weight = 0
    bulk = 1
;
    
/*
 *  lightsource: item
 *
 *  A portable lamp, candle, match, or other source of light.  The
 *  light source can be turned on and off with the islit property.
 *  If islit is true, the object provides light, otherwise it's
 *  just an ordinary object.
 */
class lightsource: item
    islamp = true
;

/*
 *  hiddenItem: object
 *
 *  This is an object that is hidden with one of the hider classes. 
 *  A hiddenItem object doesn't have any special properties in its
 *  own right, but all objects hidden with one of the hider classes
 *  must be of class hiddenItem so that initSearch can find
 *  them.
 */
class hiddenItem: object
;

/*
 *  hider: item
 *
 *  This is a basic class of object that can hide other objects in various
 *  ways.  The underHider, behindHider, and searchHider classes
 *  are examples of hider subclasses.  The class defines
 *  the method searchObj(actor, list), which is given the list
 *  of hidden items contained in the object (for example, this would be the
 *  underCont property, in the case of an underHider), and "finds"
 *  the object or objects. Its action is dependent upon a couple of other
 *  properties of the hider object.  The serialSearch property,
 *  if true, indicates that items in the list are to be found one at
 *  a time; if nil (the default), the entire list is found on the
 *  first try.  The autoTake property, if true, indicates that
 *  the actor automatically takes the item or items found; if nil, the
 *  item or items are moved to the actor's location.  The searchObj method
 *  returns the list with the found object or objects removed; the
 *  caller should assign this returned value back to the appropriate
 *  property (for example, underHider will assign the return value
 *  to underCont).
 *  
 *  Note that because the hider is hiding something, this class
 *  overrides the normal verDoSearch method to display the
 *  message, "You'll have to be more specific about how you want
 *  to search that."  The reason is that the normal verDoSearch
 *  message ("You find nothing of interest") leads players to believe
 *  that the object was exhaustively searched, and we want to avoid
 *  misleading the player.  On the other hand, we don't want a general
 *  search to be exhaustive for most hider objects.  So, we just
 *  display a message letting the player know that the search was not
 *  enough, but we don't give away what they have to do instead.
 *  
 *  The objects hidden with one of the hider classes must be
 *  of class hiddenItem.
 */
class hider: item
    verDoSearch(actor) =
    {
	"%You%'ll have to be more specific about how %you% want%s%
	to search that. ";
    }
    searchObj(actor, list) =
    {
	local found, dest, i, tot;

	/* see how much we get this time */
	if (self.serialSearch)
	{
	    found := [] + car(list);
	    list := cdr(list);
	}
	else
	{
	    found := list;
	    list := nil;
	}

	/* figure destination */
	dest := actor;
	if (not self.autoTake) dest := dest.location;
	
	/* note what we found, and move it to destination */
	"%You% find%s% ";
	tot := length(found);
	i := 1;
	while (i <= tot)
	{
	    found[i].adesc;
	    if (i+1 < tot) ", ";
	    else if (i = 1 and tot = 2) " and ";
	    else if (i+1 = tot and tot > 2) ", and ";
	    
	    found[i].moveInto(dest);
	    i := i + 1;
	}

	/* say what happened */
	if (self.autoTake) ", which %you% take%s%. ";
	else "! ";

	if (list<>nil and length(list)=0) list := nil;
	return(list);
    }
    serialSearch = nil             /* find everything in one try by default */
    autoTake = true               /* actor takes item when found by default */
;

/*
 *  underHider: hider
 *
 *  This is an object that can have other objects placed under it.  The
 *  objects placed under it can only be found by looking under the object;
 *  see the description of hider for more information.  You should
 *  set the underLoc property of each hidden object to point to
 *  the underHider.
 *  
 *  Note that an underHider doesn't allow the player to put anything
 *  under the object during the game.  Instead, it's to make it easy for the
 *  game writer to set up hidden objects while implementing the game.  All you
 *  need to do to place an object under another object is declare the top
 *  object as an underHider, then declare the hidden object normally,
 *  except use underLoc rather than location to specify the
 *  location of the hidden object.  The behindHider and searchHider
 *  objects work similarly.
 *  
 *  The objects hidden with underHider must be of class hiddenItem.
 */
class underHider: hider
    underCont = []         /* list of items under me (set up by initSearch) */
    verDoLookunder(actor) = {}
    doLookunder(actor) =
    {
	if (self.underCont = nil)
	    "There's nothing else under <<self.thedesc>>. ";
	else
	    self.underCont := self.searchObj(actor, self.underCont);
    }
;

/*
 *  behindHider: hider
 *
 *  This is just like an underHider, except that objects are hidden
 *  behind this object.  Objects to be behind this object should have their
 *  behindLoc property set to point to this object.
 *  
 *  The objects hidden with behindHider must be of class hiddenItem.
 */
class behindHider: hider
    behindCont = []
    verDoLookbehind(actor) = {}
    doLookbehind(actor) =
    {
	if (self.behindCont = nil)
	    "There's nothing else behind <<self.thedesc>>. ";
	else
	    self.behindCont := self.searchObj(actor, self.behindCont);
    }
;
    
/*
 *  searchHider: hider
 *
 *  This is just like an underHider, except that objects are hidden
 *  within this object in such a way that the object must be looked in
 *  or searched.  Objects to be hidden in this object should have their
 *  searchLoc property set to point to this object.  Note that this
 *  is different from a normal container, in that the objects hidden within
 *  this object will not show up until the object is explicitly looked in
 *  or searched.
 *  
 *  The items hidden with searchHider must be of class hiddenItem.
 */
class searchHider: hider
    searchCont = []
    verDoSearch(actor) = {}
    doSearch(actor) =
    {
	if (self.searchCont = nil)
	    "There's nothing else in <<self.thedesc>>. ";
	else
	    self.searchCont := self.searchObj(actor, self.searchCont);
    }
    verDoLookin(actor) =
    {
	if (self.searchCont = nil)
	    pass verDoLookin;
    }
    doLookin(actor) =
    {
	if (self.searchCont = nil)
	    pass doLookin;
	else
	    self.searchCont := self.searchObj(actor, self.searchCont);
    }
;
    

/*
 *  fixeditem: thing
 *
 *  An object that cannot be taken or otherwise moved from its location.
 *  Note that a fixeditem is sometimes part of a movable object;
 *  this can be done to make one object part of another, ensuring that
 *  they cannot be separated.  By default, the functions that list a room's
 *  contents do not automatically describe fixeditem objects (because
 *  the isListed property is set to nil).  Instead, the game author
 *  will generally describe the fixeditem objects separately as part of
 *  the room's ldesc.  
 */
class fixeditem: thing      // An immovable object
    isListed = nil          // not listed in room/inventory displays
    isfixed = true          // Item can't be taken
    weight = 0              // no actual weight
    bulk = 0
    verDoTake( actor ) =
    {
	"%You% can't have "; self.thedesc; ". ";
    }
    verDoTakeOut( actor, io ) =
    {
	self.verDoTake( actor );
    }
    verDoDrop( actor ) =
    {
	"%You% can't drop "; self.thedesc; ". ";
    }
    verDoTakeOff( actor, io ) =
    {
	self.verDoTake( actor );
    }
    verDoPutIn( actor, io ) =
    {
	"%You% can't put "; self.thedesc; " anywhere. ";
    }
    verDoPutOn( actor, io ) =
    {
	"%You% can't put "; self.thedesc; " anywhere. ";
    }
    verDoMove( actor ) =
    {
	"%You% can't move "; self.thedesc; ". ";
    }
    verDoThrowAt(actor, iobj) =
    {
	"%You% can't throw <<self.thedesc>>.";
    }
;

/*
 *  readable: item
 *
 *  An item that can be read.  The readdesc property is displayed
 *  when the item is read.  By default, the readdesc is the same
 *  as the ldesc, but the readdesc can be overridden to give
 *  a different message.
 */
class readable: item
    verDoRead( actor ) =
    {
    }
    doRead( actor ) =
    {
	self.readdesc;
    }
    readdesc =
    {
	self.ldesc;
    }
;

/*
 *  fooditem: item
 *
 *  An object that can be eaten.  When eaten, the object is removed from
 *  the game, and global.lastMealTime is decremented by the
 *  foodvalue property.  By default, the foodvalue property
 *  is global.eatTime, which is the time between meals.  So, the
 *  default fooditem will last for one "nourishment interval."
 */
class fooditem: item
    verDoEat( actor ) =
    {
	self.verifyRemove( actor );
    }
    doEat( actor ) =
    {
	"That was delicious! ";
	global.lastMealTime := global.lastMealTime - self.foodvalue;
	self.moveInto( nil );
    }
    foodvalue = { return( global.eatTime ); }
;

/*
 *  dialItem: fixeditem
 *
 *  This class is used for making "dials," which are controls in
 *  your game that can be turned to a range of numbers.  You must
 *  define the property maxsetting as a number specifying the
 *  highest number to which the dial can be turned; the lowest number
 *  on the dial is always 1.  The setting property is the dial's
 *  current setting, and can be changed by the player by typing the
 *  command "turn dial to number."  By default, the ldesc
 *  method displays the current setting.
 */
class dialItem: fixeditem
    maxsetting = 10 // it has settings from 1 to this number
    setting = 1     // the current setting
    ldesc =
    {
	caps(); self.thedesc; " can be turned to settings
	numbered from 1 to << self.maxsetting >>. It's
	currently set to << self.setting >>. ";
    }
    verDoTurn( actor ) = {}
    doTurn( actor ) =
    {
	askio( toPrep );
    }
    verDoTurnTo( actor, io ) = {}
    doTurnTo( actor, io ) =
    {
	if ( io = numObj )
	{
	    if ( numObj.value < 1 or numObj.value > self.maxsetting )
	    {
		"There's no such setting! ";
	    }
	    else if ( numObj.value <> self.setting )
	    {
		self.setting := numObj.value;
		"Okay, it's now turned to "; say( self.setting ); ". ";
	    }
	    else
	    {
		"It's already set to "; say( self.setting ); "! ";
	    }
	}
	else
	{
	    "I don't know how to turn "; self.thedesc;
	    " to that. ";
	}
    }
;

/*
 *  switchItem: fixeditem
 *
 *  This is a class for things that can be turned on and off by the
 *  player.  The only special property is isActive, which is nil
 *  if the switch is turned off and true when turned on.  The object
 *  accepts the commands "turn it on" and "turn it off,'' as well as
 *  synonymous constructions, and updates isActive accordingly.
 */
class switchItem: fixeditem
    verDoSwitch( actor ) = {}
    doSwitch( actor ) =
    {
	self.isActive := not self.isActive;
	"Okay, "; self.thedesc; " is now switched ";
	if ( self.isActive ) "on"; else "off";
	". ";
    }
    verDoTurnon( actor ) =
    {
	if ( self.isActive ) "It's already turned on! ";
    }
    doTurnon( actor ) =
    {
	self.isActive := true;
	"Okay, it's now turned on. ";
    }
    verDoTurnoff( actor ) =
    {
	if ( not self.isActive ) "It's already turned off! ";
    }
    doTurnoff( actor ) =
    {
	self.isActive := nil;
	"Okay, it's now turned off. ";
    }
;

/*
 *  room: thing
 *
 *  A location in the game.  By default, the islit property is
 *  true, which means that the room is lit (no light source is
 *  needed while in the room).  You should create a darkroom
 *  object rather than a room with islit set to nil if you
 *  want a dark room, because other methods are affected as well.
 *  The isseen property records whether the player has entered
 *  the room before; initially it's nil, and is set to true
 *  the first time the player enters.  The roomAction(actor,
 *  verb, directObject, preposition, indirectObject) method is
 *  activated for each player command; by default, all it does is
 *  call the room's location's roomAction method if the room
 *  is inside another room.  The lookAround(verbosity)
 *  method displays the room's description for a given verbosity
 *  level; true means a full description, nil means only
 *  the short description (just the room name plus a list of the
 *  objects present).  roomDrop(object) is called when
 *  an object is dropped within the room; normally, it just moves
 *  the object to the room and displays "Dropped."  The firstseen
 *  method is called when isseen is about to be set true
 *  for the first time (i.e., when the player first sees the room);
 *  by default, this routine does nothing, but it's a convenient
 *  place to put any special code you want to execute when a room
 *  is first entered.  The firstseen method is called after
 *  the room's description is displayed.
 */
class room: thing
    /*
     *   'reachable' is the list of explicitly reachable objects, over and
     *   above the objects that are here.  This is mostly used in nested
     *   rooms to make objects in the containing room accessible.  Most
     *   normal rooms will leave this as an empty list.
     */
    reachable = []
	
    /*
     *   roomCheck is true if the verb is valid in the room.  This
     *   is a first pass; generally, its only function is to disallow
     *   certain commands in a dark room.
     */
    roomCheck( v ) = { return( true ); }
    islit = true            // rooms are lit unless otherwise specified
    isseen = nil            // room has not been seen yet
    enterRoom( actor ) =    // sent to room as actor is entering it
    {
	self.lookAround(( not self.isseen ) or global.verbose );
	if ( self.islit )
	{
	    if (not self.isseen) self.firstseen;
	    self.isseen := true;
	}
    }
    roomAction( a, v, d, p, i ) =
    {
	if ( self.location ) self.location.roomAction( a, v, d, p, i );
    }

    /*
     *   Whenever a normal object (i.e., one that does not override the
     *   default doDrop provided by 'thing') is dropped, the actor's
     *   location is sent roomDrop(object being dropped).  By default, 
     *   we move the object into this room.
     */
    roomDrop( obj ) =
    {
	"Dropped. ";
	obj.moveInto( self );
    }

    /*
     *   Whenever an actor leaves this room, we run through the leaveList.
     *   This is a list of objects that have registered themselves with us
     *   via addLeaveList().  For each object in the leaveList, we send
     *   a "leaving" message, with the actor as the parameter.  It should
     *   return true if it wants to be removed from the leaveList, nil
     *   if it wants to stay.
     */
    leaveList = []
    addLeaveList( obj ) =
    {
	self.leaveList := self.leaveList + obj;
    }
    leaveRoom( actor ) =
    {
	local tmplist, thisobj, i, tot;

	tmplist := self.leaveList;
	tot := length( tmplist );
	i := 1;
	while ( i <= tot )
	{
	    thisobj := tmplist[i];
	    if ( thisobj.leaving( actor ))
		self.leaveList := self.leaveList - thisobj;
	    i := i + 1;
	}
    }
    /*
     *   lookAround describes the room.  If verbosity is true, the full
     *   description is given, otherwise an abbreviated description (without
     *   the room's ldesc) is displayed.
     */
    nrmLkAround( verbosity ) =      // lookAround without location status
    {
	local l, cur, i, tot;

	if ( verbosity )
	{
	    "\n\t"; self.ldesc;

	    l := self.contents;
	    tot := length( l );
	    i := 1;
	    while ( i <= tot )
	    {
		cur := l[i];
		if ( cur.isfixed ) cur.heredesc;
		i := i + 1;
	    }
	}
	"\n\t";
	if (itemcnt( self.contents ))
	{
	    "You see "; listcont( self ); " here. ";
	}
	listcontcont( self ); "\n";

	l := self.contents;
	tot := length( l );
	i := 1;
	while ( i <= tot )
	{
	    cur := l[i];
	    if ( cur.isactor )
	    {
		if ( cur <> Me )
		{
		    "\n\t";
		    cur.actorDesc;
		}
	    }
	    i := i + 1;
	}
    }
    statusLine =
    {
	self.sdesc; "\n\t";
    }
    lookAround( verbosity ) =
    {
	self.statusLine;
	self.nrmLkAround( verbosity );
    }
    
    /*
     *   Direction handlers.  The directions are all set up to
     *   the default, which is no travel allowed.  To make travel
     *   possible in a direction, just assign a room to the direction
     *   property.
     */
    north = { return( self.noexit ); }
    south = { return( self.noexit ); }
    east  = { return( self.noexit ); }
    west  = { return( self.noexit ); }
    up    = { return( self.noexit ); }
    down  = { return( self.noexit ); }
    ne    = { return( self.noexit ); }
    nw    = { return( self.noexit ); }
    se    = { return( self.noexit ); }
    sw    = { return( self.noexit ); }
    in    = { return( self.noexit ); }
    out   = { return( self.noexit ); }
    
    /*
     *   noexit displays a message when the player attempts to travel
     *   in a direction in which travel is not possible.
     */
    noexit = { "%You% can't go that way. "; return( nil ); }
;

/*
 *  darkroom: room
 *
 *  A dark room.  The player must have some object that can act as a
 *  light source in order to move about and perform most operations
 *  while in this room.  Note that the room's lights can be turned
 *  on by setting the room's lightsOn property to true;
 *  do this instead of setting islit, because islit is
 *  a method which checks for the presence of a light source.
 */
class darkroom: room        // An enterable area which might be dark
    islit =                 // true ONLY if something is lighting the room
    {
	local rem, cur, tot, i;

	if ( self.lightsOn ) return( true );

	rem := global.lamplist;
	tot := length( rem );
	i := 1;
	while ( i <= tot )
	{
	    cur := rem[i];
	    if ( cur.isIn( self ) and cur.islit ) return( true );
	    i := i + 1;
	}
	return( nil );
    }
    roomAction( actor, v, dobj, prep, io ) =
    {
	if ( not self.islit and not v.isTravelVerb and not v.issysverb )
	{
	    "%You% can't see a thing. ";
	    exit;
	}
	else pass roomAction;
    }
    statusLine =
    {
	if ( self.islit ) pass statusLine;
	else "In the dark.";
    }
    lookAround( verbosity ) =
    {
	if ( self.islit ) pass lookAround;
	else "It's pitch black. ";
    }
    noexit =
    {
	if ( self.islit ) pass noexit;
	else
	{
	    darkTravel();
	    return( nil );
	}
    }
    roomCheck( v ) =
    {
	if ( self.islit or v.issysverb or v.isTravelVerb ) return( true );
	else
	{
	    "It's pitch black.\n";
	    return( nil );
	}
    }
;

/*
 *  theFloor is a special item that appears in every room (hence
 *  the non-standard location property).  This object is included
 *  mostly for completeness, so that the player can refer to the
 *  floor; otherwise, it doesn't do much.  Dropping an item on the
 *  floor, for example, moves it to the current room.
 */
theFloor: beditem, floatingItem
    noun = 'floor' 'ground'
    sdesc = "ground"
    adesc = "the ground"
    statusPrep = "on"
    outOfPrep = "off of"
    location =
    {
	if ( Me.location = self )
	    return( self.sitloc );
	else
	    return( Me.location );
    }
    locationOK = true        // suppress warning about location being a method
    doSiton( actor ) =
    {
	"Okay, %you're% now sitting on "; self.thedesc; ". ";
	self.sitloc := actor.location;
	actor.moveInto( self );
    }
    doLieon( actor ) =
    {
	self.doSiton( actor );
    }
    ioPutOn( actor, dobj ) =
    {
	dobj.doDrop( actor );
    }
    ioPutIn( actor, dobj ) =
    {
	dobj.doDrop( actor );
    }
;

/*
 *  Actor: fixeditem, movableActor
 *
 *  A character in the game.  The maxweight property specifies
 *  the maximum weight that the character can carry, and the maxbulk
 *  property specifies the maximum bulk the character can carry.  The
 *  actorAction(verb, directObject, preposition, indirectObject)
 *  method specifies what happens when the actor is given a command by
 *  the player; by default, the actor ignores the command and displays
 *  a message to this effect.  The isCarrying(object)
 *  method returns true if the object is being carried by
 *  the actor.  The actorDesc method displays a message when the
 *  actor is in the current room; this message is displayed along with
 *  a room's description when the room is entered or examined.  The
 *  verGrab(object) method is called when someone tries to
 *  take an object the actor is carrying; by default, an actor won't
 *  let other characters take its possessions.
 *  
 *  If you want the player to be able to follow the actor when it
 *  leaves the room, you should define a follower object to shadow
 *  the character, and set the actor's myfollower property to
 *  the follower object.  The follower is then automatically
 *  moved around just behind the actor by the actor's moveInto
 *  method.
 *  
 *  The isHim property should return true if the actor can
 *  be referred to by the player as "him," and likewise isHer
 *  should be set to true if the actor can be referred to as "her."
 *  Note that both or neither can be set; if neither is set, the actor
 *  can only be referred to as "it," and if both are set, any of "him,''
 *  "her," or "it'' will be accepted.
 */
class Actor: fixeditem, movableActor
;

/*
 *  movableActor: qcontainer
 *
 *  Just like an Actor object, except that the player can
 *  manipulate the actor like an ordinary item.  Useful for certain
 *  types of actors, such as small animals.
 */
class movableActor: qcontainer // A character in the game
    isListed = nil          // described separately from room's contents
    weight = 10             // actors are pretty heavy
    bulk = 10               // and pretty bulky
    maxweight = 50          // Weight that can be carried at once
    maxbulk = 20            // Number of objects that can be carried at once
    isactor = true          // flag that this is an actor
    actorAction( v, d, p, i ) =
    {
	caps(); self.thedesc; " doesn't appear interested. ";
	exit;
    }
    isCarrying( obj ) = { return( obj.isIn( self )); }
    actorDesc =
    {
	caps(); self.adesc; " is here. ";
    }
    verGrab( item ) =
    {
	caps(); self.thedesc; " is carrying "; item.thedesc;
	" and won't let %youm% have it. ";
    }
    verDoFollow( actor ) =
    {
	"But "; self.thedesc; " is right here! ";
    }
    moveInto( obj ) =
    {
	if ( self.myfollower ) self.myfollower.moveInto( self.location );
	pass moveInto;
    }
    // these properties are for the format strings
    fmtYou = "he"
    fmtYour = "his"
    fmtYoure = "he's"
    fmtYoum = "him"
    fmtYouve = "he's"
    fmtS = "s"
    fmtEs = "es"
    fmtHave = "has"
    fmtDo = "does"
    fmtAre = "is"
    askWord(word, lst) = { return(nil); }
    verDoAskAbout(actor, iobj) = {}
    doAskAbout(actor, iobj) =
    {
	local lst, i, tot;

	lst := objwords(2);       // get actual words asked about
	tot := length(lst);
	if ((tot = 1 and (find(['it' 'them' 'him' 'her'], lst[1]) <> nil))
	    or tot = 0)
	{
	    "\"Could you be more specific?\"";
	    return;
	}

	// try to find a response for each word
	for (i := 1 ; i <= tot ; ++i)
	{
	    if (self.askWord(lst[i], lst))
		return;
	}

	// didn't find anything to talk about
	self.disavow;
    }
    disavow = "\"I don't know much about that.\""
    verIoPutIn(actor) =
    {
	"If you want to give that to << thedesc >>, just say so.";
    }
;

/*
 *  follower: Actor
 *
 *  This is a special object that can "shadow" the movements of a
 *  character as it moves from room to room.  The purpose of a follower
 *  is to allow the player to follow an actor as it leaves a room by
 *  typing a "follow" command.  Each actor that is to be followed must
 *  have its own follower object.  The follower object should
 *  define all of the same vocabulary words (nouns and adjectives) as the
 *  actual actor to which it refers.  The follower must also
 *  define the myactor property to be the Actor object that
 *  the follower follows.  The follower will always stay
 *  one room behind the character it follows; no commands are effective
 *  with a follower except for "follow."
 */
class follower: Actor
    sdesc = { self.myactor.sdesc; }
    isfollower = true
    ldesc = { caps(); self.thedesc; " is no longer here. "; }
    actorAction( v, d, p, i ) = { self.ldesc; exit; }
    actorDesc = {}
    myactor = nil   // set to the Actor to be followed
    verDoFollow( actor ) = {}
    doFollow( actor ) =
    {
	actor.travelTo( self.myactor.location );
    }
    dobjGen(a, v, i, p) =
    {
	if (v <> followVerb)
	{
	    "\^<< self.myactor.thedesc >> is no longer here.";
	    exit;
	}
    }
    iobjGen(a, v, d, p) =
    {
	"\^<< self.myactor.thedesc >> is no longer here.";
	exit;
    }
;

/*
 *  basicMe: Actor
 *
 *  A default implementation of the Me object, which is the
 *  player character.  adv.t defines basicMe instead of
 *  Me to allow your game to override parts of the default
 *  implementation while still using the rest, and without changing
 *  adv.t itself.  To use basicMe unchanged as your player
 *  character, include this in your game:  "Me: basicMe;".
 *  
 *  The basicMe object defines all of the methods and properties
 *  required for an actor, with appropriate values for the player
 *  character.  The nouns "me" and "myself'' are defined ("I''
 *  is not defined, because it conflicts with the "inventory"
 *  command's minimal abbreviation of "i" in certain circumstances,
 *  and is generally not compatible with the syntax of most player
 *  commands anyway).  The sdesc is "you"; the thedesc
 *  and adesc are "yourself," which is appropriate for most
 *  contexts.  The maxbulk and maxweight properties are
 *  set to 10 each; a more sophisticated Me might include the
 *  player's state of health in determining the maxweight and
 *  maxbulk properties.
 */
class basicMe: Actor, floatingItem
    roomCheck( v ) = { return( self.location.roomCheck( v )); }
    noun = 'me' 'myself'
    sdesc = "you"
    thedesc = "yourself"
    adesc = "yourself"
    ldesc = "You look about the same as always. "
    maxweight = 10
    maxbulk = 10
    verDoFollow( actor ) =
    {
	if ( actor = self ) "You can't follow yourself! ";
    }
    actorAction( verb, dobj, prep, iobj ) = 
    {
    }
    travelTo( room ) =
    {
	if ( room )
	{
	    if ( room.isobstacle )
	    {
		self.travelTo( room.destination );
	    }
	    else if ( not ( self.location.islit or room.islit ))
	    {
		darkTravel();
	    }
	    else
	    {
		if ( self.location ) self.location.leaveRoom( self );
		self.location := room;
		room.enterRoom( self );
	    }
	}
    }
    moveInto( room ) =
    {
	self.location := room;
    }
    // these properties are for the format strings
    fmtYou = "you"
    fmtYour = "your"
    fmtYoure = "you're"
    fmtYoum = "you"
    fmtYouve = "you've"
    fmtS = ""
    fmtEs = ""
    fmtHave = "have"
    fmtDo = "do"
    fmtAre = "are"
;

/*
 *  decoration: fixeditem
 *
 *  An item that doesn't have any function in the game, apart from
 *  having been mentioned in the room description.  These items
 *  are immovable and can't be manipulated in any way, but can be
 *  referred to and inspected.  Liberal use of decoration items
 *  can improve a game's playability by helping the parser recognize
 *  all the words the game uses in its descriptions of rooms.
 */
class decoration: fixeditem
    ldesc = "That's not important."
    dobjGen(a, v, i, p) =
    {
	if (v <> inspectVerb)
	{
	    "\^<<self.thedesc>> isn't important.";
	    exit;
	}
    }
    iobjGen(a, v, d, p) =
    {
	"\^<<self.thedesc>> isn't important.";
	exit;
    }
;

/*
 *  distantItem: fixeditem
 *
 *  This is an item that is too far away to manipulate, but can be seen.
 *  The class uses dobjGen and iobjGen to prevent any verbs from being
 *  used on the object apart from inspectVerb; using any other verb results
 *  in the message "It's too far away."  Instances of this class should
 *  provide the normal item properties:  sdesc, ldesc, location,
 *  and vocabulary.
 */
class distantItem: fixeditem
    dobjGen(a, v, i, p) =
    {
	if (v <> inspectVerb)
	{
	    "It's too far away.";
	    exit;
	}
    }
    iobjGen(a, v, d, p) = { self.dobjGen(a, v, d, p); }
;

/*
 *  buttonitem: fixeditem
 *
 *  A button (the type you push).  The individual button's action method
 *  doPush(actor), which must be specified in
 *  the button, carries out the function of the button.  Note that
 *  all buttons have the noun "button" defined.
 */
class buttonitem: fixeditem
    noun = 'button'
    plural = 'buttons'
    verDoPush( actor ) = {}
;

/*
 *  clothingItem: item
 *
 *  Something that can be worn.  By default, the only thing that
 *  happens when the item is worn is that its isworn property
 *  is set to true.  If you want more to happen, override the
 *  doWear(actor) property.  Note that, when a clothingItem
 *  is being worn, certain operations will cause it to be removed (for
 *  example, dropping it causes it to be removed).  If you want
 *  something else to happen, override the checkDrop method;
 *  if you want to disallow such actions while the object is worn,
 *  use an exit statement in the checkDrop method.
 */
class clothingItem: item
    checkDrop =
    {
	if ( self.isworn )
	{
	    "(Taking off "; self.thedesc; " first)\n";
	    self.isworn := nil;
	}
    }
    doDrop( actor ) =
    {
	self.checkDrop;
	pass doDrop;
    }
    doPutIn( actor, io ) =
    {
	self.checkDrop;
	pass doPutIn;
    }
    doPutOn( actor, io ) =
    {
	self.checkDrop;
	pass doPutOn;
    }
    doGiveTo( actor, io ) =
    {
	self.checkDrop;
	pass doGiveTo;
    }
    doThrowAt( actor, io ) =
    {
	self.checkDrop;
	pass doThrowAt;
    }
    doThrowTo( actor, io ) =
    {
	self.checkDrop;
	pass doThrowTo;
    }
    doThrow( actor ) =
    {
	self.checkDrop;
	pass doThrow;
    }
    moveInto( obj ) =
    {
	/*
	 *   Catch any other movements with moveInto; this won't stop the
	 *   movement from happening, but it will prevent any anamolous
	 *   consequences caused by the object moving but still being worn.
	 */
	self.isworn := nil;
	pass moveInto;
    }
    verDoWear( actor ) =
    {
	if ( self.isworn )
	{
	    "%You're% already wearing "; self.thedesc; "! ";
	}
	else if ( not actor.isCarrying( self ))
	{
	    "%You% %do%n't have "; self.thedesc; ". ";
	}
    }
    doWear( actor ) =
    {
	"Okay, %you're% now wearing "; self.thedesc; ". ";
	self.isworn := true;
    }
    verDoUnwear( actor ) =
    {
	if ( not self.isworn )
	{
	    "%You're% not wearing "; self.thedesc; ". ";
	}
    }
    verDoTake(actor) =
    {
	if (self.isworn) self.verDoUnwear(actor);
	else pass verDoTake;
    }
    doTake(actor) =
    {
	if (self.isworn) self.doUnwear(actor);
	else pass doTake;
    }
    doUnwear( actor ) =
    {
	"Okay, %you're% no longer wearing "; self.thedesc; ". ";
	self.isworn := nil;
    }
    doSynonym('Unwear') = 'Unboard'
;

/*
 *  obstacle: object
 *
 *  An obstacle is used in place of a room for a direction
 *  property.  The destination property specifies the room that
 *  is reached if the obstacle is successfully negotiated; when the
 *  obstacle is not successfully negotiated, destination should
 *  display an appropriate message and return nil.
 */
class obstacle: object
    isobstacle = true
;

/*
 *  doorway: fixeditem, obstacle
 *
 *  A doorway is an obstacle that impedes progress when it is closed.
 *  When the door is open (isopen is true), the user ends up in
 *  the room specified in the doordest property upon going through
 *  the door.  Since a doorway is an obstacle, use the door object for
 *  a direction property of the room containing the door.
 *  
 *  If noAutoOpen is not set to true, the door will automatically
 *  be opened when the player tries to walk through the door, unless the
 *  door is locked (islocked = true).  If the door is locked,
 *  it can be unlocked simply by typing "unlock door", unless the
 *  mykey property is set, in which case the object specified in
 *  mykey must be used to unlock the door.  Note that the door can
 *  only be relocked by the player under the circumstances that allow
 *  unlocking, plus the property islockable must be set to true.
 *  By default, the door is closed; set isopen to true if the door
 *  is to start out open (and be sure to open the other side as well).
 *  
 *  otherside specifies the corresponding doorway object in the
 *  destination room (doordest), if any.  If otherside is
 *  specified, its isopen and islocked properties will be
 *  kept in sync automatically.
 */
class doorway: fixeditem, obstacle
    isdoor = true           // Item can be opened and closed
    destination =
    {
	if ( self.isopen ) return( self.doordest );
	else if ( not self.islocked and not self.noAutoOpen )
	{
	    self.isopen := true;
	    if ( self.otherside ) self.otherside.isopen := true;
	    "(Opening << self.thedesc >>)\n";
	    return( self.doordest );
	}
	else
	{
	    "%You%'ll have to open << self.thedesc >> first. ";
	    setit( self );
	    return( nil );
	}
    }
    verDoOpen( actor ) =
    {
	if ( self.isopen ) "It's already open. ";
	else if ( self.islocked ) "It's locked. ";
    }
    doOpen( actor ) =
    {
	"Opened. ";
	self.isopen := true;
	if ( self.otherside ) self.otherside.isopen := true;
    }
    verDoClose( actor ) =
    {
	if ( not self.isopen ) "It's already closed. ";
    }
    doClose( actor ) =
    {
	"Closed. ";
	self.isopen := nil;
	if ( self.otherside ) self.otherside.isopen := nil;
    }
    verDoLock( actor ) =
    {
	if ( self.islocked ) "It's already locked! ";
	else if ( not self.islockable ) "It can't be locked. ";
	else if ( self.isopen ) "%You%'ll have to close it first. ";
    }
    doLock( actor ) =
    {
	if ( self.mykey = nil )
	{
	    "Locked. ";
	    self.islocked := true;
	    if ( self.otherside ) self.otherside.islocked := true;
	}
	else
	    askio( withPrep );
    }
    verDoUnlock( actor ) =
    {
	if ( not self.islocked ) "It's not locked! ";
    }
    doUnlock( actor ) =
    {
	if ( self.mykey = nil )
	{
	    "Unlocked. ";
	    self.islocked := nil;
	    if ( self.otherside ) self.otherside.islocked := nil;
	}
	else
	    askio( withPrep );
    }
    verDoLockWith( actor, io ) =
    {
	if ( self.islocked ) "It's already locked. ";
	else if ( not self.islockable ) "It can't be locked. ";
	else if ( self.mykey = nil )
	    "%You% %do%n't need anything to lock it. ";
	else if ( self.isopen ) "%You%'ll have to close it first. ";
    }
    doLockWith( actor, io ) =
    {
	if ( io = self.mykey )
	{
	    "Locked. ";
	    self.islocked := true;
	    if ( self.otherside ) self.otherside.islocked := true;
	}
	else "It doesn't fit the lock. ";
    }
    verDoUnlockWith( actor, io ) =
    {
	if ( not self.islocked ) "It's not locked! ";
	else if ( self.mykey = nil )
	    "%You% %do%n't need anything to unlock it. ";
    }
    doUnlockWith( actor, io ) =
    {
	if ( io = self.mykey )
	{
	    "Unlocked. ";
	    self.islocked := nil;
	    if ( self.otherside ) self.otherside.islocked := nil;
	}
	else "It doesn't fit the lock. ";
    }
    ldesc =
    {
	if ( self.isopen ) "It's open. ";
	else
	{
	    if ( self.islocked ) "It's closed and locked. ";
	    else "It's closed. ";
	}
    }
;

/*
 *  lockableDoorway: doorway
 *
 *  This is just a normal doorway with the islockable and
 *  islocked properties set to true.  Fill in the other
 *  properties (otherside and doordest) as usual.  If
 *  the door has a key, set property mykey to the key object.
 */
class lockableDoorway: doorway
    islockable = true
    islocked = true
;

/*
 *  vehicle: item, nestedroom
 *
 *  This is an object that an actor can board.  An actor cannot go
 *  anywhere while on board a vehicle (except where the vehicle goes);
 *  the actor must get out first.
 */
class vehicle: item, nestedroom
    reachable = ([] + self)
    isvehicle = true
    verDoEnter( actor ) = { self.verDoBoard( actor ); }
    doEnter( actor ) = { self.doBoard( actor ); }
    verDoBoard( actor ) =
    {
	if ( actor.location = self )
	{
	    "%You're% already in "; self.thedesc; "! ";
	}
    }
    doBoard( actor ) =
    {
	"Okay, %you're% now in "; self.thedesc; ". ";
	actor.moveInto( self );
    }
    noexit =
    {
	"%You're% not going anywhere until %you% get%s% out of ";
	  self.thedesc; ". ";
	return( nil );
    }
    out = ( self.location )
    verDoTake(actor) =
    {
	if (actor.isIn(self))
	    "You'll have to get <<self.outOfPrep>> <<self.thedesc>> first.";
	else
	    pass verDoTake;
    }
    dobjGen(a, v, i, p) =
    {
	if (a.isIn(self) and v <> inspectVerb and v <> getOutVerb
	    and v <> outVerb)
	{
	    "You'll have to get out of << thedesc >> first.";
	    exit;
	}
    }
    iobjGen(a, v, d, p) =
    {
	if (a.isIn(self) and v <> putVerb)
	{
	    "You'll have to get out of << thedesc >> first.";
	    exit;
	}
    }
;

/*
 *  surface: item
 *
 *  Objects can be placed on a surface.  Apart from using the
 *  preposition "on" rather than "in'' to refer to objects
 *  contained by the object, a surface is identical to a
 *  container.  Note: an object cannot be both a
 *  surface and a container, because there is no
 *  distinction between the two internally.
 */
class surface: item
    issurface = true        // Item can hold objects on its surface
    ldesc =
    {
	if (itemcnt( self.contents ))
	{
	    "On "; self.thedesc; " %you% see%s% "; listcont( self ); ". ";
	}
	else
	{
	    "There's nothing on "; self.thedesc; ". ";
	}
    }
    verIoPutOn( actor ) = {}
    ioPutOn( actor, dobj ) =
    {
	dobj.doPutOn( actor, self );
    }
;

/*
 *  container: item
 *
 *  This object can contain other objects.  The iscontainer property
 *  is set to true.  The default ldesc displays a list of the
 *  objects inside the container, if any.  The maxbulk property
 *  specifies the maximum amount of bulk the container can contain.
 */
class container: item
    maxbulk = 10            // maximum bulk the container can contain
    isopen = true           // in fact, it can't be closed at all
    iscontainer = true      // Item can contain other items
    ldesc =
    {
	if ( self.contentsVisible and itemcnt( self.contents ) <> 0 )
	{
	    "In "; self.thedesc; " %you% see%s% "; listcont( self ); ". ";
	}
	else
	{
	    "There's nothing in "; self.thedesc; ". ";
	}
    }
    verIoPutIn( actor ) =
    {
    }
    ioPutIn( actor, dobj ) =
    {
	if (addbulk( self.contents ) + dobj.bulk > self.maxbulk )
	{
	    "%You% can't fit that in "; self.thedesc; ". ";
	}
	else
	{
	    dobj.doPutIn( actor, self );
	}
    }
    verDoLookin( actor ) = {}
    doLookin( actor ) =
    {
	self.ldesc;
    }
;

/*
 *  openable: container
 *
 *  A container that can be opened and closed.  The isopenable
 *  property is set to true.  The default ldesc displays
 *  the contents of the container if the container is open, otherwise
 *  a message saying that the object is closed.
 */
class openable: container
    contentsReachable = { return( self.isopen ); }
    contentsVisible = { return( self.isopen ); }
    isopenable = true
    ldesc =
    {
	caps(); self.thedesc;
	if ( self.isopen )
	{
	    " is open. ";
	    pass ldesc;
	}
	else " is closed. ";
    }
    isopen = true
    verDoOpen( actor ) =
    {
	if ( self.isopen )
	{
	    caps(); self.thedesc; " is already open! ";
	}
    }
    doOpen( actor ) =
    {
	if (itemcnt( self.contents ))
	{
	    "Opening "; self.thedesc; " reveals "; listcont( self ); ". ";
	}
	else "Opened. ";
	self.isopen := true;
    }
    verDoClose( actor ) =
    {
	if ( not self.isopen )
	{
	    caps(); self.thedesc; " is already closed! ";
	}
    }
    doClose( actor ) =
    {
	"Closed. ";
	self.isopen := nil;
    }
    verIoPutIn( actor ) =
    {
	if ( not self.isopen )
	{
	    caps(); self.thedesc; " is closed. ";
	}
    }
    verDoLookin( actor ) =
    {
	if ( not self.isopen ) "It's closed. ";
    }
;

/*
 *  qcontainer: container
 *
 *  A "quiet" container:  its contents are not listed when it shows
 *  up in a room description or inventory list.  The isqcontainer
 *  property is set to true.
 */
class qcontainer: container
    isqcontainer = true
;

/*
 *  lockable: openable
 *
 *  A container that can be locked and unlocked.  The islocked
 *  property specifies whether the object can be opened or not.  The
 *  object can be locked and unlocked without the need for any other
 *  object; if you want a key to be involved, use a keyedLockable.
 */
class lockable: openable
    verDoOpen( actor ) =
    {
	if ( self.islocked )
	{
	    "It's locked. ";
	}
	else pass verDoOpen;
    }
    verDoLock( actor ) =
    {
	if ( self.islocked )
	{
	    "It's already locked! ";
	}
    }
    doLock( actor ) =
    {
	if ( self.isopen )
	{
	    "%You%'ll have to close "; self.thedesc; " first. ";
	}
	else
	{
	    "Locked. ";
	    self.islocked := true;
	}
    }
    verDoUnlock( actor ) =
    {
	if ( not self.islocked ) "It's not locked! ";
    }
    doUnlock( actor ) =
    {
	"Unlocked. ";
	self.islocked := nil;
    }
    verDoLockWith( actor, io ) =
    {
	if ( self.islocked ) "It's already locked. ";
    }
    verDoUnlockWith( actor, io ) =
    {
	if ( not self.islocked ) "It's not locked! ";
    }
;

/*
 *  keyedLockable: lockable
 *
 *  This subclass of lockable allows you to create an object
 *  that can only be locked and unlocked with a corresponding key.
 *  Set the property mykey to the keyItem object that can
 *  lock and unlock the object.
 */
class keyedLockable: lockable
    mykey = nil     // set 'mykey' to the key which locks/unlocks me
    doLock( actor ) =
    {
	askio( withPrep );
    }
    doUnlock( actor ) =
    {
	askio( withPrep );
    }
    doLockWith( actor, io ) =
    {
	if ( self.isopen )
	{
	    "%You% can't lock << self.thedesc >> when it's open. ";
	}
	else if ( io = self.mykey )
	{
	    "Locked. ";
	    self.islocked := true;
	}
	else "It doesn't fit the lock. ";
    }
    doUnlockWith( actor, io ) =
    {
	if ( io = self.mykey )
	{
	    "Unlocked. ";
	    self.islocked := nil;
	}
	else "It doesn't fit the lock. ";
    }
;

/*
 *  keyItem: item
 *
 *  This is an object that can be used as a key for a keyedLockable
 *  or lockableDoorway object.  It otherwise behaves as an ordinary item.
 */
class keyItem: item
    verIoUnlockWith( actor ) = {}
    ioUnlockWith( actor, dobj ) =
    {
	dobj.doUnlockWith( actor, self );
    }
    verIoLockWith( actor ) = {}
    ioLockWith( actor, dobj ) =
    {
	dobj.doLockWith( actor, self );
    }
;

/*
 *  transparentItem: item
 *
 *  An object whose contents are visible, even when the object is
 *  closed.  Whether the contents are reachable is decided in the
 *  normal fashion.  This class is useful for items such as glass
 *  bottles, whose contents can be seen when the bottle is closed
 *  but cannot be reached.
 */
class transparentItem: item
    contentsVisible = { return( true ); }
    verGrab( obj ) =
    {
	if ( self.isopenable and not self.isopen )
	    "%You% will have to open << self.thedesc >> first. ";
    }
    doOpen( actor ) =
    {
	self.isopen := true;
	"Opened. ";
    }
;

/*
 *  basicNumObj: object
 *
 *  This object provides a default implementation for numObj.
 *  To use this default unchanged in your game, include in your
 *  game this line:  "numObj: basicNumObj".
 */
class basicNumObj: object   // when a number is used in a player command,
    value = 0               //  this is set to its value
    sdesc = "that"
    adesc = "that"
    thedesc = "that"
    verDoTypeOn( actor, io ) = {}
    doTypeOn( actor, io ) = { "\"Tap, tap, tap, tap...\" "; }
    verIoTurnTo( actor ) = {}
    ioTurnTo( actor, dobj ) = { dobj.doTurnTo( actor, self ); }
;

/*
 *  basicStrObj: object
 *
 *  This object provides a default implementation for strObj.
 *  To use this default unchanged in your game, include in your
 *  game this line:  "strObj: basicStrObj".
 */
class basicStrObj: object   // when a string is used in a player command,
    value = ''              //  this is set to its value
    sdesc = "that"
    adesc = "that"
    thedesc = "that"
    verDoTypeOn( actor, io ) = {}
    doTypeOn( actor, io ) = { "\"Tap, tap, tap, tap...\" "; }
    doSynonym('TypeOn') = 'EnterOn' 'EnterIn' 'EnterWith'
    verDoSave( actor ) = {}
    doSave( actor ) =
    {
	if (save( self.value ))
	    "Save failed. ";
	else
	    "Saved. ";
	abort;
    }
    verDoRestore( actor ) = {}
    doRestore( actor ) =
    {
	if (restore( self.value ))
	    "Restore failed. ";
	else
	{
	    "Restored.\b";
	    scoreStatus( global.score, global.turnsofar );
	    Me.location.lookAround(true);
	}
	abort;
    }
    verDoScript( actor ) = {}
    doScript( actor ) =
    {
	logging( self.value );
	"Writing script file. ";
	abort;
    }
    verDoSay( actor ) = {}
    doSay( actor ) =
    {
	"Okay, \""; say( self.value ); "\".";
    }
;

/*
 *  deepverb: object
 *
 *  A "verb object" that is referenced by the parser when the player
 *  uses an associated vocabulary word.  A deepverb contains both
 *  the vocabulary of the verb and a description of available syntax.
 *  The verb property lists the verb vocabulary words;
 *  one word (such as 'take') or a pair (such as 'pick up')
 *  can be used.  In the latter case, the second word must be a
 *  preposition, and may move to the end of the sentence in a player's
 *  command, as in "pick it up."  The action(actor)
 *  method specifies what happens when the verb is used without any
 *  objects; its absence specifies that the verb cannot be used without
 *  an object.  The doAction specifies the root of the message
 *  names (in single quotes) sent to the direct object when the verb
 *  is used with a direct object; its absence means that a single object
 *  is not allowed.  Likewise, the ioAction(preposition)
 *  specifies the root of the message name sent to the direct and
 *  indirect objects when the verb is used with both a direct and
 *  indirect object; its absence means that this syntax is illegal.
 *  Several ioAction properties may be present:  one for each
 *  preposition that can be used with an indirect object with the verb.
 *  
 *  The validDo(actor, object, seqno) method returns true
 *  if the indicated object is valid as a direct object for this actor.
 *  The validIo(actor, object, seqno) method does likewise
 *  for indirect objects.  The seqno parameter is a "sequence
 *  number," starting with 1 for the first object tried for a given
 *  verb, 2 for the second, and so forth; this parameter is normally
 *  ignored, but can be used for some special purposes.  For example,
 *  askVerb does not distinguish between objects matching vocabulary
 *  words, and therefore accepts only the first from a set of ambiguous
 *  objects.  These methods do not normally need to be changed; by
 *  default, they return true if the object is accessible to the
 *  actor.
 *  
 *  The doDefault(actor, prep, indirectObject) and
 *  ioDefault(actor, prep) methods return a list of the
 *  default direct and indirect objects, respectively.  These lists
 *  are used for determining which objects are meant by "all" and which
 *  should be used when the player command is missing an object.  These
 *  normally return a list of all objects that are applicable to the
 *  current command.
 *  
 *  The validDoList(actor, prep, indirectObject) and
 *  validIoList(actor, prep, directObject) methods return
 *  a list of all of the objects for which validDo would be true.
 *  Remember to include floating objects, which are generally
 *  accessible.  Note that the objects returned by this list will
 *  still be submitted by the parser to validDo, so it's okay for
 *  this routine to return too many objects.  In fact, this
 *  routine is entirely unnecessary; if you omit it altogether, or
 *  make it return nil, the parser will simply submit every
 *  object matching the player's vocabulary words to validDo.
 *  The reason to provide this method is that it can significantly
 *  improve parsing speed when the game has lots of objects that
 *  all have the same vocabulary word, because it cuts down on the
 *  number of times that validDo has to be called (each call
 *  to validDo is fairly time-consuming).
 */
class deepverb: object                // A deep-structure verb.
    validDo( actor, obj, seqno ) =
    {
	return( obj.isReachable( actor ));
    }
    validDoList(actor, prep, iobj) =
    {
	local ret;
	local loc;

	loc := actor.location;
	while (loc.location) loc := loc.location;
	ret := visibleList(actor) + visibleList(loc)
	       + global.floatingList;
	return(ret);
    }
    invalidObj( actor, obj, name ) =
    {
	if ( not obj.isVisible( actor ))
	    "I don't see any << name >> here. ";
	else
	    "%You% can't do that with << obj.thedesc >>. ";
    }
    validIo( actor, obj, seqno ) =
    {
	return( obj.isReachable( actor ));
    }
    validIoList(actor, prep, dobj) = (self.validDoList(actor, prep, dobj))
    doDefault( actor, prep, io ) =
    {
	return( actor.contents + actor.location.contents );
    }
    ioDefault( actor, prep ) =
    {
	return( actor.contents + actor.location.contents );
    }
;

/*
 *   Various verbs.
 */
inspectVerb: deepverb
    verb = 'inspect' 'examine' 'look at' 'l at' 'x'
    sdesc = "inspect"
    doAction = 'Inspect'
    validDo( actor, obj, seqno ) =
    {
	return( obj.isVisible( actor ));
    }
;
askVerb: deepverb
    verb = 'ask'
    sdesc = "ask"
    prepDefault = aboutPrep
    ioAction( aboutPrep ) = 'AskAbout'
    validIo( actor, obj, seqno ) = { return( seqno = 1 ); }
    validIoList = nil
;
tellVerb: deepverb
    verb = 'tell'
    sdesc = "tell"
    prepDefault = aboutPrep
    ioAction( aboutPrep ) = 'TellAbout'
    validIo( actor, obj, seqno ) = { return( seqno = 1 ); }
    validIoList(actor, prep, dobj) = (nil)
    ioDefault( actor, prep ) =
    {
	if (prep = aboutPrep)
	    return([]);
	else
	    return(actor.contents + actor.location.contents);
    }
;
followVerb: deepverb
    sdesc = "follow"
    verb = 'follow'
    doAction = 'Follow'
;
digVerb: deepverb
    verb = 'dig' 'dig in'
    sdesc = "dig in"
    prepDefault = withPrep
    ioAction( withPrep ) = 'DigWith'
;
jumpVerb: deepverb
    verb = 'jump' 'jump over' 'jump off'
    sdesc = "jump"
    doAction = 'Jump'
    action(actor) = { "Wheeee!"; }
;
pushVerb: deepverb
    verb = 'push' 'press'
    sdesc = "push"
    doAction = 'Push'
;
attachVerb: deepverb
    verb = 'attach' 'connect'
    sdesc = "attach"
    prepDefault = toPrep
    ioAction( toPrep ) = 'AttachTo'
;
wearVerb: deepverb
    verb = 'wear' 'put on'
    sdesc = "wear"
    doAction = 'Wear'
;
dropVerb: deepverb
    verb = 'drop' 'put down'
    sdesc = "drop"
    ioAction( onPrep ) = 'PutOn'
    doAction = 'Drop'
    doDefault( actor, prep, io ) =
    {
	return( actor.contents );
    }
;
removeVerb: deepverb
    verb = 'take off'
    sdesc = "take off"
    doAction = 'Unwear'
    ioAction( fromPrep ) = 'RemoveFrom'
;
openVerb: deepverb
    verb = 'open'
    sdesc = "open"
    doAction = 'Open'
;
closeVerb: deepverb
    verb = 'close'
    sdesc = "close"
    doAction = 'Close'
;
putVerb: deepverb
    verb = 'put' 'place'
    sdesc = "put"
    prepDefault = inPrep
    ioAction( inPrep ) = 'PutIn'
    ioAction( onPrep ) = 'PutOn'
    doDefault( actor, prep, io ) =
    {
	return( takeVerb.doDefault( actor, prep, io ) + actor.contents );
    }
;
takeVerb: deepverb                   // This object defines how to take things
    verb = 'take' 'pick up' 'get' 'remove'
    sdesc = "take"
    ioAction( offPrep ) = 'TakeOff'
    ioAction( outPrep ) = 'TakeOut'
    ioAction( fromPrep ) = 'TakeOut'
    ioAction( inPrep ) = 'TakeOut'
    ioAction( onPrep ) = 'TakeOff'
    doAction = 'Take'
    doDefault( actor, prep, io ) =
    {
	local ret, rem, cur, rem2, cur2, tot, i, tot2, j;
	
	ret := [];
	
	/*
	 *   For "take all out/off of <iobj>", return the (non-fixed)
	 *   contents of the indirect object.  Same goes for "take all in
	 *   <iobj>", "take all on <iobj>", and "take all from <iobj>".
	 */
	if (( prep=outPrep or prep=offPrep or prep=inPrep or prep=onPrep
	 or prep=fromPrep ) and io<>nil )
	{
	    rem := io.contents;
	    i := 1;
	    tot := length( rem );
	    while ( i <= tot )
	    {
		cur := rem[i];
		if (not cur.isfixed and self.validDo(actor, cur, i))
		    ret += cur;
		++i;
	    }
	    return( ret );
	}

	/*
	 *   In the general case, return everything that's not fixed
	 *   in the actor's location, or everything inside fixed containers
	 *   that isn't itself fixed.
	 */
	rem := actor.location.contents;
	tot := length( rem );
	i := 1;
	while ( i <= tot )
	{
	    cur := rem[i];
	    if ( cur.isfixed )
	    {
		if ((( cur.isopenable and cur.isopen ) or
		  ( not cur.isopenable )) and ( not cur.isactor ))
		{
		    rem2 := cur.contents;
		    tot2 := length( rem2 );
		    j := 1;
		    while ( j <= tot2 )
		    {
			cur2 := rem2[j];
			if ( not cur2.isfixed and not cur2.notakeall )
			{
			    ret := ret + cur2;
			}
			j := j + 1;
		    }
		}
	    }
	    else if ( not cur.notakeall )
	    {
		ret := ret + cur;
	    }

	    i := i + 1;            
	}
	return( ret );
    }
;
plugVerb: deepverb
    verb = 'plug'
    sdesc = "plug"
    prepDefault = inPrep
    ioAction( inPrep ) = 'PlugIn'
;
lookInVerb: deepverb
    verb = 'look in' 'look on' 'l in' 'l on'
    sdesc = "look in"
    doAction = 'Lookin'
;
screwVerb: deepverb
    verb = 'screw'
    sdesc = "screw"
    ioAction( withPrep ) = 'ScrewWith'
    doAction = 'Screw'
;
unscrewVerb: deepverb
    verb = 'unscrew'
    sdesc = "unscrew"
    ioAction( withPrep ) = 'UnscrewWith'
    doAction = 'Unscrew'
;
turnVerb: deepverb
    verb = 'turn' 'rotate' 'twist'
    sdesc = "turn"
    ioAction( toPrep ) = 'TurnTo'
    ioAction( withPrep ) = 'TurnWith'
    doAction = 'Turn'
;
switchVerb: deepverb
    verb = 'switch' 'flick'
    sdesc = "switch"
    doAction = 'Switch'
;
flipVerb: deepverb
    verb = 'flip'
    sdesc = "flip"
    doAction = 'Flip'
;
turnOnVerb: deepverb
    verb = 'activate' 'turn on' 'switch on'
    sdesc = "turn on"
    doAction = 'Turnon'
;
turnOffVerb: deepverb
    verb = 'turn off' 'deactiv' 'switch off'
    sdesc = "turn off"
    doAction = 'Turnoff'
;
lookVerb: deepverb
    verb = 'look' 'l' 'look around' 'l around'
    action( actor ) =
    {
	actor.location.lookAround( true );
    }
;
sitVerb: deepverb
    verb = 'sit on' 'sit in' 'sit' 'sit down' 'sit downin' 'sit downon'
    sdesc = "sit on"
    doAction = 'Siton'
;
lieVerb: deepverb
    verb = 'lie' 'lie on' 'lie in' 'lie down' 'lie downon' 'lie downin'
    sdesc = "lie on"
    doAction = 'Lieon'
;
getOutVerb: deepverb
    verb = 'get out' 'get outof' 'get off' 'get offof'
    sdesc = "get out of"
    doAction = 'Unboard'
    action(actor) = { askdo; }
    doDefault( actor, prep, io ) =
    {
	if ( actor.location and actor.location.location )
	    return( [] + actor.location );
	else return( [] );
    }
;
boardVerb: deepverb
    verb = 'get in' 'get into' 'board' 'get on'
    sdesc = "get on"
    doAction = 'Board'
;
againVerb: deepverb         // Required verb:  repeats last command.  No
			    // action routines are necessary; this one's
			    // handled internally by the parser.
    verb = 'again' 'g'
;
waitVerb: deepverb
    verb = 'wait' 'z'
    action( actor ) =
    {
	"Time passes...\n";
    }
;
iVerb: deepverb
    verb = 'inventory' 'i'
    action( actor ) =
    {
	if (length( actor.contents ))
	{
	    "%You% %have% "; listcont( actor ); ". ";
	    listcontcont( actor );
	}
	else
	    "%You% %are% empty-handed.\n";
    }
;
lookThruVerb: deepverb
    verb = 'look through' 'look thru' 'l through' 'l thru'
    sdesc = "look through"
    doAction = 'Lookthru'
;
attackVerb: deepverb
    verb = 'attack' 'kill' 'hit' 'shoot' 'punch' 'throttle' 'bludgeon' 
	   'cut' 'saw' 'carve' 'slice' 'chop' 'butcher'
    sdesc = "attack"
    prepDefault = withPrep
    action( actor ) =
    {   local lim, i, olist, present;

	present := [];
	olist := Me.location.contents;

	lim := length(olist);
	i := 1;
	while ( i <= lim )              //search room for barrels & enemies
	{
	   if (( isclass(olist[i], enemy)) or ( isclass(olist[i], barrel)))
	   {
	       present += i;
	   }
	   i++;
	}
	if ( not( (isclass( Me.usedGun, fists)) or 
		  (isclass( Me.usedGun, chainSaw)) ) )
	  " You open fire on the room. Sparks fly off the walls and the smell
	  of chordite fills your nostrils. ";
	else if (isclass( Me.usedGun, fists))
	  " You flail your fists around in the air. ";
	else
	  " You wave your chain saw around. ";
	if ( length(present) > 0 )
	{  local lim, i, j, no, chosen; //if there are some, randomly attack
					//a random number of them.
	   chosen := [];                   //chosen is a list of booleans
	   lim := length(present);         //the same length (theoretically)
	   i := 1;                         //as present
	   while (i<=lim)
	   {
	      chosen += nil; i++;    //clear chosen
	   }
	   no := rand(lim); i := 1;
	   while (i <= no)              
	   {
	      do
		 j := rand(lim); 
	      while ( chosen[j] )       //if not chosen, use it
	      chosen[j] := true;
	      if (olist[present[j]].location) //if not now dead, attack it
	      {   
		 "\n<<olist[present[j]].thedesc>> : ";
		 olist[present[j]].doAttack( actor );
	      }
	      i++;
	   }
	}
	else
	{
	   incammo( Me.usedGun.ammotype, -(Me.usedGun.fireRate*3)/2 );
	   Me.usedGun.verIoAttackWith( actor );
	   " Fortunately, you don't hurt yourself";
	   if ( not( (isclass( Me.usedGun, fists)) or 
			     (isclass( Me.usedGun, chainSaw)) ) )
	      ", but you wasted some ammunition. \n";
	   else ".\n";
	}
    }
    doAction = 'Attack'
    ioAction( withPrep ) = 'AttackWith'
    ioDefault( actor, prep ) =
    {
	return( Me.usedGun );
    }                         
    doDefault( actor, prep, io ) =
    {
	return( takeVerb.doDefault( actor, prep, io ) );
    }
;
climbVerb: deepverb
    verb = 'climb'
    sdesc = "climb"
    doAction = 'Climb'
;
eatVerb: deepverb
    verb = 'eat' 'consume'
    sdesc = "eat"
    doAction = 'Eat'
;
drinkVerb: deepverb
    verb = 'drink'
    sdesc = "drink"
    doAction = 'Drink'
;
giveVerb: deepverb
    verb = 'give' 'offer'
    sdesc = "give"
    prepDefault = toPrep
    ioAction( toPrep ) = 'GiveTo'
    doDefault( actor, prep, io ) =
    {
	return( actor.contents );
    }
;
pullVerb: deepverb
    verb = 'pull'
    sdesc = "pull"
    doAction = 'Pull'
;
readVerb: deepverb
    verb = 'read'
    sdesc = "read"
    doAction = 'Read'
;
throwVerb: deepverb
    verb = 'throw' 'toss'
    sdesc = "throw"
    prepDefault = atPrep
    ioAction( atPrep ) = 'ThrowAt'
    ioAction( toPrep ) = 'ThrowTo'
;
standOnVerb: deepverb
    verb = 'stand on'
    sdesc = "stand on"
    doAction = 'Standon'
;
standVerb: deepverb
    verb = 'stand' 'stand up' 'get up'
    sdesc = "stand"
    action( actor ) =
    {
	if ( actor.location=nil or actor.location.location = nil )
	    "%You're% already standing! ";
	else
	{
	    actor.location.doUnboard( actor );
	}
    }
;
helloVerb: deepverb
    verb = 'hello' 'hi' 'greetings'
    action( actor ) =
    {
	"Nice weather we've been having.\n";
    }
;
showVerb: deepverb
    verb = 'show'
    sdesc = "show"
    prepDefault = toPrep
    ioAction( toPrep ) = 'ShowTo'
    doDefault( actor, prep, io ) =
    {
	return( actor.contents );
    }
;
cleanVerb: deepverb
    verb = 'clean'
    sdesc = "clean"
    ioAction( withPrep ) = 'CleanWith'
    doAction = 'Clean'
;
sayVerb: deepverb
    verb = 'say'
    sdesc = "say"
    doAction = 'Say'
;
yellVerb: deepverb
    verb = 'yell' 'shout' 'yell at' 'shout at'
    action( actor ) =
    {
	"%Your% throat is a bit sore now. ";
    }
;
moveVerb: deepverb
    verb = 'move'
    sdesc = "move"
    ioAction( withPrep ) = 'MoveWith'
    ioAction( toPrep ) = 'MoveTo'
    doAction = 'Move'
;
fastenVerb: deepverb
    verb = 'fasten' 'buckle' 'buckle up'
    sdesc = "fasten"
    doAction = 'Fasten'
;
unfastenVerb: deepverb
    verb = 'unfasten' 'unbuckle'
    sdesc = "unfasten"
    doAction = 'Unfasten'
;
unplugVerb: deepverb
    verb = 'unplug'
    sdesc = "unplug"
    ioAction( fromPrep ) = 'UnplugFrom'
    doAction = 'Unplug'
;
lookUnderVerb: deepverb
    verb = 'look under' 'look beneath' 'l under' 'l beneath'
    sdesc = "look under"
    doAction = 'Lookunder'
;
lookBehindVerb: deepverb
    verb = 'look behind' 'l behind'
    sdesc = "look behind"
    doAction = 'Lookbehind'
;
typeVerb: deepverb
    verb = 'type'
    sdesc = "type"
    prepDefault = onPrep
    ioAction( onPrep ) = 'TypeOn'
;
lockVerb: deepverb
    verb = 'lock'
    sdesc = "lock"
    ioAction( withPrep ) = 'LockWith'
    doAction = 'Lock'
    prepDefault = withPrep
;
unlockVerb: deepverb
    verb = 'unlock'
    sdesc = "unlock"
    ioAction( withPrep ) = 'UnlockWith'
    doAction = 'Unlock'
    prepDefault = withPrep
;
detachVerb: deepverb
    verb = 'detach' 'disconnect'
    prepDefault = fromPrep
    ioAction( fromPrep ) = 'DetachFrom'
    doAction = 'Detach'
    sdesc = "detach"
;
sleepVerb: deepverb
    action( actor ) =
    {
	if ( actor.cantSleep )
	    "%You% %are% much too anxious worrying about %your% continued
	    survival to fall asleep now. ";
	else if ( global.awakeTime+1 < global.sleepTime )
	    "%You're% not tired. ";
	else if ( not ( actor.location.isbed or actor.location.ischair ))
	    "I don't know about you, but I can never sleep
	    standing up. %You% should find a nice comfortable
	    bed somewhere. ";
	else
	{
	    "%You% quickly drift%s% off into dreamland...\b";
	    goToSleep();
	}
    }
    verb = 'sleep'
;
pokeVerb: deepverb
    verb = 'poke' 'jab'
    sdesc = "poke"
    doAction = 'Poke'
;
touchVerb: deepverb
    verb = 'touch'
    sdesc = "touch"
    doAction = 'Touch'
;
moveNVerb: deepverb
    verb = 'move north' 'move n' 'push north' 'push n'
    sdesc = "move north"
    doAction = 'MoveN'
;
moveSVerb: deepverb
    verb = 'move south' 'move s' 'push south' 'push s'
    sdesc = "move south"
    doAction = 'MoveS'
;
moveEVerb: deepverb
    verb = 'move east' 'move e' 'push east' 'push e'
    sdesc = "move east"
    doAction = 'MoveE'
;
moveWVerb: deepverb
    verb = 'move west' 'move w' 'push west' 'push w'
    sdesc = "move west"
    doAction = 'MoveW'
;
moveNEVerb: deepverb
    verb = 'move northeast' 'move ne' 'push northeast' 'push ne'
    sdesc = "move northeast"
    doAction = 'MoveNE'
;
moveNWVerb: deepverb
    verb = 'move northwest' 'move nw' 'push northwest' 'push nw'
    sdesc = "move northwest"
    doAction = 'MoveNW'
;
moveSEVerb: deepverb
    verb = 'move southeast' 'move se' 'push southeast' 'push se'
    sdesc = "move southeast"
    doAction = 'MoveSE'
;
moveSWVerb: deepverb
    verb = 'move southwest' 'move sw' 'push southwest' 'push sw'
    sdesc = "move southwest"
    doAction = 'MoveSW'
;
centerVerb: deepverb
    verb = 'center' 'centre'
    sdesc = "center"
    doAction = 'Center'
;
searchVerb: deepverb
    verb = 'search'
    sdesc = "search"
    doAction = 'Search'
;
wieldVerb: deepverb
   verb = 'use' 'wield' 'brandish' 'cock' 'raise' 'bunch' 'put up'
   sdesc = "wield"
   doAction = 'Wield'
;

/*
 *   Travel verbs  - these verbs allow the player to move about.
 *   All travel verbs have the property isTravelVerb set true.
 */
class travelVerb: deepverb
    isTravelVerb = true
;
eVerb: travelVerb
    action( actor ) = { actor.travelTo( self.travelDir( actor )); }
    verb = 'e' 'east' 'go east'
    travelDir( actor ) = { return( actor.location.east ); }
;
sVerb: travelVerb
    action( actor ) = { actor.travelTo( self.travelDir( actor )); }
    verb = 's' 'south' 'go south'
    travelDir( actor ) = { return( actor.location.south ); }
;
nVerb: travelVerb
    action( actor ) = { actor.travelTo( self.travelDir( actor )); }
    verb = 'n' 'north' 'go north'
    travelDir( actor ) = { return( actor.location.north ); }
;
wVerb: travelVerb
    action( actor ) = { actor.travelTo( self.travelDir( actor )); }
    verb = 'w' 'west' 'go west'
    travelDir( actor ) = { return( actor.location.west ); }
;
neVerb: travelVerb
    action( actor ) = { actor.travelTo( self.travelDir( actor )); }
    verb = 'ne' 'northeast' 'go ne' 'go northeast'
    travelDir( actor ) = { return( actor.location.ne ); }
;
nwVerb: travelVerb
    action( actor ) = { actor.travelTo( self.travelDir( actor )); }
    verb = 'nw' 'northwest' 'go nw' 'go northwest'
    travelDir( actor ) = { return( actor.location.nw ); }
;
seVerb: travelVerb
    action( actor ) = { actor.travelTo( self.travelDir( actor )); }
    verb = 'se' 'southeast' 'go se' 'go southeast'
    travelDir( actor ) = { return( actor.location.se ); }
;
swVerb: travelVerb
    action( actor ) = { actor.travelTo( self.travelDir( actor )); }
    verb = 'sw' 'southwest' 'go sw' 'go southwest'
    travelDir( actor ) = { return( actor.location.sw ); }
;
inVerb: travelVerb
    action( actor ) = { actor.travelTo( self.travelDir( actor )); }
    verb = 'in' 'go in' 'enter'
    sdesc = "enter"
    doAction = 'Enter'
    travelDir( actor ) = { return( actor.location.in ); }
    ioAction(onPrep) = 'EnterOn'
    ioAction(inPrep) = 'EnterIn'
    ioAction(withPrep) = 'EnterWith'
;
outVerb: travelVerb
    action( actor ) = { actor.travelTo( self.travelDir( actor )); }
    verb = 'out' 'go out' 'exit' 'leave'
    travelDir( actor ) = { return( actor.location.out ); }
;
dVerb: travelVerb
    action( actor ) = { actor.travelTo( self.travelDir( actor )); }
    verb = 'd' 'down' 'go down'
    travelDir( actor ) = { return( actor.location.down ); }
;
uVerb: travelVerb
    action( actor ) = { actor.travelTo( self.travelDir( actor )); }
    verb = 'u' 'up' 'go up'
    travelDir( actor ) = { return( actor.location.up ); }
;

/*
 *   sysverb:  A system verb.  Verbs of this class are special verbs that
 *   can be executed without certain normal validations.  For example,
 *   a system verb can be executed in a dark room.  System verbs are
 *   for operations such as saving, restoring, and quitting, which are
 *   not really part of the game.
 */
class sysverb: deepverb
    issysverb = true
;
quitVerb: sysverb
    verb = 'quit'
    action( actor ) =
    {
	local yesno;

	scoreRank();
	"\bDo you really want to quit? (YES or NO) > ";
	yesno := yorn();
	"\b";
	if ( yesno = 1 )
	{
	    terminate();    // allow user good-bye message
	    quit();
	}
	else
	{
	    "Okay. ";
	}
	abort;
    }
;
verboseVerb: sysverb
    verb = 'verbose'
    action( actor ) =
    {
	"Okay, now in VERBOSE mode.\n";
	global.verbose := true;
	Me.location.lookAround( true );
	abort;
    }
;
terseVerb: sysverb
    verb = 'brief' 'terse'
    action( actor ) =
    {
	"Okay, now in TERSE mode.\n";
	global.verbose := nil;
	abort;
    }
;
scoreVerb: sysverb
    verb = 'score' 'status'
    action( actor ) =
    {
	scoreRank();
	abort;
    }
;
saveVerb: sysverb
    verb = 'save'
    sdesc = "save"
    doAction = 'Save'
    action( actor ) =
    {
	local savefile;
	
	savefile := askfile( 'File to save game in' );
	if ( savefile = nil or savefile = '' )
	    "Failed. ";
	else if (save( savefile ))
	    "Saved failed. ";
	else
	    "Saved. ";
	abort;
    }
;
restoreVerb: sysverb
    verb = 'restore'
    sdesc = "restore"
    doAction = 'Restore'
    action( actor ) =
    {
	local savefile;
	
	savefile := askfile( 'File to restore game from' );
	if ( savefile = nil or savefile = '' )
	    "Failed. ";
	else if (restore( savefile ))
	    "Restore failed. ";
	else
	{
	    scoreStatus(global.score, global.turnsofar);
	    "Restored.\b";
	    Me.location.lookAround(true);
	}
	abort;
    }
;
scriptVerb: sysverb
    verb = 'script'
    doAction = 'Script'
    action( actor ) =
    {
	local scriptfile;
	
	scriptfile := askfile( 'File to write transcript to' );
	if ( scriptfile = nil or scriptfile = '' )
	    "Failed. ";
	else
	{
	    logging( scriptfile );
	    "All text will now be saved to the script file.
	    Type UNSCRIPT at any time to discontinue scripting.";
	}
	abort;
    }
;
unscriptVerb: sysverb
    verb = 'unscript'
    action( actor ) =
    {
	logging( nil );
	"Script closed.\n";
	abort;
    }
;
restartVerb: sysverb
    verb = 'restart'
    action( actor ) =
    {
	local yesno;
	while ( true )
	{
	    "Are you sure you want to start over? (YES or NO) > ";
	    yesno := yorn();
	    if ( yesno = 1 )
	    {
		"\n";
		scoreStatus(0, 0);
		restart(initRestart, global.initRestartParam);
		abort;
	    }
	    else if ( yesno = 0 )
	    {
		"\nOkay.\n";
		abort;
	    }
	}
    }
;
versionVerb: sysverb
    verb = 'version'
    action( actor ) =
    {
	version.sdesc;
	abort;
    }
;
debugVerb: sysverb
    verb = 'debug'
    action( actor ) =
    {
	if (debugTrace())
	    "You can't think this game has any bugs left in it... ";
	abort;
    }
;

undoVerb: sysverb
    verb = 'undo'
    action(actor) =
    {
	/* do TWO undo's - one for this 'undo', one for previous command */
	if (undo() and undo())
	{
	    "(Undoing one command)\b";
	    Me.location.lookAround(true);
	    scoreStatus(global.score, global.turnsofar);
	}
	else
	    "No more undo information is available. ";

	abort;
    }
;

/*
 *  Prep: object
 *
 *  A preposition.  The preposition property specifies the
 *  vocabulary word.
 */
class Prep: object
;

/*
 *   Various prepositions
 */
aboutPrep: Prep
    preposition = 'about'
    sdesc = "about"
;
withPrep: Prep
    preposition = 'with'
    sdesc = "with"
;
toPrep: Prep
    preposition = 'to'
    sdesc = "to"
;
onPrep: Prep
    preposition = 'on' 'onto' 'downon' 'upon'
    sdesc = "on"
;
inPrep: Prep
    preposition = 'in' 'into' 'downin'
    sdesc = "in"
;
offPrep: Prep
    preposition = 'off' 'offof'
    sdesc = "off"
;
outPrep: Prep
    preposition = 'out' 'outof'
    sdesc = "out"
;
fromPrep: Prep
    preposition = 'from'
    sdesc = "from"
;
betweenPrep: Prep
    preposition = 'between' 'inbetween'
    sdesc = "between"
;
overPrep: Prep
    preposition = 'over'
    sdesc = "over"
;
atPrep: Prep
    preposition = 'at'
    sdesc = "at"
;
aroundPrep: Prep
    preposition = 'around'
    sdesc = "around"
;
thruPrep: Prep
    preposition = 'through' 'thru'
    sdesc = "through"
;
dirPrep: Prep
    preposition = 'north' 'south' 'east' 'west' 'up' 'down' 'northeast' 'ne'
		  'northwest' 'nw' 'southeast' 'se' 'southwest' 'sw'
    sdesc = "north"         // Shouldn't ever need this, but just in case
;
underPrep: Prep
    preposition = 'under' 'beneath'
    sdesc = "under"
;
behindPrep: Prep
    preposition = 'behind'
    sdesc = "behind"
;

/*
 *   articles:  the "built-in" articles.  "The," "a," and "an" are
 *   defined.
 */
articles: object
    article = 'the' 'a' 'an'
;
