/* Copyright (c) 1988, 1991 by Michael J. Roberts.  All Rights Reserved. */
/*
   adv.t  - standard adventure definitions for TADS games
   Version 1.2

   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;

/*
 *   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;
die: function;
init: function;
pardon: function;
preinit: function;
sleepDaemon: function;
eatDaemon: function;
goToSleep: function;
initSearch: function;

/*
 *   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)";
            else 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;
    }
}

/*
 *  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;
    setscore( 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;
    setscore( global.score, global.turnsofar );
}

/*
 *  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.
*/
initSearch: function
{
    local o;
    
    o := firstobj();
    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);
    }
}

/*
 *  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.
*/
nestedroom: room
    islit =
    {
        if ( self.location ) return( self.location.islit );
        return( nil );
    }
    statusLine =
    {
        self.location.sdesc; ", in "; 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.
*/
chairitem: fixeditem, nestedroom, surface
    reachable = []          // 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
    roomAction( actor, v, do, prep, io ) =
    {
        if ( do<>nil and v<>inspectVerb )
            checkReach( self, actor, v, do );
        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.
*/
beditem: chairitem
    ischair = nil
    isbed = true
    sdesc = "bed"
;

/*
 *  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 a "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
 *  necessary 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; always use
 *  moveInto to move an object rather than setting its location
 *  directly, so that the appropriate contents lists are updated.
 *  To make an object disappear, move it into nil.
*/
thing: object
    isListed = true         // shows up in room/inventory listings
    contents = []
    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, SAVE \"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 ) =
    {
        "I don't know how to 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, do ) =
    {
        do.doTakeOut( actor, self );
    }
    verDoTakeOut( actor, io ) =
    {
        if ( io <> nil and not self.isIn( io ))
        {
            caps(); self.thedesc; " isn't in "; io.thedesc; ". ";
        }
    }
    doTakeOut( actor, io ) =
    {
        self.doTake( actor );
    }
    verIoTakeOff( actor ) = {}
    ioTakeOff( actor, do ) =
    {
        do.doTakeOff( actor, self );
    }
    verDoTakeOff( actor, io ) =
    {
        if ( io <> nil and not self.isIn( io ))
        {
            caps(); self.thedesc; " isn't on "; io.thedesc; "! ";
        }
    }
    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, do ) =
    {
        do.doAskAbout( actor, self );
    }
    verDoAskAbout( actor, io ) =
    {
        "Surely, %you% can't think "; self.thedesc; " knows anything
        about it! ";
    }
    verIoTellAbout( actor ) = {}
    ioTellAbout( actor, do ) =
    {
        do.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, do ) =
    {
        do.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).
*/
item: thing
    weight = 0
    bulk = 1
;

/*
 *  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 searchObj(actor,
 *  list) is given the list of hidden items contained in the object (for
 *  example, this would be the underCont property, in the case of a
 *  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.
*/
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.  Note that you
 *  should set the underLoc property of each object to be under this
 *  underHider object to point to this object.
 *  
 *  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 object
 *  the other object is under as an underHider, then declare the object
 *  to go underneath normally except that it has an underLoc property
 *  rather than a location property.  The behindHider and
 *  searchHider objects work similarly.
*/
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.
*/
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.
*/
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, a fixeditem is not
 *  described automatically in a room's description (i.e., the isListed
 *  property is set to nil, because it is generally described separately
 *  as part of the room's ldesc.  
*/
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; ". ";
    }
;

/*
 *  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.
*/
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."
*/
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.
*/
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.
*/
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.
*/
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.
*/
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, do, 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
    noun = 'floor' 'ground'
    sdesc = "ground"
    adesc = "the ground"
    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, do ) =
    {
        do.doDrop( actor );
    }
    ioPutIn( actor, do ) =
    {
        do.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, either "him''
 *  or "her" will be accepted.
*/
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.
*/
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"
;

/*
 *  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; }
    actorDesc = {}
    myactor = nil   // set to the Actor to be followed
    verDoFollow( actor ) = {}
    doFollow( actor ) =
    {
        actor.travelTo( self.myactor.location );
    }
;

/*
 *  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
    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.
*/
decoration: fixeditem
;

/*
 *  buttonitem: fixeditem
 *
 *  A button (the type you push).  The individual button's
 *  doPush( actor ) method, 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";
	}
    }
    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; ". ";
        }
    }
    doUnwear( actor ) =
    {
        "Okay, %you're% no longer wearing "; self.thedesc; ". ";
        self.isworn := nil;
    }
;

/*
 *  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.
*/
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 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.
*/
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.
*/
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; the actor must get out first.
*/
vehicle: item, nestedroom
    reachable = []
    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 )
;

/*
 *  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.
*/
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, do ) =
    {
        do.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.
*/
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, do ) =
    {
        if (addbulk( self.contents ) + do.bulk > self.maxbulk )
        {
            "%You% can't fit that in "; self.thedesc; ". ";
        }
        else
        {
	    do.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.
*/
openable: container
    contentsReachable = { return( self.isopen ); }
    contentsVisible = { return( self.isopen ); }
    isopenable = true
    ldesc =
    {
    	caps(); self.thedesc;
    	if ( self.isopen ) " is open. ";
    	else " is closed. ";
    	pass ldesc;
    }
    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.
*/
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.
*/
lockable: openable
    doOpen( actor ) =
    {
        if ( self.islocked )
        {
            "It's locked. ";
        }
        else pass doOpen;
    }
    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.
*/
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
 *  object.  It otherwise behaves as an ordinary item.
*/
keyItem: item
    verIoUnlockWith( actor ) = {}
    ioUnlockWith( actor, do ) =
    {
        do.doUnlockWith( actor, self );
    }
    verIoLockWith( actor ) = {}
    ioLockWith( actor, do ) =
    {
        do.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".
*/
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, do ) = { do.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".
*/
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...\" "; }
    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. ";
	    setscore( global.score, global.turnsofar );
	}
        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 vocabulary words known as verbs;
 *  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.
*/
deepverb: object                // A deep-structure verb.
    validDo( actor, obj, seqno ) =
    {
    	return( obj.isReachable( actor ));
    }
    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 ));
    }
    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' '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 ); }
;
tellVerb: deepverb
    verb = 'tell'
    sdesc = "tell"
    prepDefault = aboutPrep
    ioAction( aboutPrep ) = 'TellAbout'
    validIo( actor, obj, seqno ) = { return( seqno = 1 ); }
;
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'
    doAction = 'Jump'
    action = { "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 ) ret := ret + cur;
		i := i + 1;
	    }
            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'
    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'
    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'
    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'
    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'
    sdesc = "look through"
    doAction = 'Lookthru'
;
attackVerb: deepverb
    verb = 'attack' 'kill' 'hit'
    sdesc = "attack"
    prepDefault = withPrep
    ioAction( withPrep ) = 'AttackWith'
;
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'
;
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'
;
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'
    sdesc = "look under"
    doAction = 'Lookunder'
;
lookBehindVerb: deepverb
    verb = 'look 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'
    sdesc = "center"
    doAction = 'Center'
;
searchVerb: deepverb
    verb = 'search'
    sdesc = "search"
    doAction = 'Search'
;

/*
 *   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 ); }
;
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.
 */
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
	{
	    setscore( global.score, global.turnsofar );
	    "Restored. ";
	}
	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 );
	    "Writing script file. ";
	}
	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";
		setscore( 0, 0 );
                restart();
                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;
    }
;

/*
 *  Prep: object
 *
 *  A preposition.  The preposition property specifies the
 *  vocabulary word.
*/
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'
;
