We're going to fix one of the more glaring deficiencies
of the initial implementation of the game,
namely that the description of the dungeon takes the form of
hard-compiled structure arrays in main.c,
such that the source code must be edited
(and in a tedious way, at that)
and the game recompiled
whenever we want to modify the dungeon.
(We're used to recompiling when we make changes to our programs,
but we shouldn't have to recompile when we make expected changes to our data.
Imagine if a spreadsheet program
required you to enter your worksheet expressions or data
with a C compiler and recompile each time!)
We're going to add code which can read the dungeon description
dynamically, at run time, from a simple, textual data file.
The file will describe the rooms,
the objects and which rooms they're initially in,
and the exits which interconnect the rooms.
As the file is read,
we'll dynamically assign new instances
of struct room and struct object
to define the rooms and objects which the data file describes.
We won't know how many room and object structures we'll need,
and we certainly won't know what descriptions they will contain,
so we'll abandon the rooms and objects arrays from main.c,
along with their cumbersome initializations.
We'll add (in objects.c) an allocation function,
newobject(),
which will return a pointer to a brand-new object structure
which we can use to contain some new object,
usually one
we've just read from the data file.
Similarly, we'll add a newroom() function to rooms.c
which will return a pointer to a brand-new room structure.
(We're not quite ready to do full-blown dynamic memory allocation yet,
so for now, the room and object allocation functions
will dole structures out of fixed-size arrays
which we'll arbitrarily declare as having size 100.
Naturally we'll keep track of how many structures are actually in use,
and complain if an attempt is made to use more than 100.
Since the allocation code is isolated
down within rooms.c and object.c,
only those modules will need rewriting
when we move to a more flexible allocation scheme;
the code which calls newroom()
and newobject() won't need to change.)
Appended to this assignment is a listing of a new file, io.c,
for doing the data file reading.
(This code is also on the disk
or at the ftp site,
in the week2 subdirectory.)
Rip out the definitions and initializations of the objects
and rooms arrays at the top of main.c;
also remove the initialization of actor
(but leave the definition).
At the top of main(),
before the initial call to listroom,
add the lines
if(!readdatafile())
exit(1);
gotoroom(&actor, getentryroom()); /* put actor in initial room */
Add the following code to object.c
(it can go near the top):
#define MAXOBJECTS 100
static struct object objects[MAXOBJECTS];
static int nobjects = 0;
struct object *
newobject(char *name)
{
struct object *objp;
if(nobjects >= MAXOBJECTS)
{
fprintf(stderr, "too many objects\n");
exit(1);
}
objp = &objects[nobjects++];
strcpy(objp->name, name);
objp->lnext = NULL;
return objp;
}
(This code is in the file object.xc
in the week2 subdirectory.)
Add the following code to rooms.c
(it can go near the top):
#define MAXROOMS 100
static struct room rooms[MAXROOMS];
static int nrooms = 0;
struct room *
newroom(char *name)
{
struct room *roomp;
int i;
if(nrooms >= MAXROOMS)
{
fprintf(stderr, "too many rooms\n");
exit(1);
}
roomp = &rooms[nrooms++];
strcpy(roomp->name, name);
roomp->contents = NULL;
for(i = 0; i < NEXITS; i++)
roomp->exits[i] = NULL;
return roomp;
}
struct room *
findroom(char *name)
{
int i;
for(i = 0; i < nrooms; i++)
{
if(strcmp(rooms[i].name, name) == 0)
return &rooms[i];
}
return NULL;
}
struct room *
getentryroom(void)
{
if(nrooms == 0)
return NULL;
return &rooms[0]; /* temporary */
}
(This code is in the file rooms.xc
in the week2 subdirectory.)
You'll also need the file fgetline.c,
which is appended behind the new io.c,
and also in the week2 subdirectory.
After adding all the new code,
and any necessary prototype declarations to game.h,
the program should again compile and run,
but it will expect to find a data file named
dungeon.dat in the current directory.
A sample dungeon.dat file
(corresponding to the old, hard-compiled game)
is included here
(and in the week2 subdirectory).
Once you see how it works, you can start extending the dungeon
simply by modifying the dungeon.dat file.