Chapter 7 - Pushing the Boat Out
Let There be Light
This is the last chapter in which we'll try to make things more difficult for poor Heidi. The complication we'll add is quite simple: simply change the class of insideCave from Room to DarkRoom. As you'll find if you now try to pay the cave a visit, Heidi now needs a light source to see what's going on there. The next task, then, is to plant a torch (which American readers may call a flashlight) somewhere. We can't put it inside the cottage, since that would make the game unwinnable (you need to dig up the boots to get to the key to get into the cottage). So instead we'll put the torch/flashlight in a garden shed. We'll also be creating a stream, a jetty, and a shop that Heidi will eventually need to visit in order to buy some batteries for the torch/flashlight.
|
![]() |
You'll also need to put a pair of oars and a torch (without batteries) inside the shed, perhaps placing the latter in a cupboard. There'll need to be an object representing the stream at both locations, and a customised response to cross stream explaining why Heidi can't simply cross it at that point. It would be especially neat if the player got the same response from a north command issued by the stream. You might also want to add some FakeConnectors or NoTravelMessages to explain why Heidi can't go west from the Garden or either east or west from the Jetty.
Your biggest challenge, however, will be to get Heidi from the Cottage Garden to the Jetty, since the map shows no direct connection. The idea is that Heidi gets there by rowing a boat down the stream (hence the oars), so you'll need a boat that Heidi can enter, a means of moving it between the Garden and the Jetty (and back), and a new row verb, which will require Heidi to be sitting in the boat holding the oars before it all works. Maybe the boat will need more than one object, such as an inside boat room that Heidi actually enters and a boat object to represent the boat from outside, just as you may create a shed object to represent the shed from outside.
If you can't manage all this by yourself, not to worry; once you've got as far as you can get, you can read on to see at least one way this can all be implemented.
|
|
"This neat little garden is situated on the north side of the cottage. A
stream runs along the bottom of the garden, while a short path disappears
through a gap in the fence to the southeast, and another leads westwards
down to the road. Next to the fence stands a small garden shed. "
southeast = outsideCottage
north : NoTravelMessage {"<<gardenStream.cannotCrossMsg>>"}
east : NoTravelMessage {"You can't walk through the fence. "}
west : FakeConnector {"That path leads down to the road, and you don't
fancy going near all those nasty, smelly, noisy cars right now. " }
in = insideShed
;
+ Decoration 'wooden fence' 'wooden fence'
"The tall wooden fence runs along the eastern side of the garden, with
a small gap at its southern end. "
;
+ gardenStream: Fixture 'stream' 'stream'
"<<cannotCrossMsg>>"
dobjFor(Cross)
{
verify() {}
check() { failCheck(cannotCrossMsg); }
}
cannotCrossMsg = ' The stream is quite wide at
this point, and too deep to cross. '
;
+ Enterable -> insideShed 'garden shed' 'garden shed'
"It's a small, wooden shed. "
matchNameCommon(origTokens, adjustedTokens)
{
if(adjustedTokens.indexOf('shed'))
return self;
else
return cottageGarden;
}
;
insideShed : Room 'Inside the Garden Shed'
"The inside of the shed is full of garden implements, leaving just about
enough room for one person to stand. An old cupboard stands
in the corner. "
out = cottageGarden
;
+ Decoration 'garden implements/hoe/rake/shears' 'garden implements'
"There's a hoe, a rake, some shears, and several other bits and pieces. "
isPlural = true
;
+ oars : Thing 'pair/oars' 'pair of oars'
"The oars look like they're meant for a small rowing-boat. "
bulk = 10
initSpecialDesc = "A pair of oars leans against the wall. "
;
|
|
The matchNameCommon method is the way we get round this. To quote from the comments in the library source code:
|
'origTokens' is the list of the original input words making up the noun phrase, in canonical tokenizer format. Each element of this list is a sublist representing one token.
|
|
'adjustedTokens' is the "adjusted" token list, which provides more information on how the parser is analyzing the phrase but may not contain the exact original tokens of the command. In the adjusted list, the tokens are represented by pairs of values in the list: the first value of each pair is a string giving the adjusted token text, and the second value of the pair is a property ID giving the part of speech of the parser's interpretation of the phrase. For example, if the noun phrase is "red book", the list might look like ['red', &adjective, 'book', &noun].
|
|
We could have implemented some of this functionality by using a weak token in the definition of the garden shed; we'd do this by enclosing the word 'garden' in parentheses in the list of vocabulary words, i.e.:
|
This would prevent the garden shed from responding to commands that just use the word 'garden' but would not remap such commands to the garden object. This, however, could easily be achieved by adding a vocabWords property to the definition of the cottage garden thus:-
|
vocabWords = '(cottage) garden'
|
|
There's a couple more things we may want to do with the Enterable representing the outside of this garden shed. If the player types open shed or look in shed, the standard library responses may be not just unhelpful but potentially misleading (perhaps suggesting that the shed is only a decoration object):
|
>open shed
|
That is not something you can open.
|
|
>look in shed
|
There's nothing unusual in the garden shed.
|
|
|
'garden shed'
"It's a small, wooden shed. "
dobjFor(LookIn) asDobjFor(Enter)
;
|
|
'old cupboard'
"The cupboard is a battered old wooden thing, with chipped blue and
white paint. "
subContainer : ComplexComponent, OpenableContainer { }
subSurface : ComplexComponent, Surface { }
;
|
The next task is to put objects in and on the cupboard:
|
"It's a small square tin with a lid. "
subLocation = &subSurface
bulkCapacity = 5
;
+++ battery : Thing 'small red battery' 'small red battery'
"It's a small red battery, 1.5v, manufactured by ElectroLeax
and made in the People's Republic of Erewhon. "
bulk = 1
;
++ torch : Flashlight, OpenableContainer 'small blue torch/flashlight'
'small blue torch'
"It's just a small blue torch. "
subLocation = &subContainer
bulkCapacity = 1
;
|
subLocation
must be a property pointer (a property name preceded by &).
We make the torch an OpenableContainer so that we can insert the battery. The behaviour of the torch requires a little thought. By default an object of the Flashlight class will provide light if it's switched on and will stop doing so if it's switched off. This is what we want, with the added complication that it should only be possible to turn the torch on if the battery is in it. A further complication is that if the player insists on removing the battery while the torch is on, it should at once go out again. Here's the definition of the torch with all that extra handling added:
|
"It's just a small blue torch. "
subLocation = &subContainer
bulkCapacity = 1
dobjFor(TurnOn)
{
check()
{
if(! battery.isIn(self))
{
"Nothing happens. ";
exit;
}
}
}
iobjFor(PutIn)
{
check()
{
if(gDobj != battery)
{
"{The dobj/he} doesn't fit in the torch. ";
exit;
}
}
action()
{
inherited;
makeOpen(nil);
achieve.addToScoreOnce(1);
}
}
notifyRemove(obj)
{
if(isOn)
{
"Removing the battery causes the torch to go out. ";
makeOn(nil);
}
}
achieve: Achievement
{ desc = "fitting the battery into the torch" }
;
|
At this point, we need to adjust the original location, first to indicate that there's a path round to the side of the cottage, and second to provide the relevant connection:
|
outsideCottage : OutdoorRoom 'In front of a cottage'
|
"You stand just outside a cottage; the forest stretches east.
|
A short path leads round the cottage to the northwest. " //add this
|
east = forest
|
in = cottageDoor
|
west asExit(in)
|
northwest = cottageGarden // add this
|
;
|
|
Once again, you can now recompile the program and test it all out to check that it still works.
|
|
Getting Started in TADS 3
[Main]
[Previous] [Next]