Making Life More Problematic
|
So far the game allows the player to walk from the cottage to the clearing and then climb the tree, but this is not particularly challenging. The time has come to add a puzzle to the game, and one that will turn out to be sufficiently complex to introduce quite a few new elements.
Let us suppose that in order to climb the tree, Heidi first needs to fetch a chair and stand on it. The first thing to do is to prevent Heidi from being able to climb the tree from the ground. To achieve this we need to change the definition of clearing.up. For this purpose we'll use a close relative (in fact the parent) of the FakeConnector, namely the NoTravelMessage. Modify the clearing object so that its up property is now defined as follows:
|
you to reach. "}
|
That was the easy part. The trickier part is creating a chair object that will enable Heidi to climb the tree. The first thing is to create a suitable initial location for it; the most likely place you'd find a chair is probably inside the cottage. For the moment we'll keep things as simple as possible; define the inside of the cottage as follows:
|
"You are in the front parlour of the little cottage. The door out
is to the east. "
out = outsideCottage
east asExit(out)
;
|
Note that since insideCottage is an indoor room, we have defined it to be of class Room rather than class OutsideRoom. To make this room accessible at all we should add the following to the definition of outsideCottage:
|
in = insideCottage
|
west asExit(in)
|
|
The obvious way to fix this on the basis of what we've done before is to add the following to the definition of cottage:
|
dobjFor(Enter) remapTo(TravelVia, outsideCottage.in)
|
|
|
cottage/house/building' 'pretty little cottage'
"It's just the sort of pretty little cottage that townspeople dream of living in,
with roses round the door and a neat little window frame freshly painted in green. "
;
|
An alternative to using the ->connector syntax would have been to define the connector property explicitly with
|
connector = (outsideCottage.in)
|
|
Now that we have somewhere to put the chair, we can start defining it. What we need is something that we can carry around and stand on, but not both at the same time. Moreover, when Heidi is standing on the chair, she'll still be in the location the chair is in. The class of object we need is thus basically what TADS 3 calls a NestedRoom. The TADS 3 library includes a subclass of NestedRoom called Chair that does just the job (by default a Chair can be sat on and stood on but not lain upon):
|
"It's a plain wooden chair. "
;
|
|
|
Now try compiling and rerunning the game. You should find that the chair now behaves just as one would expect: you can sit or stand on it (but not lie on it), you can also take it, but you can't take it while you're sitting or standing on it, and you can't sit or stand on it while you're carrying it.
But, as you will discover, the chair still doesn't help Heidi climb the tree. The problem is that we defined the connector on clearing.up as a NoTravelMessage, which blocks travel under all circumstances. What we need is a connector that allows Heidi to pass only when the chair is at the foot of the tree, i.e. in the clearing. One type of connector appropriate to this task is a OneWayRoomConnector, since this possesses methods to control the conditions under which travel is permitted. We could define it thus:
|
up : OneWayRoomConnector
|
{
|
destination = topOfTree
|
canTravelerPass(traveler) { return chair.isIn(clearing); }
|
{ "The lowest bough is just too high for
you to reach. "; }
|
}
|
|
Before we carry on with refining this, let's digress to another matter. The connector we've just defined is defined on the up property of clearing. This might lead us to suppose that we could have defined a slightly more general version of it by defining:
|
up : OneWayRoomConnector
|
{
|
destination = topOfTree
|
canTravelerPass(traveler) { return chair.isIn(self); }
|
{ "The lowest bough is just too high for
you to reach. "; }
|
}
|
|
|
up : OneWayRoomConnector
|
{
|
destination = topOfTree
|
canTravelerPass(traveler) { return chair.isIn(lexicalParent); }
|
{ "The lowest bough is just too high for
you to reach. "; }
|
}
|
|
|
If you now recompile the game and try it again, you'll find that it now works after a fashion, but that it's less than ideal. There are still several things we should tidy up.
One thing we might like to do is to display a suitable message when the player character climbs off the chair and up the tree, rather than just have Heidi suddenly transported from the chair to the top. There is a TravelMessage class that allows a message to be displayed while traveling, but we have already defined the connector to be a OneWayRoomConnector. Since, however, the TravelMessage class inherits all the methods we have already used, we can simply change OneWayRoomConnector to TravelMessage and add the following property:
|
bough and haul yourself up the tree.<.p>"
|
The connector should now look like this:
|
|
up : TravelMessage
|
{
|
destination = topOfTree
|
canTravelerPass(traveler) { return chair.isIn(lexicalParent); }
|
{ "The lowest bough is just too high for you to reach. "; }
travelDesc = "By standing on the chair you just manage to
reach the lowest bough and haul yourself up the tree.<.p>"
}
Recompile the game and try it again. You will soon encounter another small problem: the game now describes Heidi as using the chair to reach the bough whether she's on the chair or still on the ground when the climb tree or up command is issued. You might think this is okay on the grounds that if the player has made Heidi carry the chair to the clearing he's probably figured why, so we don't need to make Heidi explicitly stand on the chair first, since this step can be taken for granted. Maybe such an argument holds some water, but it is potentially rather leaky, since the chair is still in the clearing even if Heidi is still carrying it, and this code would allow Heidi to use the chair to climb the tree while she's still holding the chair, which surely can't be right. It would be better, then, to check that Heidi is actually on the chair (which she can't be if she's carrying it) before allowing her to climb. We can achieve this by changing the canTravelerPass method to:
|
canTravelerPass(traveler) { return traveler.isIn(chair); }
|
|
Now everything should work reasonably well, except that the game will now allow Heidi to climb the tree from the chair even if she's only sitting on the chair, and not standing on it. Again, we may not think this matters very much in practice, but if we do, there are various ways we could go about fixing it. Perhaps the simplest for now is to add the condition that Heidi must be standing to the canTravelerPass() method, which finally gives us:
|
"A tall sycamore tree stands in the middle of this clearing.
One path winds to the southwest, and another to the north."
southwest = forest
up : TravelMessage
{ ->topOfTree
"By clinging on to the bough you manage to haul yourself
up the tree. "
canTravelerPass(traveler)
{ return traveler.isIn(chair) && traveler.posture==standing; }
explainTravelBarrier(traveler)
{ "The lowest bough is just out of reach. "; }
}
north = forestPath
;
|
|
|
canTravelerPass(traveler) {
|
return traveler.location is in (chair, crate, stepladder) &&
|
&& traveler.posture == standing;
|
}
|
|
|
"The lowest bough of the tree is just a bit too high up for you
to reach from the ground. "
canObjReachContents(obj)
{
if(obj.posture == standing && obj.location == chair)
return true;
return inherited(obj);
}
cannotReachFromOutsideMsg(dest)
{
return 'The bough is just too far from the ground for you to reach. ';
}
;
|
One final point: using one object (like the chair here) to gain access to a connector (like the way up the chair) is a fairly common situation in Interactive Fiction. Often, however, it turns out to be a bit more complicated to implement than the example we have worked throught here. You don't need to worry about that just yet - there's plenty more to do in this guide first - but if when you try to implement something similar in your own game you find TADS 3 doing its best to frustrate you at every turn, you'll also find that help is at hand in the article on 'Using NestedRooms as Staging Locations' in the Technical Manual.
Getting Started in TADS 3
[Main]
[Previous] [Next]