WRITING YOUR OWN ADVENTURES by Stefan Posthuma
The adventure is one of the most addictive forms of
computergames. Every computer owner must have heard of them
sometimes. Which ST-owner doesn't know 'The Pawn'?
Adventures are so attractive because they are a test of the
players' skill and creativity. The only thing that limits the
capability of an adventure is the imagination of the author. Of
course it is great fun to write your own adventure, because you
really can let go your imagination and create some juicy
location-descriptions and some tricky puzzles.
If you might think it's terribly difficult to write an adventure,
you're wrong. Everybody with a decent knowledge of BASIC can do
the job. An highly-structured programming language is preferable
to write adventures, and there is a BASIC for the ST that is
really great: GfA-BASIC, so all examples in this article have
been written in it.
If you want to write an adventure, the first thing you have to do
is to think of a 'script'. What is the final goal, and in what
can the player achieve this goal?
Dream up puzzles and exotic locations; let your imagination free!
The second step is the creation of a map with all the locations
and the way they are connected. You can do this by taking a piece
of graph-paper and draw the locations as squares, connected with
lines. In this case you can use six directions, north, south,
west, east, up and down. (if you want to make things more
complex, you can also use directions like north-west, south-east
etc.) Number each location and give it a short description like
'small room' or 'cave'. Then make a table of all the objects
used, and in which locations they are found. If there is a puzzle
in a certain location, state it clearly in a list of puzzles.
After this essential part, you can start the actual programming.
The best way to do this is to write a very structured program,
with a routine for everything, like a routine for location-
desriptions, one for command-recognition (the parser), and one
for each verb you use. The basic structure for an adventure is
this:
GOSUB INIT Q=0;quit flag
WHILE Q=0
MOVE=0
GOSUB LOCATIONS
WHILE MOVE=0
GOSUB COMMAND
GOSUB ACTION
WEND
WEND
In the routine INIT, you define all variables and objects used
throughout the program. There is one variable that is very
important: the location-number. We'll call that one 'L'. So if
the story starts in location number one, make L one in this
routine.
Also, you have to define all objects. The best way to do this is,
is to store them in an array. Let's say you have three objects in
your adventure, a hammer, a pair of scissors and a golden key. At
the start of the story, the hammer is in location 1, the scissors
are in location 4 and you carry the key. In this case you'll get
something like this:
DATA "",HAMMER,1,PAIR OF,SCISSORS,4,GOLDEN,KEY,0
O=3 :number of objects
DIM O$(O,1),O(O) FOR T=1 TO O
READ O$(T,0),O$(T,1),O(T)
NEXT T
Maybe you noticed already, the number belonging to the object is
the location in which it can be found, and if the number is zero,
the player carries it. I divided the object names in two, so you
can refer to the golden key, just by using the word 'KEY'
The next routine, LOCATIONS simply prints the description of the
location and the objects that can be found in it.
The first thing you have to do is to see if it is not 'too dark
to see'. So if locations 10 to 20 are set underground and you
need a lamp to be able to see anything, you'll get this:
PROCEDURE LOCATIONS
DARK=0
No=0 ;no movement possible in any direction (I used 'No'
because 'N' is used somewhere else)
S=0
W=0
E=0
U=0
D=0 DR=0 ;no door too
IF L>=10 AND L<=20 AND LAMP=0
PRINT "It's too dark, You can't see a thing!"
DARK=1
ENDIF
ON L GOSUB L1,L2,L3,L4,L5 ect....
IF DARK=0
PRINT
GOSUB OBJECTS
ENDIF
RETURN
The variable 'LAMP' is a flag which is set to one if you have a
lit lamp, else it is set to zero. The variable DARK is for future
use to see if it is dark. (In which case you can do nothing but
move to another location.)
The routines L1, L2, L3 ect. contain the location descriptions
and the movement-variables.
For example if location 1 is a room, to the north lies location 5
and to the west location 3:
PROCEDURE L1
PRINT "You are in a dimly lit room. Everything is covered in a
thick layer of dust. A fat brown rat scurries away at the sound
of your footsteps and a shutter schrieks in the wind..... There
is a door to the north and a dark, uninviting corridor to the
west."
N=5 ;north lies location 5
W=3 ;and west location 3
RETURN
The variables N,S,W,E,U and D indicate the possible exits to
various locations, if a variable is zero, no exit in that
direction. Remember, if a location is dark, first check if you
can see anything (DARK=1)
The routine 'OBJECTS' prints a list of all objects present. It
could go like this:
PROCEDURE OBJECTS
FOR T=1 TO O
IF O(T)=L
PRINT "There is a ";O$(T,0);" ";O$(T,1);" here."
ENDIF
NEXT T
RETURN
The next routine, the one that asks for a command and evaluates
it, can be quite complex and is called a parser. If you have ever
played 'the pawn' or and Infocom adventure you will know that
these parsers accept commands like 'Open the brown bag then take
the bottle from the bag. Open the bottle and drink the water.'
In our adventure, we'll stick to the basic form, a verb-noun
parser. Here you can enter commands like 'GO NORTH' or 'TAKE
KEY'. Here is the example:
PROCEDURE COMMAND
REPEAT
LINE INPUT C$
PRINT
IF C$=""
PRINT "HUH?"
PRINT
ENDIF
UNTIL C$<>""
C$=UPPER$(C$)
Sp=INSTR(C$," ") ;find a space between verb and noun.
IF Sp=0
GOSUB SINGLE ;you entered a single command
ELSE
V$=LEFT$(C$,Sp-1) ;store the verb in V$
N$=RIGHT$(C$,LEN(C$)-Sp) ;store the noun in N$
GOSUB INTERPRET
ENDIF
RETURN
The procedure 'SINGLE' is just a long list of IF....ENDIF
statements e.g.:
PROCEDURE SINGLE
FLAG=0
IF C$="QUIT" OR C$="STOP" OR C$="END"
GOSUB QUIT
FLAG=1
ENDIF
IF C$="INVENTORY" OR C$="INV" OR C$="I"
GOSUB INV
FLAG=1
ENDIF
IF C$="LOOK" OR C$="L"
GOSUB LOCATIONS
FLAG=1
ENDIF
and so on.....
IF FLAG=0
PRINT "I'm not sure I can follow you.."
ENDIF
RETURN
I'll also give you the procedure INV. This routine prints a list
of all ojects you carry.
PROCEDURE INV
PRINT "You are carrying:"
FLAG=0
FOR T=1 TO O
IF O(T)=0
PRINT "A ";O$(T,0);" ";O$(T,1)
FLAG=1
ENDIF
NEXT T
IF FLAG=0
PRINT "Absolutely nothing!"
ENDIF
RETURN
The procedure 'INTERPRET' is a little more complex. First, you
have to check if the noun exists and if it is present if it is an
object. After that, you have to check the verb, and handle
accordingly. Just like you made a list of objects, you also have
to make a list of verbs and recognised words. (In DATA-
statements; label them clearly)
PROCEDURE INTERPRET
RESTORE WORDS ;I used the label 'WORDS'
FLAG=0
X=0 ;counter
N=0 ;noun number
O1=0 ;object chosen flag
DO
READ W$
INC X
EXIT IF W$="endwords" ;use 'endwords' as last word in the
list
IF N$=W$
FLAG=1 ;word recognised
N=X ;noun number
ENDIF
LOOP
IF FLAG=0 ;not a word
FOR T=1 TO O
IF N$=O$(T,1) OR N$=O$(T,0)+" "+O$(T,1)
FLAG=1
O1=1 ;object chosen
N=T ;object number
ENDIF
NEXT T
ENDIF
IF FLAG=0 ;unknown word
PRINT "I'm sorry, but the word ";N$;" is not in my
vocabulary"
ELSE IF O1=1 ;check if the object is present
IF O(N)<>0 AND O(N)<>L
PRINT "I can't see any ";N$
N=0
ENDIF
ENDIF
ENDIF
RESTORE VERBS ;I used the label 'VERBS'
FLAG=0
X=0
DO
READ W$
INC X
EXIT IF W$="endverbs" ;use 'endverbs' as last verb
IF W$=V$
FLAG=1 ;verb recognised
V=X ;verbnumber
ENDIF
LOOP
IF FLAG=0
PRINT "I don't know how to ";V$
ELSE
IF N<>0 ;verb and noun recognised
IF V<>6 AND V<>7 AND DARK=1 ;is it dark and you do not
move?
PRINT "It is really hard to ";V$;" something in
the dark." ELSE
ON V GOSUB GET,GET,DROP,EXAM,EXAM,GO,GO,EAT
ENDIF
ENDIF
ENDIF
RETURN
In this case, your verb list would be this:
VERBS:
DATA GET,TAKE,DROP,EXAMINE,STUDY,GO,WALK,EAT
DATA endwords
The verbs 'GET and TAKE', 'EXAMINE and STUDY', 'GO and WALK'
refer to the same procedure because they are synonyms. Handling
verbs:
PROCEDURE GET ;'N' is holds object number chosen.
IF O1=0 ;Is it an object?
PRINT "You can't take that!"
ELSE
IF O(N)=0
PRINT "You already have it!"
ELSE
IF I=5 ;'I' counts the number of object you have
PRINT "You are carrying too much!" ELSE
PRINT "OK!"
O(N)=0 ;You now carry the object
INC I
ENDIF
ENDIF
ENDIF
RETURN
PROCEDURE DROP
IF O1=0 ;is it an object?
PRINT "You're talking complete nonsense here."
ELSE
IF O(N)<>0
PRINT "You don't have it!"
ELSE
PRINT "Dropped."
O(N)=L ;put in location number 'L' (The current location)
DEC I ;you carry one object less.
ENDIF
ENDIF
RETURN
The procedure 'EXAM' can be very large, since the player must be
able to examine a lot of things. The best way to do it is like
this: first, check in N$ is an object. (O1 contains 1). If it's
not, jump to a different subroutine for every location. In these
subroutines, check N$ for things you can examine and print the
description. (A long IF.....ENDIF list.) Check the sample program
if you don't understand this.
The procedure 'GO' is quite simple. For example, if the player
wants to GO NORTH, check if N equals zero. In that case, no
movement north is possible. In the other case, make 'L' the same
as 'N' and the player has moved to a new location. The sample
contains only movement NORTH and UP, but the procedure is the
same for the other directions.
PROCEDURE GO
N$=LEFT$(N$,1)
IF N$="N"
IF N=0 PRINT "You can't go that way!"
ELSE
L=N
MOVE=1 ;flag for main loop.
ENDIF
ENDIF
IF N$="U"
IF U=0
PRINT "You can't fly!"
ELSE
L=U
MOVE=1
ENDIF
ENDIF
ect...
IF N$<>"N" AND N$<>"S" AND N$<>"W" AND N$<>"E" AND N$<>"U" AND
N$<>"D"
PRINT "You can't go there!"
ENDIF
RETURN
Be sure to add the words 'NORTH', 'SOUTH', 'WEST' ect. to your
wordlist.
The movement commands are used a lot, and it would be handy if
the player could input 'N' instead of 'GO NORTH'. To do this, add
this to the 'SINGLE' routine:
IF C$="N" OR C$="S" OR C$="W" OR C$="E" OR C$="U" OR C$="D"
N$=C$
GOSUB GO
ENDIF
The procedure 'EAT' is one of the many. If you want to write a
good adventure, inlcude a lot of verbs, and make a large
vocabulary. The following procedure is merely an example, and can
be totally different in your adventure.
PROCEDURE EAT
IF O1=0 OR N<>3 ;object numer 3 is a bread or something like
that.
PRINT "You have to be out of your mind!"
ELSE
PRINT "It tasted really good until you almost broke
your teeth on a file that was in it."
O(4)=L ;object number four is the file O(3)=-1
;the bread 'disappears'
ENDIF
RETURN
Handling doors:
Almost every adventure has at least one door. Doors are difficult
things in an adventure program. You have to be able to open,
close, lock, unlock, and examine them. Doors often form puzzles,
because you need a certain key to open them or you have to break
them, or simply knock on them.
I handle doors like this:
I store all information about them in an array; D. Initialise and
fill this array in the procedure INIT. A door can have 3 modes:
0 : open 1 : closed
2 : closed & locked
Let's say there is a door to the north, and if you go through the
door, you enter lokation number 6. Instead of making N 6, make it
-6. Then define another variable, called 'DR'. This one contains
the number of the door present. In the procedure GO, check if a
direction is negative, if it is, check if the door is open. I
think it will become clear if you study the example program.
The procedure ACTION, is a procedure in which you count the
number of moves made, check if the player is still healthy, check
if the oil supply of the lamp isn't running out, check if that
fire-breathing dragon didn't wake up to tear you to shreds, and
lots more. It all depends on your story.
Advanced parsers
I am writing an adventure too, and I am trying to develop a good
parser. This means that it has to recognise multiple commands in
one line, and things like this: TAKE ALL BUT THE LAMP AND THE
SWORD FROM THE LARGE BOX THEN KILL THE DWARF WITH THE KNIFE.
First, split up the multiple commands. They are separated by THEN
or by full stops. Put them in an array and handle them one by
one. Of course, eliminate all 'THE's because you don't need them.
So reduce 'TAKE THE LAMP' to 'TAKE LAMP'. But there is more,
things like 'TAKE ALL BUT... are very difficult, but somehow I
managed to do it. Maybe if I feel like it, I'll write an article
about it some time.
On the disk you will find a very small adventure game that brings
all examples into practice. Of course this is not a full-grown
adventure, I kept it very basic and simple, but it is just meant
for you to figure out how an adventure program works. The object
of the game: escape from the House!
Well, that concludes this article. I hope you are inspired enough
to start writing. Of course, think carefully about it, and put it
all on paper before you start programming. Also document your
program properly; it's hard to read a very long program without
any leads, even if it's your own.
If you have a problem, contact me through the correspondence
address of ST NEWS. Don't forget to enclose two stamps: One for
the editor to send it through to me and one for me so I can
respond to your letter!
About the author:
Stefan Posthuma started programming computers back on the VIC 20
from Commodore, later on the Commodore 64. On the latter machine,
he competed in the Micro Masters Holland competition with a game
called "Hades". Later, he programmed "Scuttle" (with the music
from Jean Michel Jarre) that was marketed by Aackosoft. He won the
Micro Masters Holland and won a Philips YES PC. He felt he didn't
have enough software, so he swapped the machine against a 260 ST.
Now, he is proud to be in the ever increasing ST legions.
Disclaimer
The text of the articles is identical to the originals like they appeared
in old ST NEWS issues. Please take into consideration that the author(s)
was (were) a lot younger and less responsible back then. So bad jokes,
bad English, youthful arrogance, insults, bravura, over-crediting and
tastelessness should be taken with at least a grain of salt. Any contact
and/or payment information, as well as deadlines/release dates of any
kind should be regarded as outdated. Due to the fact that these pages are
not actually contained in an Atari executable here, references to scroll
texts, featured demo screens and hidden articles may also be irrelevant.