Adventure 1 (Abersoft)
Adventure 1 is a port of the original Crowther/Woods adventure to the 48k Spectrum. It was originally released by Abersoft in 1982, and subsequently rereleased as 'Colossal Caves' by CP Software and 'Classic Adventure' by Melbourne House.
Since the maximum score in this version is 210 points and its author is John Jones-Steele, I think that it would be referred to by taxonomists as JONE0210.
Two versions are known to exist. As the walkthrough points out, the original will not let you drop the bear to scare the troll away, while the fixed version (and the rereleases) will.
Internally, the game is implemented using the methods described in Ken Reed's 1980 article in Practical Computing — the same article that was the inspiration for several other game engines, including The Quill. The data tables describing the game occupy memory from 6979h to 0DF35h, and are followed by 4k of free space.
Although the game is written with separate game engine and database, I'm not aware of any attempts to use the engine with a different game database, though it no doubt helped in porting the game to different platforms.
In comparison with the 350-point mainframe original (I compared against Mike Goetz's FORTRAN version, GOET0350):
- All 140 locations are present, in the same order.
- The vocabulary table is similar, though not quite identical. Word IDs have been reassigned to fit in a byte (so, for example, words 1001-1064 on the mainframe become 101-164 on the Spectrum). JONE0210 is also aware of more swearwords.
- There are 78 objects, as opposed to the 64 in the original. Some of this is because the original allows an object to have more than one state; so the brass lamp is one object in the original, but two (unlit and lit versions) in this version. There is one extra object (a small eastern flute) which is not a treasure.
- The message table has 199 entries, compared to the 203 in GOET0350.
However, many of those entries are blank, corresponding to parts of the
game that aren't implemented. These include:
- The knife-throwing dwarves. The only dwarf who appears in the game is the first one, who throws the axe and misses.
- The pirate. The pirate's chest is still present, but not its owner.
- Hints such as "Are you trying to catch the bird?"
- The maximum score is 210: 15 treasures @ 10 points per treasure = 150, plus 60 points for solving the endgame.
- There is one extra puzzle: Attempting to use the bird to drive the snake away won't work unless the snake is first made sleepy.
- The parser limits words to four characters. The first magic word is thus XYZY, not XYZZY.
The amount of free space in the game suggests that memory pressure isn't responsible for the absence of the dwarves and the pirate. It's more likely that they would have needed additional support in the game engine, and the author didn't have the time to add this.
The cause of the bug with the bear is:
Original version Bugfixed version GET BEAR GET BEAR PRESENT 0x35 ; Gentle bear PRESENT 0x35 ; Gentle bear NOTPRES 0x4e ; Chained? NOTPRES 0x4e ; Chained? GET 0x35 DESTROY 0x35 OKAY CREATE 0x36 ; Bear following you GET 0x36 OKAY
That is, the original has you carrying the 'gentle bear', while the fixed version replaces the 'gentle bear' with the 'bear following you'. The DROP BEAR handler only checks for the latter.
(I think there may also be another bug in that logic. The chained bear is object 0x4c, not 0x4e).
Here's C source for a simple utility that dumps the
'Adventure 1' database. The input file should be in +3DOS format; use
tapsplit to extract it
from the .TAP / .TZX file containing the game.
Internal storage format
General note: Data tables tend to be lists of offsets. The game engine will then add a base address to get the address of the actual item in question.
Offsets are frequently 1-based; the first compression token is found at offset 1, not offset 0.
The length of an object is given by
offset[objectno+1] - offset[objectno]
. The offset table will
therefore have one more entry than objects, so that the length of the last
object can be calculated.
0x6979: Offsets for expansion tokens (each offset is 1 byte) DB 1,4,5,7 ... 0x69BC: Expansion token text DB 'the', 'a', 'of', 'You' ...
Each expansion token is a whole word. The game engine seems to handle insertion of spaces before/after a token.
0x6ABA: Offsets of location descriptions (each offset is 2 bytes). There are 140 locations; the table allows space for 148. DW 0,97,184,226 ... 0x6BE6: Location text. Bytes 0x81 and higher correspond to expansion tokens. Byte 0x7F is a newline. DB 83h,84h,'standing',91h,82h,81h,7Fh,'road' ... 0x901D: Vocabulary. Standard format: 4 bytes ASCII followed by one byte word ID. There is free space for five extra words in the table. DB 'ROAD',2,'HILL',2,'ENTE',3 ... 0x9491: Offsets of location movement tables. This table is sized for 173 locations, though there are only 140 in the game. 0x95ED: Location movement tables. Two bytes per entry; word ID, followed by room number. 0x9AB1: Offsets of object descriptions. The table is sized for 100 objects, of which 78 are used. 0x9B79: Object descriptions. These are ASCII and do not use compression tokens, though many of them start with a 0Dh (CR) byte. 0xA7F9: Object states. Two bytes per object. The first is the location where it starts, and the second is 0 for a normal object, 10h for a treasure. In play, bit 0 of the second byte is set when the object is carried. 0xA8C1: Response table. 6 bytes per entry: DB verb DB noun ;0xFF to match any noun DW cond ;Offset of conditions DW act ;Offset of actions The parser will pattern-match each entry in the table against its input. If they match, it will check each condition. If they are all true, it will execute the actions. As usual, the lengths of the condition and action lists are found by deducting the offsets in this entry from the offsets in the following entry. 0xB3B1: Conditions for response table. Each condition is two bytes: opcode, argument. The opcodes are fairly standard: 00 AT room true if player is in specified room 01 PRESENT obj true if object is here or carried 02 RAND chance true if random number 0-100 < chance 03 NOTPRES obj true if object is not here or carried 04 NOTZERO flag true if flag is nonzero 05 ZERO flag true if flag is zero 06 CARRIED obj true if object is carried 07 HERE obj true if object is in current room 08 NOTHERE obj true if object is not in current room 09 NOTCARR obj true if object is not carried 0A EXISTS obj true if object exists within game 0xB7FD: Actions for response table. Again, each action is two bytes: opcode, argument. This means that on a few actions the argument is ignored, and placing an object in a location takes two distinct actions. 00 INVEN List inventory 01 GET obj Move object to inventory 02 DROP obj Move object from inventory to location 03 MESSAGE n Display message number 04 DESC Describe room, prompt for next input 05 END End processing, prompt for next input 06 GOTO room Move player to specified room 07 SET flag Set flag to 255 08 CLEAR flag Set flag to 0 09 EXIT End game 0A OKAY Print "Okay", prompt for next input 0B QUIT Ask "Are you sure?" 0C CREATE obj Create object in the current location 0D DESTROY obj Destroy object, move to location 0 0E LET flag 4 Set specified flag to 4 (not used?) 0F SCORE Display player's score 10 TARGET room Set room to use for next PLACE opcode 11 PLACE obj Place object obj in room specified by TARGET. 0xBF69: Process table. 4 bytes per entry: DW cond ;Offset of conditions DW act ;Offset of actions Like the Response table, but executed before player input. There is no verb/noun matching, so all entries are processed. 0xC031: Conditions for the process table. 0xC0F9: Actions for the process table. 0xC289: Offsets of messages. 0xC419: Message text. Like room descriptions, these use compression tokens. 0xDF35: Free space, up to 0xF2FE. 0xF2FF: Start of game engine proper.
John Elliott 2016-10-10