Handouts:
Assignment #7
Assignment #6 Answers
Class Notes, Chapter 25
Exercises:
int (*func)(struct actor *, struct object *, struct sentence *);Notice that func takes the same arguments as all the other command functions which we split commands.c up into last week.
#define FAILURE 0 /* command completed unsuccessfully */ #define SUCCESS 1 /* command completed successfully */ #define CONTINUE 2 /* command not completed */ #define ERROR 3 /* internal error */FAILURE means that the command completed, but unsuccessfully (the player couldn't do what he tried to do). SUCCESS means that the command completed, successfully. CONTINUE means that the function which was just called did not implement the command, and that the game system (i.e. the docommand function) should keep trying, by calling other functions (if any). Finally, ERROR indicates an internal error of some kind.
if(cmd->object != NULL && cmd->object->func != NULL) { r = (*cmd->object->func)(player, cmd->object, cmd); if(r != CONTINUE) return r; } if(cmd->xobject != NULL && cmd->xobject->func != NULL) { r = (*cmd->xobject->func)(player, cmd->xobject, cmd); if(r != CONTINUE) return r; }
objp->func = NULL;to the newobject function in object.c.
int hammerfunc(struct actor *player, struct object *objp, struct sentence *cmd) { if(strcmp(cmd->verb, "break") != 0) return CONTINUE; if(objp != cmd->xobject) return CONTINUE; if(cmd->object == NULL) { printf("You must tell me what to break.\n"); return FAILURE; } if(!contains(player->contents, cmd->xobject)) { printf("You have no %s.\n", cmd->xobject->name); return FAILURE; } setattr(cmd->object, "broken"); printf("Oh, dear. Now the %s is broken.\n", cmd->object->name); return SUCCESS; } int toolfunc(struct actor *player, struct object *objp, struct sentence *cmd) { if(strcmp(cmd->verb, "fix") != 0) return CONTINUE; if(objp != cmd->xobject) return CONTINUE; if(cmd->object == NULL) { printf("You must tell me what to fix.\n"); return FAILURE; } if(!hasattr(cmd->object, "broken")) { printf("The %s is not broken.\n", cmd->object->name); return FAILURE; } if(!contains(player->contents, cmd->xobject)) { printf("You have no %s.\n", cmd->xobject->name); return FAILURE; } unsetattr(cmd->object, "broken"); printf("Somehow you manage to fix the %s.\n", cmd->object->name); return SUCCESS; } int cutfunc(struct actor *player, struct object *objp, struct sentence *cmd) { if(strcmp(cmd->verb, "cut") != 0) return CONTINUE; if(objp != cmd->xobject) return CONTINUE; if(cmd->object == NULL) { printf("You must tell me what to cut.\n"); return FAILURE; } if(!contains(player->contents, cmd->xobject)) { printf("You have no %s.\n", cmd->xobject->name); return FAILURE; } if(!hasattr(cmd->object, "soft")) { printf("I don't think you can cut the %s with the %s.\n", cmd->object->name, cmd->xobject->name); return FAILURE; } printf("The %s is now cut in two.\n", cmd->object->name); return SUCCESS; } int playfunc(struct actor *player, struct object *objp, struct sentence *cmd) { if(strcmp(cmd->verb, "play") != 0) return CONTINUE; if(objp != cmd->object) return CONTINUE; if(!hasattr(cmd->object, "immobile")) { if(!contains(player->contents, cmd->object)) { printf("You don't have the %s.\n", cmd->object->name); return FAILURE; } } printf("You're not quite ready for Carnegie hall,\n"); printf("but you do manage to force out a recognizable tune.\n"); return SUCCESS; }I put these functions in a new source file, objfuncs.c. (It is not on the disk.)
break hammer
object hammer attribute heavy func hmmerfunc(We'll keep the attribute ``heavy'' on the hammer, because it might be useful elsewhere, although we won't be needing it for the old breakcmd function any more.) The new code for io.c is pretty simple, but before we can write it, we have to remember that there's a significant distinction between the name by which we know a function and the internal address, or function pointer, by which the compiler knows it. When we write
func hammerfuncin the data file, it's obvious to us that we want the hammer object's func pointer to be hooked up to hammerfunc, but it is not at all obvious to the compiler. In particular, the compiler is not going to be looking at our data file at all! Code in io.c is going to be looking at the data file, and it's going to be doing it as the program is running, well after the compiler has finished its work. So we're going to have to ``play compiler'' just a bit, to match up the name of a function with the function itself.
else if(strcmp(av[0], "func") == 0) { struct cmdtab *cmdtp = findcmd(av[1], objfuncs, nobjfuncs); if(cmdtp != NULL) currentobject->func = cmdtp->func; else fprintf(stderr, "unknown object func %s\n", av[1]); }The new array of cmdtab structures is called objfuncs. I chose to define it at the end of the new source file objfuncs.c:
struct cmdtab objfuncs[] = { "hammerfunc", hammerfunc, "toolfunc", toolfunc, "cutfunc", cutfunc, "playfunc", playfunc, }; #define Sizeofarray(a) (sizeof(a) / sizeof(a[0])) int nobjfuncs = Sizeofarray(objfuncs);So that readdatafile can access the objfuncs array, we need these two lines at the top of io.c:
extern struct cmdtab objfuncs[]; extern int nobjfuncs;An explanation of the nobjfuncs variable is in order. When we called findcmd in commands.c to search our main list of commends, the call looked like
cmdtp = findcmd(cmd->verb, commands, Sizeofarray(commands));But our little Sizeofarray() macro uses sizeof, and sizeof works (rather obviously) only for objects which the compiler knows the size of. In io.c, where we have
extern struct cmdtab objfuncs[];the code sizeof(objfuncs) would not work, because in that source file, all the compiler knows about objfuncs is that it is an array which is defined (and which therefore has its size set) somewhere else. So we need to do the computation of the number of elements in the array within the source file objfuncs.c where the array is defined, and store this number in a second global variable, nobjfuncs, so that code in io.c can get its hands on it.
func hammerfuncto the description of the hammer in dungeon.dat, and the line
func toolfuncto the description of the pliers or some other tool-like object, and the line
func cutfuncto the description of the knife or some other sharp object. You can also add the lines
object violin func playfunc object end object piano attribute immobile func playfunc object endto try out the sample ``play'' function. Notice that playfunc makes you pick up the violin before playing it, but since the piano is immobile, you can just walk up to it and start playing.
take showerthe game will print neither ``Taken'' nor ``The shower cannot be picked up'' but rather something cute like ``After spending 20 luxurious minutes in the shower, singing three full-length show tunes, and using up all the hot water, you emerge clean and refreshed.''
This page by Steve Summit // Copyright 1995-9 // mail feedback