It's time to draw a map - and implement it!
The best tools for drawing a map will always be: a pencil and a piece of paper. A basic map consists of locations (the rectangles), connected by passages (the arrows). We already created locations in chapter 3, now we will start adding the passages.
In the virtual world, a ‘passage’ may be anything connecting two locations: a road, a door, a stretch of sand in a desert. Basically, a passage has the following properties:
Considering these properties, it may not come as a surprise that the struct object defined in chapter 4 is very suitable to store a passage. In fact, a passage is not that different from an item or person; it is present at a certain location as a ‘visible exit’ (this location is the starting point). It just behaves differently to certain commands. In particular the command ‘go’: applied to a passage, go will change the player's location. The target location (the ‘destination’) can be stored in the struct object as a new attribute.
struct object { const char *description; const char *tag; struct object *location; struct object *destination; }; |
Notes:
So we expand the array of objects:
Sample output |
Welcome to Little Cave Adventure. You are in an open field. You see: a silver coin a burly guard a cave entrance --> go entrance OK. You are in a little cave. You see: a gold coin a way out --> go out OK. You are in an open field. You see: a silver coin a burly guard a cave entrance --> go cave OK. You are in a little cave. You see: a gold coin a way out --> quit Bye! |
object.h | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | typedef struct object { const char *description; const char *tag; struct object *location; struct object *destination; } OBJECT; extern OBJECT objs[]; #define field (objs + 0) #define cave (objs + 1) #define silver (objs + 2) #define gold (objs + 3) #define guard (objs + 4) #define player (objs + 5) #define intoCave (objs + 6) #define exitCave (objs + 7) #define endOfObjs (objs + 8) |
object.c | |
1 2 3 4 5 6 7 8 9 10 11 12 13 | #include <stdio.h> #include "object.h" OBJECT objs[] = { {"an open field" , "field" , NULL , NULL }, {"a little cave" , "cave" , NULL , NULL }, {"a silver coin" , "silver" , field, NULL }, {"a gold coin" , "gold" , cave , NULL }, {"a burly guard" , "guard" , field, NULL }, {"yourself" , "yourself", field, NULL }, {"a cave entrance", "entrance", field, cave }, {"a way out" , "out" , cave , field } }; |
Next, we have to make some changes to function executeGo.
location.h | |
1 2 | extern void executeLook(const char *noun); extern void executeGo(const char *noun); |
location.c | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | #include <stdio.h> #include <string.h> #include "object.h" #include "misc.h" void executeLook(const char *noun) { if (noun != NULL && strcmp(noun, "around") == 0) { printf("You are in %s.\n", player->location->description); listObjectsAtLocation(player->location); } else { printf("I don't understand what you want to see.\n"); } } void executeGo(const char *noun) { OBJECT *obj = parseObject(noun); if (obj == NULL) { printf("I don't understand where you want to go.\n"); } else if (obj == player->location) { printf("You are already there.\n"); } else if (getPassageTo(obj) != NULL) { printf("OK.\n"); player->location = obj; executeLook("around"); } else if (obj->location == player->location && obj->destination != NULL) { printf("OK.\n"); player->location = obj->destination; executeLook("around"); } else { printf("You can't go there.\n"); } } |
Explanation:
Function executeGo uses a separate function to check if two locations are connected by a passage. Because such a function will prove to be useful in the next chapter as well, I made it a general function getPassageTo as part of misc.c.
misc.h | |
1 2 3 4 | extern OBJECT *getPassageTo(OBJECT *targetLocation); extern OBJECT *parseObject(const char *noun); extern OBJECT *personHere(void); extern int listObjectsAtLocation(OBJECT *location); |
misc.c | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | #include <stdio.h> #include <string.h> #include "object.h" #include "misc.h" OBJECT *getPassageTo(OBJECT *targetLocation) { OBJECT *obj; for (obj = objs; obj < endOfObjs; obj++) { if (obj->location == player->location && obj->destination == targetLocation) { return obj; } } return NULL; } OBJECT *parseObject(const char *noun) { OBJECT *obj, *found = NULL; for (obj = objs; obj < endOfObjs; obj++) { if (noun != NULL && strcmp(noun, obj->tag) == 0) { found = obj; } } return found; } OBJECT *personHere(void) { OBJECT *obj; for (obj = objs; obj < endOfObjs; obj++) { if (obj->location == player->location && obj == guard) { return obj; } } return NULL; } int listObjectsAtLocation(OBJECT *location) { int count = 0; OBJECT *obj; for (obj = objs; obj < endOfObjs; obj++) { if (obj != player && obj->location == location) { if (count++ == 0) { printf("You see:\n"); } printf("%s\n", obj->description); } } return count; } |
Explanation:
The other modules (main.c and inventory.*) remain unchanged, you can see them in the previous chapter.
Obviously, the map in this sample is trivial: there are only two locations, and they are connected in both directions. I will leave it up to the reader to draw a more complicated map and turn it into a list of objects (locations and passages).
Many responses in the game are still kind of dull. For example, “go guard” returns “You can't go there.” Exactly the kind of output I criticized in the previous chapter! This is something we will be working on in the next chapter.
Next chapter: 7. Distance