/*
* There's a couple bugs in this sample game. I'm posting it
* anyway because it's not supposed to be a 'plug-n-play'
* module, but rather just here to demonstrate a technique.
* I rather doubt that anyone will use it in its present form.
* (It works quite well enough to see what's going on here.)
* If you fix it gooder, please repost it on ifarchive. thanks.
* Anyway:
*
* I'm trying to achieve several things with this example game.
*
* First and most important, there's a technique here by which
* the parser can deal intelligently with standard English
* dependent clauses during disambiguation. (It recognizes and
* correctly understands "the water that is in the bucket" for
* example.)
*
* Second, this is a liquid handler with special capabilities.
*
* The liquid handler explores a bit how this parser stuff can
* be applied practically, but I bet you can think of even
* cooler uses for the parser tricks in this example game.
*
* I don't have a lot of time on my hands (contrary to
* appearances...), but if you want to you can bug me with
* questions about this thing. I hope it's well enough
* commented, though.
*
* I don't know how useful this is for T3. (If you decide to
* adapt it, I'd be happy to hear from you.) I wrote this before
* T3 was released. It's probably totally obsolete as it is now,
* but hopefully it will be useful as an illustration of a
* technique.
*
* Anyhow,
* Do whatever you like with this. (hack, steal, sell, ridicule, adapt,
* and/or take complete credit for.)
* Drop me a line if you do something clever with it that you think I
* might want to see.
* - breslin
* [email protected]
*
*/

#include <adv.t>
#include <std.t>

addliquidlocationadjective: function;
delliquidlocationadjective: function;

replace commonInit: function
   {
   setdaemon(delliquidlocationadjective, nil);
   }

/****************************************
* this is a complex section. our execution has to do
* specifically with the reservoir class (see below) and related
* object handling.
*
* however, the general potential of this code is impressive, in our
* opinion. this is recommended reading for anyone interested in parser
* systems and techniques.
*
* we're making clauses such as 'the water thats in the bottle'
* parseable. that's the goal. here's the plan:
* first, we make the final word of the phrase a noun for the target
* object.
* the target object in this case is the water (the water that's in the
* bottle in particular). so to the corresponding object, we add the noun
* 'bottle'. we then make its other noun ('water') into an adjective.
* to recap: at this point,
* we have an object _water_, and to this object has been added the noun
* 'bottle' (the same name which its location has)
* and we have also added to this object the adjective 'water' (its
* primary noun before we started the name adding). we can't refer to it
* as 'the water thats in the bottle' yet, but
* we can already refer to it as 'water bottle'. (this doesnt help much
* yet, of course.)
*
* now all we have to do is contort each of the words in the 'thats in the'
* part of the clause into simple adjectives which we assign to the target
* object. so, now we have the target object _water_ which has adjectives
* 'water' 'thats' 'in' and 'the', and for one of its nouns, 'bottle'.
* viola.
*
* Beware:
* this contortion is a problem if not handled carefully. for one example,
* "in" and "from" are used in other senses. if we don't treat "in"
* carefully, for example, it can be misinterpreted by the parser as an
* adjective elsewhere:
*
* > put the water in the bottle
*
* What do you want to put it in?
*
* by 'it', the parser is referring to the water that's inside the bottle.
* the parser thinks this construction was attempting: "put the water
* (thats currently) in the bottle (into someplace as yet unspecified)".
* for this reason, if not for several others, the changes we make to the
* object for the purposes of parsing clauses like 'the water thats in the
* bottle' or 'the lotion thats in the basket' must be temporary changes.
* in other words, if we don't revert to basic parsing, we introduce more
* problems than we solve.
*
* in the present game, we're only using this technique during
* disambiguation of specific classes of objects.
*
* our implementation of this plan works fine in this game, but we submit
* a few questions/ideas to anyone interested in developing this further:
*
* once we spoil the player with definite clauses (clauses that begin
* with "that" -- or alternatively "which"), they'll want to use them
* elsewhere. developing this clause-parsing system might involve hooking
* into the parser near the point where the parser would normally complain,
* "there's words after your command I can't use". (?)
* it seems as though we then could add our contorted adjectives to the
* words in question, assign them the noun from the end of the phrase, and
* run the command through again. (?)
*
* it's possible that doing this would merely complicate, and not improve
* the parser. (?)
*
* it might be desireable to move all this stuff from parseDisambig to
* disambigDobj/Iobj... although for the moment we can't see any particular
* benefit. (?)
*
**********************************************************************/

parseDisambig: function(str, lst)
 {
 local i, cnt, liquidinlist;
 liquidinlist := nil;
 for (i := 1, cnt := length(lst) ; i <= cnt ; ++i)
       {
       if ((isclass(lst[i], liquid)) or (isclass(lst[i], liquidreservoir)))
               liquidinlist := true;
       }
       if (liquidinlist)
               {
               local i, cnt, testdog;
               "Do %you% mean the ";
       for (i := 1, cnt := length(lst) ; i <= cnt ; ++i)
               {
                       if (isclass(lst[i], liquid))
                               {
                       lst[i].thedesc;
                               " thats in <<lst[i].location.thedesc>>";
                               addliquidlocationadjective (lst[i]);
                               }
                       else if (isclass(lst[i], liquidreservoir))
                               {
                               if (isclass(lst[i].location, liquidcontainer))
                                       {
                               lst[i].thedesc;
                                       " thats in <<lst[i].location.thedesc>>";
                                       }
                               else
                                       {
                                       if (lst[i].preferredDisambName <> 'undefined')
                                               "<<lst[i].preferredDisambName>>";
                                       else
                                               "<<str>> from <<lst[i].thedesc>>";
                                       }
                               addliquidlocationadjective (lst[i]);
                               }
                       else
                               {
                               say(car(getwords(lst[i], &adjective)));
                               " ";
                               lst[i].sdesc;
                               }
                       if (i < cnt) ", ";
                       if (i + 1 = cnt) "or ";
                       }
               "?";
               }
       else
               {
       local i, tot, cnt;
       "Which << str >> do %you% mean, ";
       for (i := 1, cnt := length(lst) ; i <= cnt ; ++i)
       {
          lst[i].thedesc;
          if (i < cnt) ", ";
          if (i + 1 = cnt) "or ";
       }
       "?";
       }
}

addliquidlocationadjective: function(item)
{
       local loc, newnoun, newadj;
       if (isclass(item.location, liquidcontainer) = nil)
               return;                                                 // unnecessary unless container
                                                                               // is involved (!)

       if (item.locationadjective = true)      // should always be redundant
               return;
       loc := item.location;
       newnoun := car(getwords(loc, &noun));   // container's name.
                                                                                       // reservoirs inside (fixed)
                                                                                       // liquidcontainers _should_
                                                                                       // naturally inherit the
                                                                                       // container name. if the
       /*                                      // reservoir did not, we're
    * need to save the noun, because       // going to add it here, and
    * the location potentially changes.    // we're not going to take it
    */                                     // away with the del-function

                                                                                       // liquids inside
                                                                                       // liquidcontainers (!) are
                                                                                       // assigned the container
                                                                                       // name as a temporary
                                                                                       // noun (!)
       if (isclass(item, liquid))
               item.tempnoun := newnoun;


       newadj := car(getwords(item, &noun));   // actually many new adjectives
                                                                                       // (see below). the only
                                                                                       // _item_specific_ one is
                                                                                       // the name of the item.
                                                                                       // (!) the name of the
                                                                                       // item is re-assigned to the
                                                                                       // item as an adjective (!)

       addword(item, &noun, newnoun);                  // these two are the only
       addword(item, &adjective, newadj);              // _item_specific_ changes.

       addword(item, &adjective, 'in');                // the remaining changes
       addword(item, &adjective, 'from');              // are generic
       addword(item, &adjective, 'inside');

       item.locationadjective := true;                 // changes have been made (!)
       global.liquidlocationadjectivelist := global.liquidlocationadjectivelist + item;
}

delliquidlocationadjective: function(parm)      // reverses (!) the effect of
{                                                                                       // addliquidlocationadjective.
if (global.liquidlocationadjectivelist <> [])   // we apologise for the
 {local llal, i;                                                               // obsessive doublechecking
  llal := global.liquidlocationadjectivelist;  // you're about to witness.
  for (i := 1; i <= length(llal); i++)         // we're leaving this in, in
                                               // case you're debugging
                                               // and need the help.
       {
       local loc, newnoun, newadj, liqlist;
   if (llal[i] = nil) return;
       liqlist := global.liquidlist;
       loc := llal[i].location;

       if  (
               (length(liqlist) > length(liqlist - llal[i]))   // if item isn't on the
               and                                                                                     // global.liquidlist,
               (isclass(loc, liquidcontainer) = true)                  // we leave it alone.
               and                                                                                             // this shouldn't happen.
               (llal[i].locationadjective <> nil)      // should always be a redundant check
               )
               {
       newnoun := llal[i].tempnoun;                    // the noun that was newly
                                                                                       // assigned during
                                                                                       // addliquidlocationadjective.
                                                                                       // primary noun of the item's
                                                                                       // container. we're stripping
                                                                                       // that from the item if the
                                                                                       // item isn't a reservoir.

       if (isclass(llal[i], liquid))                   // only liquids, not reservoirs, need
               delword(llal[i], &noun, newnoun);       // to be adjusted here. reservoirs
                                                                               // are supposed to inherit their
                                                                               // container name, if they have a
                                                                               // container. so we don't want to
                                                                               // delete that.

       newadj := car(getwords(llal[i], &noun));        // primary noun of the item, which
                                                                                               // had been made into an adjective
                                                                                               // (reservoir or not).

       delword(llal[i], &adjective, newadj);           // we remove the item's primary
                                                                                               // noun from the item's adjective
                                                                                               // list. This may or may not be
                                                                                               // really necessary.... (?)

       delword(llal[i], &adjective, 'in');             // now we remove the other generic
       delword(llal[i], &adjective, 'from');   // adjectives.
       delword(llal[i], &adjective, 'inside');

       llal[i].locationadjective := nil;               // changes reversed, remove flag
               }
       }
 global.liquidlocationadjectivelist := [];
 }
}


modify thing
   adjective = 'thats' 'that\'s' 'the' // see parsedisamb for explanation
   doPutIn(actor, io) =        // this deals with foreign objects
   {                           // (solid objects) going into
                               // reservoirs.
       if ((isclass(io, liquidreservoir)) and (isclass(io.location, room)))
   // if the reservoir doesn't have a container, objects placed in it
   // are destroyed. (like throwing an object in a lake or so. change this
   // whenever necessary.)
               {
               "%You% throw%s% <<self.thedesc>> into <<io.thedesc>>. Hope you didn't
               need that!";
               self.moveInto(nil);
               }
       else if (isclass(io, liquidreservoir))
               {
               "%You% put%s% <<self.thedesc>> in <<io.location.thedesc>>.";
               self.moveInto(io.location);
               }
       else                    // not a reservoir. regular putIn.
               {
       self.moveInto(io);
       "Done. ";
               }
   }
       verIoPourIn(actor) = { }
       verDoPourIn(actor, iobj) = { }
       verIoPourInto(actor) = { }
       verDoPourInto(actor, iobj) = { }
       verDoFillWith(actor, iobj) = { }
       verIoFillWith(actor) = { }
       ioFillWith(actor, dobj) =
               {
               execCommand(actor, putVerb, self, inPrep, dobj);
               }
       ioPourInto(actor, dobj) =
               {
               execCommand(actor, putVerb, dobj, inPrep, self);
               }
       ioPourIn(actor, dobj) =
               {
               execCommand(actor, putVerb, dobj, inPrep, self);
               }

       verDoEmpty(actor) = { "\^<<self.thedesc>> isn't the sort of thing that
               can be filled and emptied."; }
       ioEmptyOn(actor, dobj) = { dobj.doEmptyOn(actor, self); }
       ioEmptyIn(actor, dobj) = { dobj.doEmptyInto(actor, self); }
       ioEmptyInto(actor, dobj) = { dobj.doEmptyInto(actor, self); }
;

modify global
   liquidlocationadjectivelist = []
       lamplist = []   // list of all known light providers in the game
       liquidlist = [] // list of all liquids and liquidreservoirs
;

startroom: room
   sdesc = "The Demonstration Room"

   ldesc = {
       "There's a toilet here, a river, and a gruesome stream of ";
       "blood. You might be carrying some liquid containers.";
       }
   north = startroom
;

toilet: liquidcontainer, fixeditem
       location = startroom
       sdesc = "toilet"
       noun = 'toilet'
       adjective = 'white'
   canReachContents = true
       ldesc = {
               "It's a standard public toilet:
               white porcelain, and a flusher for flushing.\n";
               "There's water in the bowl. ";
       }
       verDoFlush( actor ) = {}
       doFlush( actor ) = {
       "The toilet gurgles loudly as water rushes through the bowl.\n";
       }
;

bottle: liquidcontainer
       location = startroom
       sdesc = "bottle"
       noun = 'bottle'
       ldesc = {
                       "It's made of glass, and somewhat ribbed around the sides,
                       much like an old style cocacola bottle. It has no lid. ";
                       pass ldesc;
                       }
       adjective = 'glass'
       ioPutIn(actor, dobj) = {
               if      (
                       (isclass(dobj, liquid) = nil)
                       and
                       (isclass(dobj, liquidreservoir) = nil)
                       )
                       "The rim of the bottle is too narrow. You can't force it in.";
               else
                       dobj.doPutIn(actor, self);
               }
;



/****************
* the whole purpose of liquidreservoir is, you don't empty
* the liquidreservoir when you get the liquid from it.
*
*      the implementation adopted for liquids is as follows:
*
* there's a liquid class, and then there's the
* liquidreservoir class.
* the player actually only takes an object of class 'liquid',
* which is dynamically created and moved into the player's
* container when s/he gets/pours/etc the liquidfountain object
* with/into/etc an appropriate container. for each liquidreservoir
* type object, there should be a corresponding liquid
* but not necessarily vice-versa. liquidreservoir
* items should be lakes, swamps, streams, fountains,
* etc. liquids are 'water/riverwater' 'slime' etc.
* the player never touches the actual reservoir
* while pouring. the corresponding liquid is substituted
* for it. liquidreservoir items can be inside other items,
* as in the water inside a fountain. see below for info about
* liquidcontainers (items designed to contain liquids, as opposed
* to items designed to contain liquidcontainers).
*
* now here's some slightly more complicated technical stuff that you
* only need to read if you want to improve our system:
* the command involved in moving and creating liquids
* center around the verb PutIn. the other verbs (such
* as FillWith, PourIn, PourInto) execute PutIn.
* one important thing to remember: normally, the
* io<Verb>[Prep] actually carries out the action of the
* verb after it passes its verIo<Verb>[Prep] and
* verDo<Verb>[Prep], but with the verb "PutIn", this action
* is transferred from IoPutIn to DoPutIn. (this is its
* natural library definition, and wasn't our idea -- wether
* it makes more sense or not... probably does or mike would have
* made it more consistant.) anyway,
* in other words, the object being "contained" actually
* performs the action of the verb, not the container.
* There's nothing wrong with changing this if you want
* to, but we've adopted this for liquids, to keep things
* (?) simpler (?), and standard, and hopefully more
* intuitive for people who know TADS well enough to be
* making improvements on our system.
*
* as a side note, dynamically created liquids are, so to
* speak, only one volume, as are liquidcontainers (i.e.,
* liquidcontainers are either filled, or empty.) this means
* that you can fill a barrel with a teacup, and vice versa.
* if you feel like you need to fix this for your game, check
* out safe.t in if-archive\programming\tads\examples. that
* might get you started.
* also, you might consider testing containability
* based on wether or not one container will fit inside another.
* you could use container bulk for this, or some other measure,
* for example dan shovitz' (unpublished ?) 1 thru 6 size gague,
* a la LFPhoenix.
                               ************************************************/
modify theFloor
   ioPutOn(actor, dobj) =
       {
               if (isclass(dobj,liquid))
                       { dobj.doPutOn(actor, self); }
               else
       dobj.doDrop(actor);
       }
;

modify class container

       verDoEmpty(actor) = { }
       verDoEmptyOn(actor, io) = { }
       verIoEmptyIn(actor) = { self.verIoEmptyInto(actor); }
       verDoEmptyIn(actor, io) = { self.verDoEmptyInto(actor, io); }
       verIoEmptyInto(actor) = { }
       verDoEmptyInto(actor, io) = {
               if (io.isIn(self))
                       {
                       "%You%'ll have to take <<io.thedesc>> out of
                       <<self.thedesc>> before %you% can do that.";
                       }
               }
       doEmptyIn(actor, io) = { self.doEmptyInto(actor,io); }
       doEmpty(actor) = {
               "(onto the ground)\n";
               self.doEmptyOn(actor, theFloor);
               }
       doEmptyOn(actor, io) = {
               if ((self.contents) = [])
                       {
                       "%You% might want to try putting something inside <<self.thedesc>> ";
                       "before %you% decide%s% to empty it.";
                       return;
                       }
               if ((self.isIn(parserGetMe()) = nil) and (isclass(self, fixeditem)))
                       {
                       "%You%'d have to pick up <<self.thedesc>> to empty it, and %you%
                       can't pick it up. Maybe %you% should try just taking its
                       contents.";
                       return;
                       }
               else
                       {
                       local i, c;
                       if (self.location <> parserGetMe())
                               {
                               "%You% grab%s% <<self.thedesc>> and empt%ies% its contents onto
                               <<io.thedesc>>.\n";
                               self.moveInto(parserGetMe());
                               }
                       c := self.contents;
                       for (i := 1; i <= length(c); i++)
                               {
                               if (c[i].isOnSurface = nil)
                                       {
                                       "\n\^";
                                       c[i].sdesc;
                                       ": ";
                                       execCommand(actor, putVerb, c[i], onPrep, io);
                                       }
                               }
                       }
               }

       doEmptyInto(actor, io) = {
               if ((self.contents) = [])
                       {
                       "%You% might want to try putting something inside <<self.thedesc>>
                       before %you% decide%s% to empty it.";
                       return;
                       }
               if ((self.isIn(parserGetMe()) = nil) and (isclass(self, fixeditem)))
                       {
                       "%You%'d have to pick up <<self.thedesc>> to empty it, and %you%
                       can't pick it up. Perhaps %you% should try just taking its
                       contents.";
                       return;
                       }
               else
                       {
                       local i, c;
                       if (self.location <> parserGetMe())
                               {
                               "%You% grab%s% <<self.thedesc>> and empt%ies% its contents into
                               <<io.thedesc>>.\n";
                               self.moveInto(parserGetMe());
                               }
                       c := self.contents;
                       for (i := 1; i <= length(c); i++)
                               {
                               if (c[i].isOnSurface = nil)
                                       {
                                       "\n\^";
                                       c[i].sdesc;
                                       ": ";
                                       execCommand(actor, putVerb, c[i], inPrep, io);
                                       }
                               }
                       }
               }
;


class liquidreservoir: fixeditem
       preferredDisambName = 'undefined'       // useful for reservoirs
                                                                               // without containers (lakes, etc.)
       verIoPutIn(actor) = { }
       ioPutIn(actor, dobj) = { dobj.doPutIn(actor, self); }
       ispoison = nil
       liquidContentsClassPointer = nil        // must put the contents class here
       verDoTake(actor) =      {
               "\^<<self.liquidContentsClassPointer.thedesc>> would just
               slip through %your% fingers.";
               }

       doPutIn(actor, io) =
               {
               local contentsClassPointer := new liquidContentsClassPointer;
               if (io.isIn(actor)=nil)
                       {
                       "(takeing <<io.thedesc>>)\n";
                       io.moveInto(actor);
                       }

               global.liquidlist := global.liquidlist + contentsClassPointer;
       contentsClassPointer.moveInto(io);
               (io.isfilled := true);
/******************************
* we're assuming that carriable reservoirs haven't been implemented.
* if you have a reservoir that's carried, you might
* want to add a custom message about that in the
* following lines. if you want to change its behavior, you might need
* to do something with verIoPutIn/verDoPutIn
               *********************************************/
               if ((self.location) and (firstsc(self.location) <> room))
                       {
                       "%You% dip%s% <<io.thedesc>> into <<self.location.thedesc>> and
                       fill%s% it with <<liquidContentsClassPointer.sdesc>>.";
                       }
               else
                       {
               "%You% dip%s% <<io.thedesc>> into <<self.thedesc>> and
                       fill%s% it with <<liquidContentsClassPointer.sdesc>>. ";
                       }
               }

   verDoPutIn(actor, io) =
       {
       if (io = nil)
           return;
               else if (
                               ((isclass(io, liquidcontainer)) = nil)
                               and
                               ((isclass(io, liquidreservoir)) = nil)
                               )
                       {
                       "\^<<io.thedesc>> isn't a suitable container
                       for <<self.liquidContentsClassPointer.sdesc>>. ";
                       }
       else if ((io = self) or (io = self.location))
               {
           "%You% can't pour "; self.thedesc; " in <<self.itselfdesc>>, pal. ";
               }
       else if (io.isIn(self))
           self.circularMessage(io);
               else if (isclass(io, fixeditem))
                       {
                       "How do%es% %you% expect to do that? %You% can move
                       neither ";
                       if (firstsc(io.location) = room)
                               io.thedesc;
                       else
                               io.location.thedesc;
                       " nor ";
                       if (firstsc(self.location) = room)
                               self.thedesc;
                       else
                               self.location.thedesc;
                       ".";
                       }
       else if (length(io.contents) > 0)
               {
                       local i, c;
                       c := io.contents;
                       for (i := 1; i <= length(c); i++)
                               {
                               if (isclass(c[i], liquidContentsClassPointer))
                                       {
                                       local liquidInsideIo := c[i];
                                       "%You're% too late. It's already filled
                                       with <<io.thedesc>>.\n";
                                       }
                               else if (isclass(c[i], liquid))
                                       {
                                       local liquidInsideIo := c[i];
                                       "\^<<io.thedesc>> is already filled
                                       with <<liquidInsideIo.sdesc>>. %You%'ll have to empty it
                                       first.";
                                       }
                               }
/* optional:   else "There's already something inside <<io.thedesc>>."; */
                       }
       else
           self.verifyRemove(actor);
   }

   verDoPourOn(actor, io) =
   {
       "If %you% could figure out a way to pick up <<self.thedesc>>, %you%
               might have a chance.";
   }
       verIoDouseWith(actor, io) = {self.verDoPourOn(actor, io);}
       verDoPutOn(actor, io) = {self.verDoPourOn(actor, io);}
   doPutOn(actor, io) = {self.verDoPourOn(actor, io);}

   verDoDrink(actor) = { }
       doDrink(actor) = {
               if (self.ispoison)
                       {
                       if (actor = parserGetMe())
                               {
                               "Hmm, that tasted strange...\nYou sputter and gag for a moment,
                               then roll up into a ball and die a quick but painful death.\n";
                               die();
                               }
                       else
                               {
                               "<<actor.sdesc>> refuses.\n";   // if this ever happens, you'll want to
                               }                                                               // change the message here anyway
                       }
               else
                       {
                       "%You% gulp%s% down <<self.thedesc>>. Aah, refreshing!";
                       }
               }

   verDoGiveTo(actor, io) =
           {
       "%You% can't grab <<self.liquidContentsClasspointer.thedesc>> without spilling it.
               Perhaps %you% should find something to carry it in.";
       }

       verDoSmell( actor ) = {}
       doSmell( actor ) =
       {
       "It smells like <<self.sdesc>>";
       }
;

class liquidcontainer: container
   isfilled = nil
;

class liquid: item
       verDoEmptyOn(actor, io) = { }
       doEmptyOn(actor, io) = { io.ioPourOn(actor, self); }
       verDoEmptyIn(actor, io) = { }
       doEmptyIn(actor, io) = { self.doPutIn(actor, io); }
       verDoEmpty(actor) = { }
       doEmpty(actor) = {
               "(onto the ground)\n";
               self.doPutOn(actor, theFloor);
               }
       doDrop(actor) = {
               self.doPutOn(actor, theFloor);
               }
       tempnoun = ''
       isEquivalent = nil
       ispoison = nil
       verDoTake(actor) =      {
               "\^<<self.thedesc>> would just slip through %your% fingers.";
               }
/**************************************************************
*      the way we've implemented it here, you can dump as much
*      foreign liquids into a liquidreservoir as you want, and
*      they'll just dissolve into the reservoir. doing that does not
*      change the reservoir at all. even if you dump tons of poison
*      into the reservoir, it dissolves safely, no problem. depending on
*      the imagined size of a specific reservoir, this might need to be
*      over-ridden (on a case by case basis.) keep in mind that you'll
*      need to change the behavior of liquids, not the behavior of the
*      reservoir. (one of the magics of ioPutIn = dobj.doPutIn)
**************************************************************/
       doPutIn(actor, io) =
               {
               self.location.isfilled := nil;
               if (isclass(io, liquidreservoir))
                       {
                       if (isclass(self, io.liquidContentsClassPointer))
                               {
                               "%You% pour%s% <<self.thedesc>> back into <<io.thedesc>>.";
                               }
                       else
                               {
                               "%You% pour%s% <<self.thedesc>> from <<self.location.thedesc>> into ";
                               if (firstsc(io.location) = room)
                                       {
                                       io.thedesc;
                                       ". It mixes in ";
                                       }
                               else
                                       {
                                       io.location.thedesc;
                                       ". It mixes with
                                       <<io.liquidContentsClassPointer.thedesc>> ";
                                       }
                               "and slowly dissolves.";
                               }
                       global.liquidlist -= self;
                       delete self;
                       }
               else if (
                               (io.location)
                               and
                               (isclass(io.location, fixeditem) = nil)
                               )
                       {
                       if (io.isIn(actor)=nil)
                               {
                               "(takeing <<io.thedesc>>)\n";
                               io.moveInto(actor);
                               }
                       "%You% pour%s% <<self.thedesc>> from <<self.location.thedesc>> into
                       <<io.thedesc>>. ";
                       self.moveInto(io);
                       }
               else if (io.location)
                       {
                       "%You% pour%s% <<self.thedesc>> from <<self.location.thedesc>> into
                       <<io.thedesc>>. ";
                       self.moveInto(io);
                       }
               else
                       pass doPutIn;   // this won't happen unless your game is wierd
               }

   verDoPutIn(actor, io) =
       {
               local i;
       if (io = nil)
           return;
               else if (self.isIn(actor) = nil)
                       {
                       if (isclass(self.location, liquidcontainer))
                               "%You're% not carrying <<self.location.thedesc>>.";
                       else
                               "%You're% not carrying <<self.thedesc>>.";
                       }
       else if (self.location = io)
               {
           "\^<<self.thedesc>> is already inside <<io.thedesc>>, ace. ";
           }
       else if (io = self)
               {
           "%You% can't pour "; self.thedesc; " in <<self.itselfdesc>>, pal. ";
               }
               else if (
                               ((isclass(io, liquidcontainer)) = nil)  // it can go into
                               and                                                                             // containers.
                               ((isclass(io, liquidreservoir)) = nil)  // allow liquids to
                               )                                                                               // mix into reservoirs
                       {
                       "\^<<io.thedesc>> isn't a suitable container for <<self.sdesc>>. ";
                       }
       else if (io.isIn(self))
           self.circularMessage(io);
               else if (io.isfilled)
                       {
                       for (i := 1; i <= length(io.contents); i++)
                               {
                               if (firstsc(io.contents[i]) = firstsc(self))
                                       {
                                       "%You're% a bit late, pal. There's already <<self.thedesc>> in
                                       <<io.thedesc>>.";
                                       return;
                                       }
                               }
                       "\^<<io.thedesc>> already has another liquid in it. Maybe %you%
                       should empty it first.";
                       }
       else
           self.verifyRemove(actor);
   }

   verDoDrink(actor) = { }
       doDrink(actor) =
               {
               if (self.ispoison)
                       {
                       if (actor = parserGetMe())
                               {
                               (self.location.isfilled := nil);
                               "Hmm, that tasted strange...\nYou sputter and gag for a moment,
                               then roll up into a ball and die a quick but painful death.\n";
                               die();
                               }
                       else
                               {
                               "<<actor.sdesc>> adamantly refuses.";   // you'll change this if it
                               }                                                                               // ever becomes relevant
                       }
               (self.location.isfilled := nil);
               "%You% gulp%s% down <<self.thedesc>>. Aah, refreshing!";
               (global.liquidlist := global.liquidlist - self);
               delete self;
               }

   verDoGiveTo(actor, io) =
   {
       if (isclass(self.location, liquidcontainer))
               {
       "%You% can't grab <<self.thedesc>> without spilling it. ";
               if (isclass(self.location, fixeditem) = nil)
                       {
                       "Perhaps %you% should give <<io.thedesc>> ";
                       self.location.thedesc;
                       ".";
                       }
               else
                       {
                       "Perhaps %you% should find something to carry <<self.thedesc>> in.";
                       }
               }
       else if (not self.location = parserGetMe())
               {
               "%You%'ll have to figure out some way to pick it up first.";
               }
   }
;

toiletbowlwater: liquidreservoir
       location = toilet
       ispoison = true
       noun = 'water'
       sdesc = "water"
       adjective = 'toilet'
       ldesc = "You carefully study the inside of the toilet bowl. It's rather
                       dirty... stinky... hmm, filthy... disgusting...."
       liquidContentsClassPointer = toiletwater
;

class toiletwater: liquid
       ispoison = true
       ldesc = "You study the yellowish-brownish toilet water closely.
                       Evidently, you find it quite interesting."
       location = nil
       noun = 'water'
       adjective = 'brownish' 'yellowish' 'yellowish-brownish' 'dirty' 'brown' 'yellow' 'toilet'
       sdesc = "yellowish-brownish toilet water"
       adesc = "some yellowish-brownish toilet water"
;
/*****************
*              The next few items are examples of liquids
*              and liquidreservoirs.
*              You will find that assigning names, nouns, and
*              adjectives to the liquids and reservoirs is
*              slightly trickier that you might expect.
*              This is an unavoidable (?) result of the disambiguation
*              tricks we're using. If you can read the code well
*              enough to figure out the way that disambiguation
*              works in this game, you should have no problem
*              picking unproblematic names and unambiguous
*              adjectives. If we haven't commented the code
*              well enough for you to understand it, we apologise,
*              and offer these examples in compensation.
*
*              The best advice is, keep it as simple as possible.
*              Dynamic objects are difficult enough to disambiguate
*              already, and making them come from quasi-identical
*              sources (sources that a player will imagine are just
*              bigger versions of the dynamically created object
*              which they come away with) makes disambiguation
*              really tough.
*              So keep the names and adjectives as simple and clear
*              as possible.
*
* we'll load Me with a couple containers, and put the examples
* in the startroom
*/


jug: liquidcontainer                    // must be liquidcontainer. normal
       location = Me                           // containers don't work for liquids.
       sdesc = "jug"
       noun = 'jug'
;

cup: liquidcontainer
       location = Me
       sdesc = "cup"
       noun = 'cup'
;

river: liquidreservoir                  // name and class, same as usual

                                                               // these kinds of things you'll normally
                                                               // want to leave unlisted, so you can
                                                               // talk about them in room descriptions.

       noun = 'river' 'water'          // name of reservoir, AND name of liquid
                                                               // which comes out of reservoir. (MUST
                                                               // have the latter. You might want to
                                                               // eliminate the former, but only if the
                                                               // reservoir has a container. check out
                                                               // the toiletbowlwater for further ideas.
                                                               // if it does have a container, it must
                                                               // be a fixed container, or wierd stuff
                                                               // happens.)

       adjective = 'river'                     // MUST have name of reservoir as adjective.
                                                               // if the reservoir is in a fixedcontainer,
                                                               // this is optional.

       sdesc = "river"                         // keep it as simple as possible. adding
                                                               // words here might confuse the player.

       ldesc = "It is running uphill. How odd."
       location = startroom
       ispoison = nil
       preferredDisambName = 'river water'     // useful ONLY if the reservoir is
                                                                               // _not_ in another object. Lakes,
                                                                               // streams, rivers, etc. get a
                                                                               // preferred disambiguation name.
                                                                               // reservoirs that are inside
                                                                               // containers, like the
                                                                               // toiletbowlwater in this game,
                                                                               // don't use this name, but instead
                                                                               // are disambiguated based on their
                                                                               // location. (e.g., the water that's
                                                                               // in the toilet). You can assign
                                                                               // this name anyway, but it will never
                                                                               // be used by the game. If you fail
                                                                               // to assign a preferred disamb name,
                                                                               // our disambiguator will figure out
                                                                               // something, but it might not be
                                                                               // quite as elegant. (then again, it
                                                                               // might be more elegant. hehe.)
                                                                               // (!) IMPORTANT: (!)
                                                                               // one other thing about this
                                                                               // preferredDisambName: the last
                                                                               // word in the string MUST be the
                                                                               // NOUN of the reservoir.
                                                                               // also, ALL of the other words in
                                                                               // the string MUST be adjectives of
                                                                               // the reservoir. If the noun you
                                                                               // choose is the noun which the
                                                                               // reservoir shares with the liquid,
                                                                               // the adjectives are crucial, and
                                                                               // should be obviously different from
                                                                               // the adjectives for the liquid. (as
                                                                               // in this particular example).

       liquidContentsClassPointer = riverwater         // _MUST_ put the contents
                                                                                               // class here. (the liquid
                                                                                               // you want it to serve.)
                                                                                               // this is what makes the
                                                                                               // fountain work.
;



class riverwater: liquid                                // this object works
                                                                               // basically as a template for
                                                                               // all of the dynamically generated
                                                                               // objects (liquids) which come
                                                                               // out of the corresponding reservoir.
                                                                               // therefore, it is a CLASS, NOT an
                                                                               // object. and it doesn't have a
                                                                               // location.

       noun = 'water'                                          // keep it simple. shares this noun
                                                                               // with its reservoir source (!)

       adjective = 'clear' 'clean'                     // intuitive, simple.
       adesc = "water"
       sdesc = "some water"
       thedesc = "the water"
       ldesc = "It's clean, cool, refreshing. Just the thing after a
                       long adventure! Or a long codeing session!!"
;

stream: liquidreservoir         // another example, basically the same as river
       location = startroom

       ispoison = true                                                 // careful!

       noun = 'stream' 'blood'                                 // must share noun (here, 'blood')
       sdesc = "terrible stream"
       adjective = 'stream'
       preferredDisambName = 'the stream'              // last word is primary noun
                                                                                       // not shared noun. so we're not
                                                                                       // worried about making clear
                                                                                       // adjectives in this string.

       ldesc = "It's a terrible stream of seeping blood."
       liquidContentsClassPointer = blood              // must put the contents class here
;

class blood: liquid
       ispoison = true
       adesc = "some blood"
       noun = 'blood'                                                  // must share noun
       sdesc = "blood"
       adjective = 'succulent'
       ldesc = "Eww. Oozy and yucky. You can almost feel it pulsing!"
;


/*
*       the most interesting example is the toilet, toiletwater, and
*       toiletbowlwater, above. that's somewhat more complex, because the
*       toiletbowlwater (reservoir) is inside a container (namely, the toilet).
*       such containers, as noted above, can't be moveable. (because we didn't
*       implement that).
*       you will notice there that a liquidreservoir CAN have only
*       one noun, as long as it's the same noun as its corresponding
*       liquid. (depends on the specifics of the situation. with the toilet,
*       one noun works best. with a fountain or a well, it probably needs
*       to be named after its container, in addition to sharing a name with
*       its liquid. you'll figure it out when you tinker with it.)
*       perhaps obvious (?): if your liquidreservoir is in a container, you
*       should probably make the container a liquidcontainer. (that way,
*   liquids can
*       be poured into it.)
*********************************************************************/

fillVerb: deepverb
       sdesc = "fill"
       verb = 'fill'
       ioAction( withPrep ) =  'FillWith'
       action( actor ) = 'Fill'
       prepDefault = withPrep
;

emptyVerb : deepverb
       sdesc = "empty"
       verb = 'empty'
       ioAction( onPrep ) = 'EmptyOn'
       ioAction( inPrep ) = 'EmptyIn'
       ioAction( inPrep ) = 'EmptyInto'
       doAction = 'Empty'
;