Skip to main content
? Dave 'Spaz of TLB' Moss

 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.