Adventure is a computerised version of the game Dungeons and Dragons, both of which are enormously popular. In Dungeons and Dragons a player is assigned the role of dungeon master, devising the dungeons which the other players explore in an attempt to acquire the treasures hidden there, often after having fought their way past monsters of various kinds.
Each player announces his action to the dungeon master who then tells him of the outcome, usually after spinning various dice to generate a random element.
Adventure, run on PDP-11 systems everywhere, is similar to Dungeons and Dragons except that the dungeons are devised by the original programmer and the computer then assumes the role of dungeon master.
Somewhere nearby is Colossal Cave, where others have found fortunes in treasure and gold, though it is rumoured that some who enter are never seen again. Magic is said to work in the cave. I will be your eyes and hands. Direct me with commands of one or two words. You are standing at the end of a road before a small brick building. Around you is a forest. A small stream flows out of the building and down a gully. ENTER BUILDING You are inside a building. A well house for a large spring. There are some keys on the ground here. There is a shiny brass lamp nearby. There is food here. There is a bottle of water here. GET KEYS OK GET LAMP OK RUB LAMP Rubbing an electric lamp is not particularly rewarding. Anyway, nothing exciting happens. EXIT You're at the end of the road again. Go SOUTH You are in a valley in the forest beside a stream tumbling along a rocky bed GO SOUTH You are in a 20-ft. depression floored with bare dirt. Set into the dirt is a strong steel grate mounted in concrete. The grate is locked UNLOCK GRATE The grate is unlocked.Once having opened the grate, for which he must have the keys, the player then has access to Colossal Cave where there are problems to solve and treasures to gather. However, if he does not have the keys, there is no way that the grate can be opened. In fact, it may take him, a while to find the entrance as it is all too easy to become lost in the forest.
As you can see from the example, playing Adventure is rather like reading a novel, with one important difference. Instead of following the story passively, the reader is involved actively, deciding what is the best action to take in a given situation, often having to think very carefully as the wrong decision may lead to death.
That affinity with a novel is Adventure's main disadvantage. Once all the problems have been solved, which may take several weeks, interest wanes and another Adventure is required.
The original version of Adventure, programmed by Will Crowther at Stanford Research Institute, is coded in Fortran, requires 64Kbytes of memory, disc back-up and is very difficult to modify to generate new games as many of its features are buried deep within the program code. That explains the current shortage of Adventures.
A better solution would be to have a general Adventure program driven by a separate database allowing new games to be generated without having to overcome the programming complexities every time. In fact, that approach was used by Scott Adams who has now produced a number of excellent adventures for some of the more popular systems such as the TRS-8O and Sorcerer.
The program described here carries this concept one step further. Instead of one person producing adventures for a limited range of systems the idea is to describe a program which can be implemented on almost any system and driven by an entirely separate and machine-independent database. That allows owners of the program to write adventures in a simple form and swap games with someone who may have an entirely different processor.
1. The vocabulary of words recognised in the game. 2. The objects that may be manipulated 3. The places that may be visited 4. The actions performed by specific words.All that is required to produce the database and the program is an assembler and examples of various table entries are shown for a Z-8O type assembler.
The vocabulary is held as the first four letters of a word followed by an identifying code. That permits the program to reduce words to simple numbers which are much easier to manipulate. It also allows different words to have the same code and hence the same meaning.
VOCAB: DEFM 'NORT' ;Word "NORTH" DEFB 1 ;Identifying code "1" DEFM 'EAST' DEFB 2 ;EAST has code "2"DEFM is the instruction to define an ASCII string and DEFB is to define a byte. The table has the name "VOCAB" and terminated by a byte of 0FFH (255 or -1). The words for movement - north, south, etc. - must have codes in the range 1 to 12 as the program prints the message - I cannot go in that direction - if it cannot find anything to do with words in that range. Other unmatched words generate the simpler response: I can't.
Objects are anything which may be moved from one place to another and/or transformed from one thing to another. A lamp, for example, may be carried with the player and it may be transformed from a "LIT LAMP" to an 'UNLIT LAMP" and, of course, back again.
Each object has an entry in each of two tables: the object location table which records the current position of the object and the object description table which contains the text used to describe the object.
The current location table is named "OBJLOC" and the descriptive text table "OBJTXT". OBJLO is terminated by a byte of OFFH. OBJTXT needs no termination.
OBJLOC: DEFB 3,0 ;Object 0 at location 3 DEFB 5,0 ;Object 1 at locations 5 ;Similarly for other objects OBJTXT: DEFW M0 ;Address of text for object 0 DEFW M1 ;Address of text for object 1 M0: DEFM 'A little axe' ;Description of object 0 DEFB 80H ;String terminator Ml: DEFM 'A bunch of keys' DEFB 80HNote that the object position information is two bytes to allow it to be at a location - first byte is 0-225 - or in some special place, such as carried by the player - second byte is used. Also, the object description table OBJTXT contains the address of the actual description for each object.
The locations are the places that the player may visit. They may be rooms, caves or anything desired by the Adventure writer. Each location has an entry in two tables: The description of the location and the list of directions the player may go from there. The location descriptions are held in a table named LOCTXT and the possible movements in MOVEMT. Both of those tables consist of pointers to the actual data as described for the object descriptions above.
MOVEMT: DEFW D0 ;Pointer to location 0 moves DEFW D1 ;Pointer to location 1 moves ;etc. for rest of locationsThe following example movement shows an entry that says that word 0 takes us to location 1 and word 3 will take us to location 5. Note that a -1 terminates the list.
D0: DEFB 0,1,3,5,-1 LOCTXT: DEFW L0 ;Pointer to descriptions DEFW L1 L0: DEFM 'I am in an empty room DEFB 80H L1: DEFM 'I am by a stream' DEFB 80HThe action table is the section of the database interpreted or executed by the main program. It consists of word, conditions and actions performed. If there is an entry in the table for the words entered by the user and the conditions specified are met, the actions are performed.
For example, he may be in the same room as a Vampire without a crucifix so the computer may make the Vampire attack. The user action table is named EVENT and the computer's table STATUS. Both have the same format:
EVENT: DEFB 0,1 ;words 0 and 1 DEFW C0 ;Pointer to conditions DEFW A0 ;Pointer to actions DEFB 3,-1 ;Only word 3 required DEFW C1 ;Pointers DEFW Al C0: DEFB 0,1,1,2,-1 ;Must be at location 1 (0,1) ;Object 2 must be here(1,2) A0: DEFB 5,3,-1 ;Print message 3The lists of actions and conditions are terminated by a byte of 0FFH (-1). Note that the examples are only very small extracts from a real table. A full-size database may have up to 255 locations and any number of entries in the event and object tables.
Assembly listings for every processor would occupy far more space than the magazine can provide and flowcharts would not really describe the action of the program at the level of detail we want. For those reasons, the program is represented in pseudo-code.
For those not familiar with the term, pseudo-code is a non-existent language or shorthand representation of a program often used by programmers for detailed design when the actual target language is not yet known. Pseudo-code provides far more detail than flowcharts and is, in fact, detailed instructions for the actual coding of the program.
Although the listing should be more or less self-explanatory, it's worth mentioning two conventions used. If a variable is preceded by a "@" it means that the variable is used as a pointer to the data. For example, if "HL" contains the value 100 and we say "A=@HL", "A" is loaded with the contents of memory location 100.
A Similar convention is used to identify the address of a variable except the "#" character is used - also used by the IF statement for not equal to. For example, if we say "HL=#OBJLOC", it means that the variable HL is loaded with the address of OBJLOC and not the contents.
Variables used are defined as either BYTE, 8-bit, or WORD, 16-bit, and the contents are assumed to be set to zero unless a value is included between two 'P's. For example, to define two 8-bit variables in memory, one set to zero and the other to 3 we use:
BYTE VARA,VARB/3/A memory block is reserved by:
BYTE VARC/<7>/which means reserve seven bytes of memory starting at label "VARC".
That leads to the implementation of arrays used by the program. All references to arrays mean an offset to the base label of a memory area. For example, VARC(3) simply means the address found by adding three to the value of label VARC.
Thus VARC(0) is exactly equivalent to VARC. Remember that words occupy two bytes, so, if VARC was a word array, VARC(3) would actually be addressing VARC + 6 and also VARC + 7 for the top byte.
Knowing this, you should now be able to produce a version of the program for your particular system by working through the listing and generating the appropriate assembly code for your machine. If you have access to a medium-level language; such as PL/M for example, that is of course, equally acceptable.
The pseudo-code program shown here has in fact been compiled by a specially-written compiler to ensure that it is sound.
Let us work through the program considering what makes it tick and explaining the meaning of the pseudo-code representation.
Referring to the listing, we can see that the first section is simply the definition of items not within this listing, that is the items marked "GLOBAL". Four subroutines not described here are called, but as these are relatively simple entities they should present no problem in coding.
The first subroutine required is called "$REPLY" and it is simply a routine to read a response from the user and return a value of one if it was a "Y", and a value of zero if it was a "N". The routine should check that either a "Y" or a "N" was entered and prompt "PLEASE ANSWER YES OR NO" for any other reply.
The second routine is named $MESS and is the routine used to print messages on the console. It must take the ADDRESS of a message as a parameter and print all the bytes found there until a byte with the most significant bit set is encountered. The routine used also had the additional feature that it printed a return/line feed if it was called with an address of zero.
The next routine, $LINE, is the opposite of $MESS; it obtains a line from the user and passes back the address of the stored text.
It can be done by reading the refresh register if you have a Z-80 system, or if your keyboard is software controlled, you may increment a counter in, the keyboard - wait loop and use that value as the random number. If you want a more elegant solution, the random numbers used in the prototype program were generated using the algorithm:
[Generated number] = 11x[Last generated number] + 999 MOD 101although this does require 16-bit multiply and divide.
The next group of globals refer to the addresses of the various tables in the database.
A further point is that some items are used for temporary storage only and may be replaced by the processor registers if you desire. The only variables that must be in memory are Here, the current location and User, the variables the data- base may access. If you run out of registers, remember they may be saved on the stack while a register is used for something else.
Proceeding to the code, we can see that the program begins at label "Start" which simply sets the first location to zero. The code beginning at "Desc" describes the current location by printing the description found adding the contents of 'HERE' to the base address LOCTXT and using the pointer there
The current location will, of course, change as the game progresses. A small piece of code checks to see if the database has set user flag zero and if so, we are in dark locations and object zero must be present (a lamp) to obtain the location description. Otherwise the message "Everything is dark. I cannot see" is displayed.
The code then goes onto scan through the object location table "Objloc" and if any objects position is the same as the current position "Here", the object description is printed.
Next, the program looks quickly as the status table which is effectively the computer's turn at the game. However, as the same mechanism which decodes the player's command is used, we will consider it later. That function, when completed returns to the label "PROC".
The routine that obtains a line from the user is called ($LINE) and the address of the entered text obtained. The routine used returned the address on the top of the stack and the instruction "HL = @ SP" finds that address.
That reduction of the line allows complex sentences like "TURN ON THE LAMP" to be reduced to simpler entities like "ON LAMP", provided the words TURN and THE are not in the vocabulary. Hence it is important to consider carefully which words are not in the vocabulary as well as which ones are.
If none of the entered words is found in the vocabulary, the message "I don't understand" is printed and we go and obtain another line from the player.
After we have converted the user's command into one or two single-byte codes, we take the first code and see if it is one of the words which cause movement at the location. If it is the current position (HERE) is updated and we return to label "MOVED" to describe the new place. If it is not, we proceed to examine the main event table to see if there is an entry there.
If the first word code '"W1"' matches the first byte of an entry and "W2" matches the second byte, we proceed to extract the conditions and test them. If all the conditions are satisfied, we extract the list of actions and execute them.
If all the conditions do not match or the two-word codes do not match, we try the next entry in the table. That is repeated until an explicit command to leave the table is given-or the table is exhausted.
The action or condition is decoded by using it as an index into a list of addresses for the function we want and simply moving the address to the program counter (PC). It can usually be done on most machines by pushing the address on to the stack and executing a return from subroutine instruction.
The comments in the program listing explain the operation in greater detail and indicate what actions and conditions are available.
Looking at some examples of a database should further clarify the operation of the program. To make the database more readable, the example extracts shown below were produced using a macro assembler and calling various macros to make the entries in the appropriate table.
VOCAB:: TABLE <SOUT> ,1 TABLE <S> ,1 TABLE <EAST> ,2 TABLE <E> ,2 TABLE <WEST> ,3 TABLE <W> ,3 TABLE <NE> ,4 TABLE <NW> ,5 TABLE <SE> ,6 TABLE <SW> ,7 TABLE <UP> ,8 TABLE <U> ,8 TABLE <DOWN> ,9 TABLE <D> ,9 TABLE <NORT> ,12 TABLE <N> ,12 TABLE <END> ,13 TABLE <TOP> ,13 TABLE <QUIT> ,13 TABLE <ABOR> ,13In the objects shown here, note how items which can change state are two objects although only one of the pair may exist at any given time.
OBJECT 0, <0,8>, <A lit lamp> OBJECT 1, <S7,0>, <An old oil lamp> OBJECT 2, <S5,0>, <A small cloth bag> OBJECT 3, <S5A,0>, <A bottle of holy water> OBJECT 4, <0,8>, <An empty bottle> OBJECT 5, <0,8>, <A match> OBJECT 6, <0,8>, <A spent match>The first byte of the location information is used to mark the location of the object. If the second byte is non-zero, the object is at one of the special places. These are:
2 - Object is carried [512] 4 - Object is worn [1024] 8 - Object does not exist (yet) [2048]The value in "[]" indicates the number obtained when the two bytes are considered as a single 16-bit word.
LOC S0, <HELP,S1,BEGI,S2> TXT <Welcome to Adventure!> TXT <If you know what to do type BEGIN otherwise type HELP> LOC S1, <BEGI,S2> TXT <I have managed to get myself lost in the forest on my> TXT <quest for the seven golden keys of Waydor and don't know> TXT <what to do next. So it is up to you to help me.> TXT <> TXT <Give me your instructions and I will obey. For example,> TXT <if you want me to go to the north. Type "Go NORTH", if> TXT <we should come across some keys and you want me to get> TXT <them, type "GET THE KEYS".> TXT <Some other words that you may find useful are:> TXT <INVENTORY to find out what I'm carrying> TXT <QUIT to give up.> TXT <> TXT <Type "BEGIN" when you are ready to start.> LOC S2, <S,S4,PATH,S4> TXT <I am in a clearing in a very dense forest.> TXT <There is a path leading off to the south.> LOC S5, <N,S2,E,S5,W,S6> TXT <I am at a "T" junction with exits to the north, west and east> LOC S5, <W,S4,EXIT,S4,E,S5A,ALTA,S5A> TXT <I am amongst the ruins of a church. At the far end there> TXT <are the remains of an altar. The exit is to the west.> LOC S5A, <EXIT,S5,W,S5> TXT <I'm beside the altar.> LOC S6, <E,S4,IN,S7,CRYP,S7> TXT <I'm outside the entrance of a crypt.> LOC S7, <EXIT,S6,DOOR,S6> TXT <I'm in a vaulted chamber. Thick cobwebs hide the ceiling. TXT <There is an empty coffin in the corner and a passage leading> TXT <off into darkness to the north.> LOC S8, <D,S9,STEP,S9> TXT <I'm at the top of a steep flight of steps. I can see a> TXT <dim light to the south.>The event table is the real heart of the database as it contains the actions performed by each command from the user. This section also contains the various messages which may appear under database control.
EVT 2 <N,-1> <0,S7,-l> <9,0,8,S8,6> EVT 3 <S,-1> <0,S8,-1> <10,0,8,S7,6> EVT 4 <GET,LAMP> <1,0,-1> <2,0,13> EVT 5 <GET,LAMP> <1,1,-1> <2,1,13> EVT 6 <DROP,LAMP> <1,0,-1> <3,0,13> EVT 7 <DROP,LAMP> <1,1,-1> <3,1,13> EVT 8 <LIGH,LAMP> <1,1,1,5,-1> <11,0,11,5,5,11,18,-1> EVT 9 <OFF,LAMP> <1,0,-1> <11,0,13> EVT 10 <LIGH,LAMP> <1,1,-1> <5,14,-1> Status: EVT A <-1,-1> <7,5,5,0,2,10,-l> <5,7,15,2,8,9,5,-1> EVT B <-1,-1> <6,2,1-1> <5,8,12> EVT C <-1,-1> <5,2,-1> <5,5,-1> EVT Z <-1,-1> -1 7 MSG 5,<I feel sick and dizzy!> MSG 7,<Some one has lept out of the shadows and BITTEN MY NECK!!!!> TXT <He vanished as suddenly as he appeared!> MSG 8,<Everything is getting dark! I Think I'm dy...> MSG 11,<I have lit the lamp with the-match which has now burned out> MSG 14,<1 don't have anything to light it with.>It is worthwhile examining some of the entries in the table in detail to show just what can be accomplished in the database. For example in the location S7 shown, there is a passage leading north, but there is no entry in the movement list for it.
That is because rooms past there are dark and we want to tell the program. So let us look at entry 2. The word codes which must match are "N" (north) and anything will do for the second. There is a single condition, namely that he must be at location S7. If that is so, actions are performed which are: 9,0 - Set flag zero; 8,S8 - Go to location S8; 6 - Describe the location and obtain another command from the player.
An informative message is also printed. The final command (18) aborts the scanning of the table as a little later in the table, there is an entry for LIGHT LAMP when no match is present - which gives message 14 - and we do not want to fall through to it if we have already lit the lamp.
The table is terminated by a word code of zero. Note that in the example the words GET, DROP etc., are shown but in a real table the word code is used.
The entries in the STATUS table show an example of how a "wandering monster" may be implemented. The conditions are: Flag 5 must be zero; he must be in "dark" locations and 10 percent probability will generate the actions. The actions are: print message 7; store 8 in flag 2 - counted-down by the program - and set flag 5 to prevent more vampires.
The program should fit in less than 4K but you will find that the descriptive text, particularly for locations, will eat memory.
In terms of software, all you need are an editor and an assembler. However, if you have access to a disc-based system, all the better. Perhaps the best way to go about it is through your local computer club working on the program as a team and generating your own adventures.
As the database is pure data, any database will run on any machine - providing there is enough memory. However, the program still needs to know the position of the tables in memory.
ORG 2000W ;start of program LOCTXT EQU 200H ;Define table address VOCAB EQU 1000H ;ETC for rest of tables OBJLOC EQU 50HAnother approach would be to make the tables of a fixed length and define specific addresses for them. That removes the need to re-assemble the program for each database but does not use memory very efficiently.
Unfortunately, Adventure is not the kind of game you can describe in such a way that the program can be blindly copied and played. However, I hope that the description given here will allow anyone to implement it on his system. If you're wondering if it is worth the effort, ask anyone who has played before.
! ************************************************ ! * ADVENTURE * ! * * ! * Programmed by - K Reed * ! * Date - 12-May 80 * ! * * ! ************************************************ BEGIN DATA ! External subroutines ! $REPLY - Gets a YES/NO response from the user ! $MESS - Output to console ! $LINE - Read a line from the console ! $RAND - Get a random number (Range 1-100) GLOBAL $REPLY,$MESS,$LINE,$RAND ! Driving database labels ! Message - User messages ! Vocab - Basic vocabulary ! Loctxt - Location descriptions ! Objloc - Object locations ! Objtxt - Object descriptions ! Event - Main event table ! Movemt - Location movements ! Status - Status check table GLOBAL Message,Vocab,Loctxt,Objloc,Objtxt GLOBAL Event,Movemt,Status WORD Here,HL,BD,DE,Rnum,I,J,User/<15>/ BYTE Flag,W1,W2,Btemp,Ctemp,Domeit BYTE Word1/<4>/,Space/' '/,Cret/0/,Bneg1/-1/,Bzero/0/ END DATA PROGRAM Adventure Start: here=0 ! Start at location 0 Moved: CALL $mess(0) ! New line ! User flag 0 when set indicates a dark location ! He cant see unless object 0 is here ! Note that Objloc(0) is equivalent to simply Objloc Desc: BEGIN IF (User#0) IF (User(3)#0)User(3)=User(3)-1 IF (Objloc=Here)GO TO Seen ! Object here IF (Objloc=512)GO TO Seen ! Carried TYPE 'Everything is dark. I cannot see' IF (User(4)#0)User(4)=User(4)-1 GO TO Command ENDIF Seen: CALL $MESS(Loctxt(HERE)) ! Describe Here Look: Flag=0 ! List objects here I=0 Look1: IF (objloc(I)=-1)GO TO command ! End of objects IF (objloc(I)#here)GO TO next BEGIN IF (Flag=0) ! Object here CALL $mess(0) ! New line TYPE 'I can also see' Flag=1 ! That message only once ENDIF CALL $mess(objtxt(I)) ! Describe object CALL $mess(0) Next: I=I+1 ! Next entry GO TO look1 Command: HL=$Status ! See if anything happens GO TO Active Proc: ! Returns here IF (User(2)#0)User(2)=User(2)-1 ! Count down active CALL $RAND(#Rnum) ! Keep random spinning CALL $Mess(0) CALL $Line ! Get a line HL=@SP ! Point to it GETWD: CALL Lookup(#W1) ! See if we know it BEGIN IF (W1=Bneg1) ! Not found in table BEGIN IF (@HL=Cret) ! No more words Err1: Type 'I just don't understand what you mean' BEGIN IF (Word1>90) TYPE 'Perhaps if you used UPPER CASE' ENDIF GO TO Command ! Try again ENDIF Scan: BEGIN IF (@HL=Space) ! Next word HL=HL+1 GO TO getwd ENDIF IF (@HL=Cret)GO TO Err1 ! No more words HL=HL+1 GO TO Scan ENDIF ! If we fall out here we have a known word in W1. Now see ! if we can find one for word number 2 W2=Bneg1 ! No word yet Scan2: IF (@HL=Space)GO TO Second ! Found one IF (@HL=Cret)GO TO Allin ! No more HL=HL+1 GO TO Scan2 Second: HL=HL+1 ! Point to word CALL Lookup(#W2) ! See if an object BEGIN IF (W2=Bneg1) ! Not found Scan3: IF (@HL=Cret)GO TO Allin IF (@HL=Space)GO TO Second ! Another word found HL=HL+1 GO TO Scan3 ENDIF ! See if this word causes a change of location Allin: HL=Movemt(Here) ! Point to movements Moveit: IF (@HL=Bneg1)GO TO Nomove ! End of list BEGIN IF (@HL=W1) ! Entry found HL=HL+1 ! Point to dest Btemp=@HL ! Go there Here=Btemp ! Keep to bytes GO TO Moved ENDIF HL=HL+2 ! Next entry GO TO Moveit Nomove: ! Look up the words in the main event table to see ! what (if anything) happens HL=#Event ! Point to table Doneit=0 ! Clear flag Active: BEGIN IF (@HL=Bzero) ! End of table IF (Doneit#0)GO TO Command ! We did something BEGIN IF (W1<13) ! Explicit movement TYPE 'I cannot go in that direction' ELSE TYPE 'I cant' ENDIF GO TO Command ! Get another command ENDIF IF (@HL=Bneg1)GO TO Entry ! Any match IF (@HL=W1)GO TO Entry ! Exact match HL=HL+6 ! Next entry GO TO Active Entry: HL=HL+1 ! Point to 2nd word IF (@HL=Bneg1)GO TO Match ! Any match IF (@HL=W2)GO TO Match ! Exact match HL=HL+5 ! Next entry GO TO Active Match: HL=HL+1 ! Condition pointer BC=@HL ! Get it HL=HL+2 ! Point to actions Check: IF (@BC=Bneg1)GO TO Doit ! End of conditions Btemp=@BC ! Get this condition BC=BC+1 ! Next operand Ctemp=@BC ! Preload PC=TABLE1(Btemp) ! Computed GO TO C0: IF (Ctemp=Here)GO TO passed ! Check current location Cont: HL=HL+2 ! Next word pair GO TO Active ! Try next table entry C1: IF (Objloc(Ctemp)=HereGO TO passed ! Object present IF (511<Objloc(Ctemp)<1025)GO TO passed GO TO Cont C2: CALL $RAND(#Rnum) ! Probable event IF (Ctemp>Rnum)GO TO passed GO TO Cont C3: IF(Objloc(Ctemp)=Here)GO TO Cont ! Object not here IF (511<Objloc(Ctemp)<1025)GO TO Cont GO TO passed C4: IF (Objloc(Ctemp)#1024)GO TO passed ! Object not worn GO TO Cont C5: IF (User(Ctemp)=0)GO TO Cont ! Flag not zero Passed: BC=BC+1 ! Next condition GO TO Check C6: BC=BC+1 ! Check flag value Btemp=@BC ! IF (User(Ctemp)#Btemp)GO TO Cont GO TO Passed C7: IF (User(Ctemp)#0)GO TO Cont ! Flag zero GO TO Passed C8: IF (Objloc(Ctemp)#512)GO TO Cont ! Object carried GO TO Passed ! Condition met so perform actions Doit: BC=@HL ! Point to actions HL=HL+2 ! Point to next entry Doneit=1 ! Say we've done something Nxtact: IF (@BC=Bneg1)GO TO Active ! All done Btemp=@BC ! Get action BC=BC+1 ! Point to next Ctemp=@BC ! Preload value PC=TABLE2(Btemp) ! Computed GO TO ! In the following TABLE3 is simply a continuation of TABLE2 ! and not a separate entity. It is done this way to keep the ! compiler happy as it can't handle continuation lines BEGIN DATA WORD TABLE2/A0,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10/ WORD TABLE3/A11,A12,A13,A14,A15,A16,A17,Done/ END DATA A0: TYPE 'I have with me' ! Inventory Flag=0 I=0 Inven: IF (Objloc(I)=-1)GO TO inven0 ! End of list BEGIN IF (511<Objloc(I)<1025) ! Carried Flag=1 CALL $mess(objtxt(I)) BEGIN IF (Objloc(i)=1024) ! Worn TYPE ' which I am wearing' ELSE CALL $mess(0) ! New line ENDIF ENDIF Nextob: I=I+1 GO TO inven Inven0: IF (Flag=0)TYPE 'Nothing at all' GO TO Done A1: BEGIN IF (Objloc(Ctemp)#1024) ! Remove worn object TYPE 'I am not wearing it' GO TO Done ENDIF BEGIN IF (User(1)=4) ! Hands full TYPE 'I cant. My hands are full' GO TO Done ENDIF Objloc(Ctemp)=512 ! Say carried User(1)=User(1)+1 ! Update tote GO TO Nxtop A2: BEGIN IF (User(1)=4) ! Pick up object TYPE 'I cannot carry any more' GO TO Done ENDIF BEGIN IF (Objloc(Ctemp)=Here Objloc(Ctemp)=512 ! Say carried User(1)=User(1)+1 ! Update total GO TO Nxtop ENDIF TYPE 'Im already carrying it' GO TO Done A3: BEGIN IF (Objloc(Ctemp)=Here) ! Drop object TYPE 'I don't have it' GO TO Done ENDIF IF (Objloc(Ctemp)=512)User(1)=User(1)-1 Objloc(Ctemp)=Here GO TO Nxtop A4: BEGIN IF (Objloc(Ctemp)=512) ! Wear it Objloc(Ctemp)=1024 ! Say carried User(1)=User(1)-1 GO TO Nxtop ENDIF BEGIN IF (Objloc(Ctemp)=1024) TYPE 'I am already wearing it' ELSE TYPE 'I don't have it' ENDIF GO TO Done A5: CALL $Mess(Message(Ctemp)) ! Type message GO TO Nxtop ! Get next action A6: GO TO Desc ! Describe location A7: GO TO Proc ! Procede A8: Here=Ctemp ! Immediate move GO TO Nxtop A9: User(Ctemp)=255 ! Set flag GO TO Nxtop A10: User(Ctemp)=0 ! Clear flag Nxtop: BC=BC+1 GO TO Nxtact A11: DE=Objloc(Ctemp) ! Swap objects Objloc(Ctemp)=Objloc(Ctemp+1) ! Move 1st object Objloc(Ctemp+1)=DE ! Move 2nd object GO TO Nxtop A12: STOP ! Stop the program A13: TYPE 'Okay' ! Say okay GO TO Done ! And procede A14: TYPE 'Are you sure you want to quit now' CALL $reply(#i) IF (I=0)GO TO Nxtact STOP 'Okay ... bye' A15: BC=BC+1 ! Store value in flag Btemp=@BC ! Get it User(Ctemp)=Btemp GO TO Nxtop A16: Objloc(Ctemp)=Here ! Create object GO TO Nxtop A17: Objloc(Ctemp)=2048 ! Destroy object GO TO Nxtop Done: GO TO Command ! Lookup - Find word in table ! Each entry consists of a four byte name ! followed by an byte identification code. Eg 'FRED',2 ! this code is returned if found, otherwise -1. SUBROUTINE lookup(DE) LOOP J=0 TO 3 ! Clear out word Word(J)=Space ENDLOOP J LOOP J=0 TO 3 ! Extract 1st 4 letters IF (@HL=Space)GO TO Gotwrd ! End of word IF (@HL=Cret)GO TO Gotwrd ! End of word Word(J)=@HL ! Get character HL=HL+1 ENDLOOP J Gotwrd: BC=$Vocab ! Point to table @DE=Bneg1 ! Assume no match Find: Flag=0 ! fndit Flag LOOP I=0 TO 3 ! 4 bytes IF (@BC=Bneg1)RETURN ! End of table IF (Word1(I)#@BC)Flag=1 ! No match BC=BC+1 ENDLOOP I BEGIN IF (Flag=0) ! Matched Btemp=@BC ! Get ID @DE=Btemp ! Pass it to caller RETURN ENDIF BC=BC+1 ! Skip over ID GO TO Find ! And try again END(Practical Computing - August 1980)