Roger Firth's IF pages | ![]() |
InFancy -- using Inform objects | ![]() |
![]() |
In previous pages we've developed a bottle and glass, placed liquid in the glass, and implemented a DRINK verb. What we need to finish things off is, of course, some way of starting with the liquid in the bottle, so that we can POUR it into the glass. To complicate matters, the bottle can hold more than the glass, so at times we'll have liquid in two places at once. There's any number of ways of tackling this situation: we'll use the technique of dynamic objects -- of creating and destroying instances of the Liquid class at run-time. For this to work, we must (a) set up the Liquid class appropriately, and (b) issue the necessary run-time messages. Happily, both of these are easy to do.
When we left our Liquid class on the previous page, its definition started out like this:
Class Liquid
with name 'liquid',
add_liquid [ qty;
self.capacity = self.capacity + qty;
if (self.capacity > 0)
return self.capacity;
else
{ remove self; return 0; }
],
description [; print_ret "It looks rather like ", (name) self, "."; ],
|
To make it support dynamic object creation, we've changed it thus:
Class Liquid(1)
with name 'liquid' '.spare' '.spare' '.spare' '.spare' '.spare',
short_name "liquid",
rename [ sn n0 n1 n2 n3 n4 n5;
if (sn) self.short_name = sn;
if (n0) self.&name-->0 = n0;
if (n1) self.&name-->1 = n1;
if (n2) self.&name-->2 = n2;
if (n3) self.&name-->3 = n3;
if (n4) self.&name-->4 = n4;
if (n5) self.&name-->5 = n5;
],
add_liquid [ qty;
self.capacity = self.capacity + qty;
if (self.capacity > 0)
return self.capacity;
else
{ Liquid.destroy(self); return 0; }
],
description [; print_ret "It looks rather like ", (name) self, "."; ],
|
We've made five changes:
If we start the game with all of the liquid in the bottle, then it's natural to make the bottle responsible for creating Liquid instances in the glass. When we last saw it, the bottle looked something like this:
Object bottle "bottle" kitchen_table
with parse_name [ wd adj_count noun_count;
if (parser_action == ##TheSame) return -2;
wd = NextWord();
while (wd == 'green' or 'glass' or 'wine' or 'corked')
{ wd = NextWord(); adj_count++; }
while (wd == 'bottle' or 'flask' or 'flagon')
{ wd = NextWord(); noun_count++; }
if (noun_count > 0) return noun_count + adj_count;
return 0;
],
short_name [;
if (cork in self) print "corked ";
!!rfalse;
],
description [ liq;
liq = ChildOfClass(self,Liquid);
print "You see an ordinary wine bottle, of green glass,
with a faded label and a ";
if (cork in self) print "cork protruding from the ";
print "slender neck";
if (liq ~= 0) print ". There is a quantity of liquid inside";
".";
],
before [;
Uncork: <<Remove cork self>>;
Cork: <<Insert cork self>>;
]
has transparent;
|
So now let's add the ability to pour its contents into a Vessel:
Object bottle "bottle" kitchen_table
with
|
We've enhanced the bottle's before property to trap the EmptyT action; basically, that's EMPTY noun INTO noun. There's a lot happening here -- making sure the bottle is uncorked and not empty, that the destination is a Vessel which isn't already full, and so on -- but the most important lines are highlighted. We send a create() message to the Liquid class, and receive in reply the address of a newly-created instance of the class: a Liquid object. If the reply is zero, a new object couldn't be created, presumably because we're already at our specified limit of concurrent run-time instances. In this example this shouldn't happen, so if it does we just output a debugging message. Assuming that object creation is successful, we then call the new object's rename property to over-write its default short_name and name properties. Finally, more housekeeping: telling the player what's happened, moving the new Liquid object into the Vessel, and adjusting the quantities of Liquid in the bottle and the Vessel.
Once we've primed the bottle by giving it an initial Liquid content, it's time for a test drive:
Kitchen Oddly, there are no exits. In the centre of the room stands a battered pine table. On the table are a corked bottle and an empty glass tumbler. >EXAMINE THE BOTTLE You see an ordinary wine bottle, of green glass, with a faded label and a cork protruding from the slender neck. There is a quantity of liquid inside. >UNCORK IT You pull the cork from the bottle. >POUR IT INTO THE GLASS You pour some liquid into the empty glass tumbler. >TAKE THE GLASS Taken. >INV You are carrying: a glass of red wine (full) a cork >DRINK SOME WINE You take a mouthful of red wine. >POUR THE BOTTLE INTO THE GLASS You add some more liquid to the glass of red wine. >DRINK WINE You take a mouthful of red wine. >DRINK WINE You finish the rest of the red wine. >EMPTY THE BOTTLE INTO THE GLASS You pour some liquid into the empty glass tumbler. >EXAMINE BOTTLE You see an ordinary wine bottle, of green glass, with a faded label and a slender neck. >EXAMINE GLASS The glass tumbler is slightly chipped, but still usable with care. It's currently partly full of red wine. >DRINK WINE You finish the rest of the red wine. >INV You are carrying: an empty glass tumbler a cork |
OK, that's about as far as we can take things here; we've reached the point where the complexities of refining the objects' behaviour would outweigh their educational benefit. The classes and objects that we've created provide a simplistic but usable representation of liquid handling, and could potentially be developed into a full-fledged general package; we'll leave that as an exercise for the reader.
Finally, on the past page, you'll find a listing of the complete game in its 'final' state. Enjoy!