Turbo Pascal Text Adventure Primer cover: BEACON, the adventure the primer builds in Turbo Pascal 2.0, mid-game on the Tatung Einstein
← Back to Modules
module
17

The World As Data

Introduction

Part three builds BEACON, a section at a time, from the best of what parts one and two found. It starts with the world itself. In DRIFT, the BBC BASIC course's adventure, the four rooms are lines of DATA in the program. BEACON's twelve are records in a file, and the program does not hold a word of their description.

A Room Is A Record

Room = record
  Name: Str16;
  Exits: array[1..6] of Byte;
end;

The exits are room numbers, in the order north, south, east, west, up, down; 0 means there is no way that way. The beach is room 1, and its exits are 5 3 4 2 0 0: north to room 5, the cliff path, south to the jetty, east to the shed, west to the cove, and no up or down.

The twelve records are in BEACON.DAT, one after another, and the program reads them all into Map, an array of twelve rooms, when it starts. Each record is 23 bytes, 17 for the name and 6 for the exits, so the whole map is 276 bytes of memory.

The description is not in the record. Room N's description is message N in BEACON.MSG, S13's file: the rooms are its first twelve messages. So Look prints Map[Here].Name from memory and then Say(Here) from the disc.

Where The Files Come From

Both files are made from BEACON.TXT by MAKEDAT, which is on the disc. It is S13's MAKEMSG with one addition: when a line starts *ROOM, it makes a room record as well. The name is the rest of that line, and the next line's six numbers are read straight into the exits with Read:

for I := 1 to 6 do
  Read(T, R.Exits[I]);
Readln(T);

Read of a number from a text file takes the next number on the line, skipping the spaces; Readln(T) then moves on to the next line.

program MakeDat;
type
  Str255 = string[255];
  Str16 = string[16];
  Room = record
    Name: Str16;
    Exits: array[1..6] of Byte;
  end;
var
  T: Text;
  F: file of Str255;
  D: file of Room;
  Line, Msg: Str255;
  R: Room;
  N, I: Integer;
begin
  Assign(T, 'BEACON.TXT');
  Reset(T);
  Assign(F, 'BEACON.MSG');
  Rewrite(F);
  Assign(D, 'BEACON.DAT');
  Rewrite(D);
  N := 0;
  while not Eof(T) do
  begin
    Readln(T, Line);
    if Copy(Line, 1, 1) = '*' then
    begin
      if N > 0 then
        Write(F, Msg);
      N := N + 1;
      FillChar(Msg, SizeOf(Msg), 0);
      if Copy(Line, 1, 5) = '*ROOM' then
      begin
        FillChar(R, SizeOf(R), 0);
        R.Name := Copy(Line, 7, 16);
        for I := 1 to 6 do
          Read(T, R.Exits[I]);
        Readln(T);
        Write(D, R);
      end;
    end
    else if (N > 0) and (Line <> '') then
    begin
      if Msg <> '' then
        Msg := Msg + '|';
      Msg := Msg + Line;
    end;
  end;
  Close(T);
  Writeln(FileSize(D), ' rooms, ',
          FileSize(F), ' messages');
  Close(D);
  Close(F);
end.

You need not type it: it is on the disc as MAKEDAT.PAS. Load it with W, MAKEDAT, and run it with R, and it says 12 rooms, 27 messages. Run it whenever you change BEACON.TXT, and the game changes with it, without one line of the game being touched.

The Code

program Beacon;
const
  Rooms = 12;
type
  Str255 = string[255];
  Str16 = string[16];
  Room = record
    Name: Str16;
    Exits: array[1..6] of Byte;
  end;
var
  Map: array[1..Rooms] of Room;
  Msg: file of Str255;
  Here: Integer;

procedure Wrap(S: Str255);
var
  P: Integer;
begin
  S := S + '|';
  while S <> '' do
  begin
    P := Pos('|', S);
    if P > 40 then
    begin
      P := 40;
      while S[P] <> ' ' do
        P := P - 1;
    end;
    Writeln(Copy(S, 1, P - 1));
    Delete(S, 1, P);
  end;
end;

procedure Say(N: Integer);
var
  S: Str255;
begin
  Seek(Msg, N - 1);
  Read(Msg, S);
  Wrap(S);
end;

procedure Load;
var
  D: file of Room;
  I: Integer;
begin
  Assign(D, 'BEACON.DAT');
  Reset(D);
  for I := 1 to Rooms do
    Read(D, Map[I]);
  Close(D);
  Assign(Msg, 'BEACON.MSG');
  Reset(Msg);
end;

procedure Look;
begin
  Writeln;
  Writeln(Map[Here].Name);
  Say(Here);
end;

begin
  Load;
  Here := 1;
  Look;
  Close(Msg);
end.

Wrap and Say are S13's, unchanged. Load reads the map and opens the messages, leaving BEACON.MSG open for the whole game.

Starting from

A new work file BEACON1, on the primer's disc. Save it before you run it (S); S18 starts from it.

What you should see

Beach
You are on a strip of shingle at the
foot of the cliffs. Wreckage from your
boat rolls in the surf. A path climbs
north. The beach runs west to a cove
and south to a jetty, and a store shed
stands to the east.

Change One Thing

  • Change Here := 1 to Here := 12. Where are you?
  • In Look, change Say(Here) to Say(Here + 12). What does the beach say now, and where did that come from?
  • Change Writeln(Map[Here].Name) to Writeln(Map[Here + 1].Name).

Exercises

17.1 Instead of looking at room 1, print the number and name of every room.

17.2 After the description, list the beach's exits by direction and the name of the room each leads to: North to Cliff Path.

17.3 Print how many messages there are in BEACON.MSG.

Worked solutions are in Appendix II.

When It Goes Wrong

SymptomCause
I/O error 01BEACON.DAT or BEACON.MSG is not on the disc. Run MAKEDAT.
The wrong description under a nameThe message number is off by one, or the rooms in BEACON.TXT are not in the same order as the messages.

Summary

A room is a record: a name and six exits. MAKEDAT turns BEACON.TXT into twelve room records in BEACON.DAT and 27 messages in BEACON.MSG. The game reads the map into an array, and prints each room's description straight from the disc when it needs it.

Next

S18, Moving And Looking: the parser from part one joins the game.

Get the Newsletter

New guides, disk images and community finds, roughly once a quarter. No spam, we promise, this isn't Tatung's marketing department.
Your subscription could not be saved. Please try again.
Your subscription has been successful.

Newsletter

Subscribe to our newsletter and stay updated.

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.