/*
 * 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
 * versim@hotmail.com
 *
 */

#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'
;

