CONTENTS
1. Introduction
2. The main loop
3. Locations
4. Objects
5. Inventory
6. Passages
7. Distance
8. North, east, south, west
9. Code generation
10. More attributes
11. Conditions
12. Open and close
13. The parser

How to program a text adventure in C

5. Inventory

We will now make it possible for persons to ‘hold’ items.

In the previous chapter, we made an array to store all objects, including the player himself. By considering the player as an object, we make the player an integrated part of the game, instead of a spectator watching the virtual world from a safe distance.

The advantage of this approach becomes most obvious in a game with multiple player characters (either MUD-style or a single-player game where you can switch between different characters), but it is equally useful when there is only one character. Player attributes no longer have to be stored in separate variables; we can use the same data structure as used for any other object. So the player, being an object:

This makes certain common actions very easy to implement:

Action Typical
command
Example
Player moves from location to location go player->location = cave;
List items and persons present at a location look listObjectsAtLocation(cave);
Player gets an item get silver->location = player;
Player drops an item drop silver->location = player->location;
List the player's inventory inventory listObjectsAtLocation(player);
Player gives an item to a person give silver->location = guard;
Player receives an item from a person ask silver->location = player;
List another person's inventory examine listObjectsAtLocation(guard);

Commands go and look (the first two examples above) were already implemented in the previous chapter. Now we will introduce some typical inventory actions for both the player and the non-player characters (commands get, drop, give, ask and inventory).

Sample output
Welcome to Little Cave Adventure. You are in an open field. You see: a silver coin a burly guard --> get silver OK. --> inventory You see: a silver coin --> look around You are in an open field. You see: a burly guard --> give silver OK. --> inventory You are empty-handed. --> ask silver OK. --> inventory You see: a silver coin --> go cave OK. You are in a little cave. You see: a gold coin --> give silver There is nobody here to give that to. --> drop silver OK. --> look around You are in a little cave. You see: a silver coin a gold coin --> inventory You are empty-handed. --> quit Bye!
main.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 63 64 65 66 67 #include <stdio.h> #include <string.h> #include "location.h" #include "inventory.h" static char input[100]; static int getInput() { printf("\n--> "); return fgets(input, sizeof(input), stdin) != NULL; } static int parseAndExecute() { char *verb = strtok(input, " \n"); char *noun = strtok(NULL, " \n"); if (verb != NULL) { if (strcmp(verb, "quit") == 0) { return 0; } else if (strcmp(verb, "look") == 0) { executeLook(noun); } else if (strcmp(verb, "go") == 0) { executeGo(noun); } else if (strcmp(verb, "get") == 0) { executeGet(noun); } else if (strcmp(verb, "drop") == 0) { executeDrop(noun); } else if (strcmp(verb, "give") == 0) { executeGive(noun); } else if (strcmp(verb, "ask") == 0) { executeAsk(noun); } else if (strcmp(verb, "inventory") == 0) { executeInventory(); } else { printf("I don't know how to '%s'.\n", verb); } } return 1; } int main() { printf("Welcome to Little Cave Adventure.\n"); executeLook("around"); while (getInput() && parseAndExecute()); printf("\nBye!\n"); return 0; }

Explanation:

The new commands are implemented by the following module.

inventory.h
1 2 3 4 5 extern void executeGet(const char *noun); extern void executeDrop(const char *noun); extern void executeGive(const char *noun); extern void executeAsk(const char *noun); extern void executeInventory(void);
inventory.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 #include <stdio.h> #include "object.h" #include "misc.h" static void moveObject(const char *noun, OBJECT *from, OBJECT *to) { OBJECT *obj = parseObject(noun); if (obj == NULL) { printf("I don't understand what item you mean.\n"); } else if (from != obj->location) { printf("You can't.\n"); } else if (to == NULL) { printf("There is nobody here to give that to.\n"); } else { obj->location = to; printf("OK.\n"); } } void executeGet(const char *noun) { moveObject(noun, player->location, player); } void executeDrop(const char *noun) { moveObject(noun, player, player->location); } void executeGive(const char *noun) { moveObject(noun, player, personHere()); } void executeAsk(const char *noun) { moveObject(noun, personHere(), player); } void executeInventory(void) { if (listObjectsAtLocation(player) == 0) { printf("You are empty-handed.\n"); } }

Explanation:

Function personHere is used in commands give and ask, but it could be useful for other commands as well. So we define it in misc.c.

misc.h
1 2 3 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 #include <stdio.h> #include <string.h> #include "object.h" #include "misc.h" 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 (object.* and location.*) remain unchanged, you can see them in the previous chapter.

In chapter 12, you will see us add more commands. But first, we will be making some improvements to command go.


Next chapter: 6. Passages