Login
Back to forumReply to this topicGo to last reply

Posted By

Dunric
on 2008-02-20
12:28:44
 Writing text adventures

Writing text adventures
By: Paul Panks (dunric@yahoo.com)

NOTE: This document may be reproduced in any form, for any purpose, provided that this notice and the above title remain fully intact.

Text adventures have a long history stretching back to the early 1970s, when Crowther and Woods developed the original Adventure on a mainframe computer. Soon, a company named Infocom came along and added Zork to the mix. Zork was revolutionary in that the included parser could understand multiple commands on a single line. For example, Zork understood "TURN ON THE LAMP AND GO WEST".

Although fun to play in their own right, there is something to be said about sitting down to write a text adventure. Over at the interactive fiction archive (www.ifarchive.org), there are literally dozens of tools provided for in the creation of new adventure games, many which run on modern platforms like Windows, Linux and the Mac.

Today I will be going over some of the preliminary details of fleshing out a text adventure from scratch. I will start with data structures -- the brains behind the game engine -- and then progress to simple commands such as GO NORTH and GET SWORD. Finally, I will wrap up this article by commenting on future expansion of the adventure game shell provided in this article.

DATA STRUCTURES

Data structures are tables of data which are indexed into arrays, giving the computer something better to work with -- numbers (for computers do far better with numbers than words.)

But the first thing anyone considering writing a text adventure must do is to create some ideas on paper first. An example might be writing down a short introduction to the game and what the ultimate goal of the adventure is. Another good thing to do while fleshing out the storyline is to create a small list of objects that could be useful during gameplay.

As an example, suppose you have the idea to create an adventure whereby the goal is to retrieve a stolen gem from a large, menacing dragon. Your word list might look something like this (along with any object descriptions of the objects):

NORTH (A direction heading north.)
SOUTH (A direction heading south.)
EAST (A direction heading east.)
WEST (A direction heading west.)
UP (A direction heading up.)
DOWN (A direction heading down.)
LANTERN (A brass lantern with blood-stains on it.)
OIL (A flask of oil. Used with the lantern.)
ROPE (A coil of rope about ten feet in length.)
SWORD (A long sword with a ruby hilt.)
RUBY (A gleaming ruby! It shines brightly.)
HELLHOUND (A fiery hellhound from the gates of hell!)
DRAGON (A large, menacing dragon with sharp scales and claws!)

...

and so on. Obviously, you can add more as the game goes along.

The stated goal should be to fill your adventure with as many useful objects as possible. Even in small adventures where you might not have many rooms, it always helps to have objects that can be picked up, used or otherwise manipulated in some way. This not only enhances the adventure experience, but it provides for useful puzzles down the road.

To put these objects into a data array (data structures, as you might recall, are simply tables of data used by the program) you would do the following:

10 NM=13:R=13:VL=8:DIM NO$(NM),EX$(NM),M%(R,6),DE$(R)
15 DIM VB$(VL),LO(NM)
20 FOR X=1 TO NM:READ NO$(X):READ EX$(X):NEXT
1001 DATA"NORTH","(A direction heading north.)
1002 DATA"SOUTH","(A direction heading south.)
1003 DATA"EAST","(A direction heading east.)
1004 DATA"WEST","(A direction heading west.)
1005 DATA"UP","(A direction heading up.)
1006 DATA"DOWN","(A direction heading down.)
1007 DATA"LANTERN","(A brass lantern with blood-stains on it.)
1008 DATA"OIL","(A flask of oil. Used with the lantern.)
1009 DATA"ROPE","(A coil of rope about ten feet in length.)
1010 DATA"SWORD","(A long sword with a ruby hilt.)
1011 DATA"RUBY","(A gleaming ruby! It shines brightly.)
1012 DATA"HELLHOUND","(A fiery hellhound from the gates of hell!)
1013 DATA"DRAGON","(A large, menacing dragon with sharp scales and claws!)

Now that you have a list of objects, it is time to determine a map for the adventure. You need a map for two reasons: first, to be able to move around the adventure from room to room, and secondly, to place objects within these rooms so that the player can interact with them in some meaningful way.

I usually just use the six compass directions of North,South,East,West,Up and Down. Some exclude both the up and the down to use only 4 directions, but I find that including UP and DOWN can be helpful for rooms that require moving up and down flights of stairs, for example.

Let's say you've drawn up a small map as follows:

3-4 9
/ /
1-2 7-8
| |
5-6
|
10-11
|
12-13

In data structure form, your map may look like this:

1999 FOR X=1 TO R:FOR Y=1 TO 6:READ M%(X,Y):NEXT:NEXT
2000 REM N, S, E, W, U, D
2001 DATA 0, 0, 2, 0, 0, 0
2002 DATA 3, 5, 0, 1, 0, 0
2003 DATA 0, 0, 3, 0, 0, 0
2004 DATA 0, 0, 0, 3, 0, 0
2005 DATA 2,10,6, 0, 0, 0
2006 DATA 7, 0, 0, 5, 0, 0
2007 DATA 0, 6, 8, 0, 0, 0
2008 DATA 9, 0, 0, 7, 0, 0
2009 DATA 0, 8, 0, 0, 0, 0
2010 DATA 5, 0, 11, 0, 0, 0
2011 DATA 0, 0, 0, 10, 0, 0
2012 DATA 11, 0, 13, 0, 0, 0
2013 DATA 0, 0, 0, 12, 0, 0

This map is fairly small -- only 13 rooms -- but it can easily be expanded by changing the variable "R" in the DIM statement in line 10.

Now for a quick verb list. Verbs are useful in the game for GOing places and DOing things. Verbs can be as easy as this:

2100 FOR X=1 TO VL:READ VB$(X):NEXT
2101 DATA "GO","GET","DROP","INVENTORY","EXAMINE","READ"
2102 DATA "USE","LIGHT"

This is a sufficient verb list for most small adventures. But remember to add to it as you think of new verbs along the way.

It is always helpful to define where an object will be in the adventure. You can do that as follows:

2500 FOR X=1 TO NM:READ LO(X):NEXT
2501 DATA 99,99,99,99,99,99
2507 DATA 4,4,8,11,1013,10,13

You also need room descriptions for each room. This isn't as hard as it might seem. Just store the results in DE$(x), as follows:

3000 FOR X=1 TO R:READ DE$(X):NEXT
3001 DATA"You are inside a small tavern. A staircase leads upstairs onto a narrow hallway."
3002 DATA"You are standing before a staircase within the tavern. You can can ascend the staircase here."
3003 DATA"You are on a narrow hallway upstairs of the main tavern. A room lies to the east."
3004 DATA"You are inside a small room. There isn't much here save for a small bed. It looks comfortable."
3005 DATA"You are standing outside the tavern by a large fountain. Water pours out into a mottled basin below. A church is east."
3006 DATA"You are standing inside a small church. Pews down the room, while an altar is directly north."
3007 DATA"You are standing in front of a stone altar. Sacrifices are made here to the Gods. A room is east."
3008 DATA"You are inside a small room. Large wooden barrels can be seen near the back wall. A door is north."
3009 DATA"You are walking in a neglected garden overgrown with weeds. No one seems to have cared for it lately."
3010 DATA"You are walking in a forest. A path heads east into a dark cave."
3011 DATA"You are standing inside a dark cave. Very little light can be seen here. It turns south around a bend just below. There are words scratched upon the wall. You also notice a hole here, where you can descend down into the cavern below."
3012 DATA"You are walking in a small tunnel inside the cave. It feels wet here, like water is dripping nearby."
3013 DATA"You have reached an underground pool of water, complete with cascading waterfall. The smell of smoke and ash is quite heavy here!"

We now have most of the data we'll need to make a simple adventure. But how to process this data? A parser is need to read in user imput. Parsers can be easy or they can be complex. A simple one is listed below:

100 v=0:n=0:ne$="":n$="":n2$="":v$="":v2=0:
a$=""r=0t=0:nm=0:bz=0:FOR X=1 TO 10:wd$(x)="":NEXT x
101 INPUT A$t=1:nm=0:D$=A$:FOR a=1 TO LEN(D$)
102 IF MID$(D$, a, 1)=" " THEN A$=MID$(D$,pt,a-pt):
pt=a+1:nm=nm+1:wd$(nm)=A$
103 NEXT a:nm=nm+1:a$=MID$(D$,pt,a-pt):wd$(nm)=A$
104 v$=wd$(1):n$=wd$(2):IF wd$(3)="and" OR
wd$(3)="then" THEN v2$=wd$(4):n2$=wd$(5):co=1
105 IF wd$(3)="in" OR wd$(3)="from" OR wd$(3)="to"
THEN v$=wd$(1):ne$=wd$(2)r=1:bz=1

This parser takes each word in a sentence, breaking them into individual words used by the program. This is the easiest and most efficient way that I know of to do this. An advantage to a parser like this is the ability to "put" objects in other objects and use multiple commands, i.e. "GET LAMP AND GO NORTH".

Now that the parser has been written, we need to extend it slightly by checking words entered against our own object list stored in NO$(x). A way to do this follows:

106 V=0:FOR X=1 TO VL:IF VB$(X)=V$ THEN V=X:X=100
107 NEXT:IF V=0 THEN PRINT"I don't understand your verb.":GOTO 100
108 N=0:FOR X=1 TO NM:IF NO$(X)=N$ THEN N=X:X=100
109 NEXT

Here comes the best part: the verbs and nouns can now branch to certain, specific routines used in the game. We will use the ON x GOTO command below:

115 ON V GOTO 120,130,140,150,160,170,180,190

Listed below are the command subroutines for GO, GET, DROP, INVENTORY,EXAMINE,READ,USE and LIGHT:

119 REM GO
120 IF LT=0 THEN IF RM=11 AND N=2 THEN PRINT"It is much too dark to see!":GOTO 100
121 IF RM=11 AND N=2 OR RM=11 AND N=6 THEN IF LO(9)<>0 THEN PRINT"You need the coil of rope to descend down the cavern.":GOTO 100
122 IF M%(R,N)=0 THEN RM=M%(R,N)RINT DE$(RM):GOTO 100
123 PRINT "You can't go that way!":GOTO 100

129 REM GET
130 IF LO(N)<>0 AND LO(N)<>RM AND LO(N)<>105 AND LO(N)<>205 THEN PRINT"That isn't here.":GOTO 100
131 IF N=12 OR N=13 THEN PRINT"It's beyond your power to do that!":GOTO 100
132 IF IC>5 THEN PRINT "You are carrying too much!":GOTO 100
133 LO(N)=0:IC=IC+1RINT "OK.":GOTO 100

139 REM DROP
140 IF LO(N)<>0 THEN PRINT"You can't drop that.":GOTO 100
141 LO(N)=RM:IC=IC-1RINT"OK.":GOTO 100

149 REM INVENTORY
150 SI=0:FOR X=1 TO NM:IF LO(X)=0 THEN PRINT " ";NO$(X):SI=1
151 IF LO(X)=105 THEN PRINT " ";NO$(X);" (wielded)":SI=1
152 IF LO(X)=205 THEN PRINT " ";NO$(X);" (worn)":SI=1
153 NEXT:IF SI=0 THEN PRINT"Alas, you are empty handed.":GOTO 100
154 GOTO 100

159 REM EXAMINE
160 IF LO(N)<>RM AND LO(N)<>0 AND LO(N)<>105 AND LO(N)<>205 THEN PRINT "That isn't here.":GOTO 100
161 PRINT EX$(N):GOTO 100

169 REM READ
170 IF RM=11 THEN ?"The writing on the wall reads: 'BEWARE THE TERRIBLE DRAGON WITHIN!":GOTO 100
171 PRINT "You see nothing unusual.":GOTO 100

179 REM USE
180 IF LO(N)<>RM AND LO(N)<>0 AND LO(N)<>105 AND LO(N)<>205 THEN PRINT "That isn't here.":GOTO 100
181 IF N=7 THEN PRINT "Please use 'light' instead.":GOTO 100
182 IF N=9 AND RM<>11 THEN PRINT "You can't use the rope here.":GOTO 100
183 IF N=9 AND RM=11 THEN RM=12:?"You use the rope and climb down...":RM=12RINT DE$(RM):GOTO 100
184 PRINT "You can't use that here.":GOTO 100

189 REM LIGHT
190 IF LO(N)<>RM AND LO(N)<>0 AND LO(N)<>105 AND LO(N)<>205 THEN PRINT "That isn't here.":GOTO 100
191 IF N=7 AND LO(8)=0 THEN LT=1RINT "Poof! The lantern is now aflame.":GOTO 100
192 PRINT "You can't light that here.":GOTO 100

Hopefully, this article has given you some idea of how to get started writing your own adventures. An adventure can be big or small, but the main point is that in creating your own you are painting your own universe upon the window of computing!

Paul

Posted By

Lavina
on 2008-02-21
10:09:42
 Re: Writing text adventures

LOL! If you list out my adventures, you will just find almost identical framework. wink

The only difference is that I did not use data matrixes for objectives, I usually handled verbs all separately and programmed all objectives by hand. But this method also has its advantages, even if it is slower.

My most recent command parser is designed for a maximum of 3 words, this is enough I think.



Back to topReply to this topic


Copyright © Plus/4 World Team, 2001-2024