/* * 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 #include 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 <>"; addliquidlocationadjective (lst[i]); } else if (isclass(lst[i], liquidreservoir)) { if (isclass(lst[i].location, liquidcontainer)) { lst[i].thedesc; " thats in <>"; } else { if (lst[i].preferredDisambName <> 'undefined') "<>"; else "<> from <>"; } 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% <> into <>. Hope you didn't need that!"; self.moveInto(nil); } else if (isclass(io, liquidreservoir)) { "%You% put%s% <> in <>."; 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) = { "\^<> 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[Prep] actually carries out the action of the * verb after it passes its verIo[Prep] and * verDo[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 <> out of <> 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 <> "; "before %you% decide%s% to empty it."; return; } if ((self.isIn(parserGetMe()) = nil) and (isclass(self, fixeditem))) { "%You%'d have to pick up <> 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% <> and empt%ies% its contents onto <>.\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 <> before %you% decide%s% to empty it."; return; } if ((self.isIn(parserGetMe()) = nil) and (isclass(self, fixeditem))) { "%You%'d have to pick up <> 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% <> and empt%ies% its contents into <>.\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) = { "\^<> would just slip through %your% fingers."; } doPutIn(actor, io) = { local contentsClassPointer := new liquidContentsClassPointer; if (io.isIn(actor)=nil) { "(takeing <>)\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% <> into <> and fill%s% it with <>."; } else { "%You% dip%s% <> into <> and fill%s% it with <>. "; } } verDoPutIn(actor, io) = { if (io = nil) return; else if ( ((isclass(io, liquidcontainer)) = nil) and ((isclass(io, liquidreservoir)) = nil) ) { "\^<> isn't a suitable container for <>. "; } else if ((io = self) or (io = self.location)) { "%You% can't pour "; self.thedesc; " in <>, 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 <>.\n"; } else if (isclass(c[i], liquid)) { local liquidInsideIo := c[i]; "\^<> is already filled with <>. %You%'ll have to empty it first."; } } /* optional: else "There's already something inside <>."; */ } else self.verifyRemove(actor); } verDoPourOn(actor, io) = { "If %you% could figure out a way to pick up <>, %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 { "<> refuses.\n"; // if this ever happens, you'll want to } // change the message here anyway } else { "%You% gulp%s% down <>. Aah, refreshing!"; } } verDoGiveTo(actor, io) = { "%You% can't grab <> without spilling it. Perhaps %you% should find something to carry it in."; } verDoSmell( actor ) = {} doSmell( actor ) = { "It smells like <>"; } ; 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) = { "\^<> 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% <> back into <>."; } else { "%You% pour%s% <> from <> into "; if (firstsc(io.location) = room) { io.thedesc; ". It mixes in "; } else { io.location.thedesc; ". It mixes with <> "; } "and slowly dissolves."; } global.liquidlist -= self; delete self; } else if ( (io.location) and (isclass(io.location, fixeditem) = nil) ) { if (io.isIn(actor)=nil) { "(takeing <>)\n"; io.moveInto(actor); } "%You% pour%s% <> from <> into <>. "; self.moveInto(io); } else if (io.location) { "%You% pour%s% <> from <> into <>. "; 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 <>."; else "%You're% not carrying <>."; } else if (self.location = io) { "\^<> is already inside <>, ace. "; } else if (io = self) { "%You% can't pour "; self.thedesc; " in <>, pal. "; } else if ( ((isclass(io, liquidcontainer)) = nil) // it can go into and // containers. ((isclass(io, liquidreservoir)) = nil) // allow liquids to ) // mix into reservoirs { "\^<> isn't a suitable container for <>. "; } 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 <> in <>."; return; } } "\^<> 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 { "<> adamantly refuses."; // you'll change this if it } // ever becomes relevant } (self.location.isfilled := nil); "%You% gulp%s% down <>. Aah, refreshing!"; (global.liquidlist := global.liquidlist - self); delete self; } verDoGiveTo(actor, io) = { if (isclass(self.location, liquidcontainer)) { "%You% can't grab <> without spilling it. "; if (isclass(self.location, fixeditem) = nil) { "Perhaps %you% should give <> "; self.location.thedesc; "."; } else { "Perhaps %you% should find something to carry <> 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' ;