| /* | |
| * 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 | |
| * | |
| */ | |
| 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' | |
| ; | |
Xet Storage Details
- Size:
- 35.2 kB
- Xet hash:
- cccd8e04c307493d555ebf0f6b4d19453c070c3bd2bba9ff9be46e09bc609009
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.