[Main]
[Previous]
[Next]
Making the Items do Something
The game is still rather bland; it has no puzzles. So, let’s introduce a
small puzzle. Let’s assume that the gold skull wasn’t merely left lying
around; instead, whoever left it there arranged for a trap to go off if
it should be lifted off the pedestal. To implement this, we need to add
a method to the goldSkull object. A method is a special type of
property which contains code (i.e. a sequence of one or more statements)
to be executed; it is very much like a function in C or Pascal. The new
goldSkull with the method looks like this:
|
vocabWords = 'gold skull/head'
|
|
"As you lift the skull, a volley of poisonous
|
|
arrows is shot from the walls! You try to dodge
|
|
the arrows, but they take you by surprise!";
|
|
finishGameMsg(ftDeath, [finishOptionUndo]);
|
The method actionDobjTake (which stands for “action Direct
object Take”) is invoked when the player (or any other actor)
tries to take the skull. Here, we’ve simply defined it first to display
a message (since the message is enclosed in double quotes, it is
displayed immediately upon being evaluated), and to then call a special
function called finishGameMsg (the argument ftDeath shows that we want
finishGameMsg to end the game with a YOU HAVE DIED message;
finishOptionUndo offers the player an UNDO option after the death
message).
You should note that we didn’t just pick the name actionDobjTake out of
thin air. The actionDobjTake method in the goldSkull object is called by
TADS 3 when the player types a “take” command with goldSkull as the
direct object. Each verb the player types results in the system calling
particular methods in the object or objects named in the command. The
naming of these methods is described in more detail later in this
guide.
You might wonder why we didn’t need a actionDobjTake method in our
original definition of goldSkull, or you might have assumed that the
system automatically knows what to do if no actionDobjTake is defined
for an object. In fact, all objects do need a actionDobjTake method, and
the system doesn’t automatically know anything about it. However, since
practically every object has the same actionDobjTake, with a few
exceptions such as goldSkull, it would be extremely tedious to type a
actionDobjTake method for every object in the game. Instead, we use
something called “inheritance.” By defining the goldSkull to be a member
of the Thing class, you tell TADS 3 that goldSkull “inherits” all of the
definitions for Thing, in addition to any definitions it makes on its
own. The Thing class, which appears in the adv3 library file included at
the beginning of the program, defines a actionDobjTake method, so
anything that is defined to be a Thing inherits that definition.
However, if something is defined in both Thing and goldSkull, as
actionDobjTake is in this example, the definition in goldSkull takes
precedence - it “overrides” the inherited method.
We actually don’t have a very good puzzle here, because there’s no way
to take the gold skull without dying. So, let’s put a rock on the cave
floor:
|
vocabWords = 'small rock'
|
|
Now, let’s change the actionDobjTake method of the goldSkull.
|
if (location != pedestal || /* am I off the pedestal? */
|
|
|
smallRock.location == pedestal ) /* or is the rock there? */
|
|
|
inherited; /* yes - take as usual */
|
|
|
else /* no - the trap goes off! */
|
|
|
"As you lift the skull, a volley
|
|
|
of poisonous arrows is shot from
|
|
|
the walls! You try to dodge the
|
|
|
arrows, but they take you by surprise!";
|
|
|
finishGameMsg(ftDeath, [finishOptionUndo]);
|
|
This new actionDobjTake first checks to see if the object being taken
(the special object self, which is the object to which the message
actionDobjTake was originally sent), which in this case is the gold
skull, is already off the pedestal; if it is, we don’t want anything to
happen, so we invoke the inherited handling of actionDobjTake. We also
use the inherited handling if the small rock is on the pedestal. When we
invoke the inherited handling, the actionDobjTake method that we
inherit from our parent class (in this case, Thing) is invoked. This
allows us to override a method only under certain special circumstances,
and otherwise do business as usual. If we don’t satisfy one of these two
requirements, the volley of poisonous arrows is released as before.
So, the solution to the puzzle is to put the rock on the pedestal before
taking the skull, thereby fooling the pedestal into thinking the skull
is still there.
As this stands, the player can avoid losing the game, but can’t actually
win it. To finish the game with a winning ending instead of a deadly
one, you can call finishGameMsg with ftVictory instead of ftDeath. If
you want to try experimenting for yourself before going on to the next
chapter, see if you can make the game end in victory either (a) when the
player succeeds in picking up the gold skull, or (b) when he succeeds in
leaving the cave with it. For the latter, try putting your extra code in
the enteringRoom(traveler) method of startroom, and testing for the
condition goldSkull.isIn(gPlayerChar). If you want to be even more
adventurous, try adding another room, say a path through the jungle
leading away from startroom, and make the player win the game when the
player character enters your new room with the skull. However, if you
don’t feel confident enough to try any of this on your own just yet, no
matter, just read on.
This should give you some idea of how a TADS 3 program looks. In the
next chapter, we’ll start to develop a somewhat larger game, starting
once again from the very basics and developing our understanding of how
they’re normally handled in TADS 3, before going on to add items and
puzzles of increasing complexity.
Getting Started in TADS 3
[Main]
[Previous]
[Next]