1 <!DOCTYPE HTML PUBLIC
"-//W3O//DTD W3 HTML 2.0//EN">
4 <link rev=
"owner" href=
"mailto:scs@eskimo.com">
5 <link rev=
"made" href=
"mailto:scs@eskimo.com">
6 <title>Assignment #
7</title>
15 <B>Intermediate C Programming
18 UW Experimental College
23 </B></p><p><a href=
"PS7.html">Assignment #
7</a>
24 <br><a href=
"PS6a.html">Assignment #
6 Answers
</a>
25 <br><a href=
"http://www.eskimo.com/~scs/cclass/int/sx11.html">Class Notes, Chapter
25</a>
27 </B></p><OL><li>Adding new command verbs to the game quickly becomes cumbersome.
28 As long as they all go into
<TT>commands.c
</TT>, they can
29 potentially be invoked on any object and with any tool.
30 So if a new command is (or ought to be) specific to some tool
32 the code to implement it has to spend most of its time
33 verifying that the overcreative player isn't trying to apply
34 the new command verb in some wildly imaginative but utterly
35 inappropriate way (``sharpen gum with feather'';
36 ``pierce idea with boot''; etc.).
37 It would be far better,
40 for implementing certain command verbs, could be attached
42 so that they would only be invoked if those objects were involved
43 (and, additionally, so that the same verb
46 actions if applied to different objects).
47 The notion of attaching code to data structures is one
48 of the fundamentals of
<dfn>Object Oriented Programming
</dfn>,
49 and it's a curious coincidence (or is it?)
50 that the data structures which in this program we'd like to
51 attach code to are in fact what we call ``objects.''
56 of type pointer-to-function,
57 to our
<TT>struct object
</TT>.
58 This field will contain a pointer to a per-object function
59 which handles one or more command verbs unique to that object.
60 (If an object has no unique verbs it wishes to process,
61 its function pointer will be null.)
62 Here is the new field for
<TT>struct object
</TT> in
<TT>game.h
</TT>:
64 int (*func)(struct actor *, struct object *, struct sentence *);
66 Notice that
<TT>func
</TT>
67 takes the same arguments as all the other command functions
68 which we split
<TT>commands.c
</TT> up into last week.
71 If an object contains some custom verb-handling code,
72 it will be called from
75 Before trying all of the default commands,
76 it first sees if either of the two objects which a command might reference
77 (the direct object, or the object of the preposition)
79 If so, it calls that (those) function(s).
82 Now, those functions might or might not know how to handle the
83 particular verb the user typed.
85 control should flow through to the default, global command functions.
86 (For example, you can still
<B>take
</B> or
<B>drop
</B> or
<B>examine
</B>
87 just about any object,
88 even if it has its own special code for when you try to
89 do other things with it,
92 shouldn't have to--and couldn't--repeat
93 all the other code for all the other verbs.)
94 To coordinate things, then,
95 each function that tries to implement one of the user's commands
96 can return various status codes.
99 There are four status codes
100 (which go in
<TT>game.h
</TT>):
102 #define FAILURE
0 /* command completed unsuccessfully */
103 #define SUCCESS
1 /* command completed successfully */
104 #define CONTINUE
2 /* command not completed */
105 #define ERROR
3 /* internal error */
107 <TT>FAILURE
</TT> means that the command completed, but unsuccessfully
108 (the player couldn't do what he tried to do).
109 <TT>SUCCESS
</TT> means that the command completed, successfully.
110 <TT>CONTINUE
</TT> means that the the function which was just called
111 did not implement the command,
112 and that the game system (i.e. the
<TT>docommand
</TT> function)
113 should keep trying, by calling other functions (if any).
114 Finally,
<TT>ERROR
</TT> indicates an internal error of some kind.
118 is the new code for
<TT>docommand
</TT>.
119 This goes at the top of the function,
120 before it calls
<TT>findcmd
</TT>:
122 if(cmd-
>object != NULL
&& cmd-
>object-
>func != NULL)
124 r = (*cmd-
>object-
>func)(player, cmd-
>object, cmd);
129 if(cmd-
>xobject != NULL
&& cmd-
>xobject-
>func != NULL)
131 r = (*cmd-
>xobject-
>func)(player, cmd-
>xobject, cmd);
142 objp-
>func = NULL;
144 to the
<TT>newobject
</TT> function in
<TT>object.c
</TT>.
147 Next we'll need some actual functions
148 (special-purpose ones)
149 for various objects to use.
150 Here are a few examples:
152 int hammerfunc(struct actor *player, struct object *objp,
153 struct sentence *cmd)
155 if(strcmp(cmd-
>verb,
"break") !=
0)
157 if(objp != cmd-
>xobject)
160 if(cmd-
>object == NULL)
162 printf(
"You must tell me what to break.\n");
165 if(!contains(player-
>contents, cmd-
>xobject))
167 printf(
"You have no %s.\n", cmd-
>xobject-
>name);
171 setattr(cmd-
>object,
"broken");
172 printf(
"Oh, dear. Now the %s is broken.\n", cmd-
>object-
>name);
177 int toolfunc(struct actor *player, struct object *objp,
178 struct sentence *cmd)
180 if(strcmp(cmd-
>verb,
"fix") !=
0)
182 if(objp != cmd-
>xobject)
185 if(cmd-
>object == NULL)
187 printf(
"You must tell me what to fix.\n");
190 if(!hasattr(cmd-
>object,
"broken"))
192 printf(
"The %s is not broken.\n", cmd-
>object-
>name);
195 if(!contains(player-
>contents, cmd-
>xobject))
197 printf(
"You have no %s.\n", cmd-
>xobject-
>name);
201 unsetattr(cmd-
>object,
"broken");
202 printf(
"Somehow you manage to fix the %s.\n", cmd-
>object-
>name);
207 int cutfunc(struct actor *player, struct object *objp,
208 struct sentence *cmd)
210 if(strcmp(cmd-
>verb,
"cut") !=
0)
212 if(objp != cmd-
>xobject)
215 if(cmd-
>object == NULL)
217 printf(
"You must tell me what to cut.\n");
220 if(!contains(player-
>contents, cmd-
>xobject))
222 printf(
"You have no %s.\n", cmd-
>xobject-
>name);
225 if(!hasattr(cmd-
>object,
"soft"))
227 printf(
"I don't think you can cut the %s with the %s.\n",
228 cmd-
>object-
>name, cmd-
>xobject-
>name);
232 printf(
"The %s is now cut in two.\n", cmd-
>object-
>name);
237 int playfunc(struct actor *player, struct object *objp,
238 struct sentence *cmd)
240 if(strcmp(cmd-
>verb,
"play") !=
0)
242 if(objp != cmd-
>object)
245 if(!hasattr(cmd-
>object,
"immobile"))
247 if(!contains(player-
>contents, cmd-
>object))
249 printf(
"You don't have the %s.\n", cmd-
>object-
>name);
254 printf(
"You're not quite ready for Carnegie hall,\n");
255 printf(
"but you do manage to force out a recognizable tune.\n");
260 I put these functions in a new source file,
<TT>objfuncs.c
</TT>.
261 (It is not on the disk.)
264 The first three of these are basically simplifications
265 of the
<TT>breakcmd
</TT>,
<TT>fixcmd
</TT>, and
<TT>cutcmd
</TT> functions
267 the code for ``break'', ``fix'', and ``cut'')
268 from
<TT>commands.c
</TT>.
269 Since these functions will be attached to certain objects,
270 and called only when those objects are onvolved in the command,
271 these functions do not have to do quite so much checking.
272 For example,
<TT>hammerfunc
</TT> does not have to
273 make sure that the implement being used is heavy,
274 because
<TT>hammerfunc
</TT> will only be attached to the hammer
275 (or perhaps to other objects
276 which it might be appropriate to smash things with).
280 there are two new things these functions do need to check.
281 Since they will be called first,
282 before the default functions in
<TT>commands.c
</TT>,
283 they must defer to those default functions for most verbs.
284 They must check to see that they're being called for a verb they recognize,
285 and if not, return the status code
<TT>CONTINUE
</TT>
286 indicating that some other code down the line should keep trying.
287 Also, these functions can be called
288 when the object they're attached to is either
289 the direct object of the sentence
290 <em>or
</em> the object of a preposition.
291 When one of these functions is called
292 for an object which appears as the direct object,
293 the
<TT>objp
</TT> parameter is a copy of
<TT>cmd-
>object
</TT>,
295 When they're called for objects which are objects of prepositions,
297 <TT>objp
</TT> is a copy of that object pointer
298 (see the new code for
<TT>docommand
</TT> above).
299 So these commands must typically check
300 that they're being used in the way they expect--for example,
301 if
<TT>hammerfunc
</TT> didn't check to make sure that it was being used
303 as a prepositional object,
304 it might incorrectly attempt to handle a nonsense sentence like
310 The next step is to hook these functions up to the relevant objects.
311 To do that, we'll need to invent
312 a new object descriptor line for the data file,
313 and add some new code to
<TT>readdatafile
</TT> in
<TT>io.c
</TT> to parse it.
314 We'll rig it up so that we can say
320 (We'll keep the attribute ``heavy'' on the hammer,
321 because it might be useful elsewhere,
322 although we won't be needing it
323 for the old
<TT>breakcmd
</TT> function any more.)
324 The new code for
<TT>io.c
</TT> is pretty simple,
325 but before we can write it,
326 we have to remember that there's a significant distinction between
327 the name by which we know a function
328 and the internal address,
330 by which the compiler knows it.
336 it's obvious to
<em>us
</em>
337 that we want the hammer object's
<TT>func
</TT> pointer
338 to be hooked up to
<TT>hammerfunc
</TT>,
339 but it is not at all obvious to the compiler.
340 In particular, the compiler is not going to be looking at our data file
342 Code in
<TT>io.c
</TT> is going to be looking at the data file,
343 and it's going to be doing it as the program is running,
345 after the compiler has finished its work.
346 So we're going to have to ``play compiler'' just a bit,
347 to match up the name of a function
348 with the function itself.
351 This will actually be rather simple to do,
352 because matching up a name
354 with a function is exactly what the
<TT>struct cmdtab
</TT> structure
355 and
<TT>findcmd
</TT> function do.
356 So if we build a second array of
<TT>cmdtab
</TT> structures,
357 holding the names and function pointers
358 for functions which it's appropriate to attach to objects,
359 all we have to add to
<TT>readdatafile
</TT>
360 is this scrap of code:
362 else if(strcmp(av[
0],
"func") ==
0)
364 struct cmdtab *cmdtp = findcmd(av[
1], objfuncs, nobjfuncs);
366 currentobject-
>func = cmdtp-
>func;
367 else fprintf(stderr,
"unknown object func %s\n", av[
1]);
370 The new array of
<TT>cmdtab
</TT> structures is called
<TT>objfuncs
</TT>.
371 I chose to define it at the end of the new source file
<TT>objfuncs.c
</TT>:
373 struct cmdtab objfuncs[] =
375 "hammerfunc", hammerfunc,
376 "toolfunc", toolfunc,
378 "playfunc", playfunc,
381 #define Sizeofarray(a) (sizeof(a) / sizeof(a[
0]))
383 int nobjfuncs = Sizeofarray(objfuncs);
385 So that
<TT>readdatafile
</TT> can access the
<TT>objfuncs
</TT> array,
386 we need these two lines at the top of
<TT>io.c
</TT>:
388 extern struct cmdtab objfuncs[];
389 extern int nobjfuncs;
391 An explanation of the
<TT>nobjfuncs
</TT> variable is in order.
392 When we called
<TT>findcmd
</TT> in
<TT>commands.c
</TT>
393 to search our main list of commends,
396 cmdtp = findcmd(cmd-
>verb, commands, Sizeofarray(commands));
398 But our little
<TT>Sizeofarray()
</TT> macro uses
<TT>sizeof
</TT>,
399 and
<TT>sizeof
</TT> works
401 only for objects which the compiler knows the size of.
405 extern struct cmdtab objfuncs[];
408 <TT>sizeof(objfuncs)
</TT> would not work,
409 because in that source file,
410 all the compiler knows about
<TT>objfuncs
</TT>
411 is that it is an array which is defined
412 (and which therefore has its size set)
415 the computation of the number of elements in the array
416 within the source file
<TT>objfuncs.c
</TT>
417 where the array is defined,
418 and store this number in a second global variable,
<TT>nobjfuncs
</TT>,
419 so that code in
<TT>io.c
</TT> can get its hands on it.
422 Having made these changes,
423 you should be able to add the line
427 to the description of the hammer in
<TT>dungeon.dat
</TT>,
432 to the description of the pliers or some other tool-like object,
437 to the description of the knife or some other sharp object.
438 You can also add the lines
449 to try out the sample ``play'' function.
450 Notice that
<TT>playfunc
</TT> makes you
451 pick up the violin before playing it,
452 but since the piano is immobile,
453 you can just walk up to it and start playing.
454 <li>Rewrite the
<TT>lockcmd
</TT> and
<TT>unlockcmd
</TT> functions
457 <li>Invent some more object functions and some objects for them to be attached to.
462 <li>Since object functions (if present)
463 are called before the default functions in
<TT>commands.c
</TT>,
464 they can not only supplement the commands in
<TT>commands.c
</TT>,
465 they can also override (on a per-object basis)
466 the default behavior of an existing verb.
467 Invent a shower object.
468 (If your dungeon doesn't already have a bathroom,
469 perhaps you'll need to add that, too.)
470 Rig it up so that if the player types
474 the game will print neither ``Taken''
475 nor ``The shower cannot be picked up''
476 but rather something cute like
477 ``After spending
20 luxurious minutes in the shower,
478 singing three full-length show tunes,
479 and using up all the hot water,
480 you emerge clean and refreshed.''
481 <li>Since per-object functions are called before default functions,
482 and since they can defer to those default functions
483 by returning
<TT>CONTINUE
</TT>,
484 another kind of customization is possible.
485 A per-object function can recognize a verb,
486 do some processing based upon it,
487 but then return
<TT>CONTINUE
</TT>,
488 so that the default machinery will also act.
489 Implement a magic sword object
490 so that when the player picks it up,
493 the sword briefly glows blue''
496 The first message is printed by the sword's custom function,
497 and the second one by the normal ``take'' machinery in
<TT>commands.c
</TT>.
498 <li>Implement a container of radioactive waste.
499 Arrange that if the player tries to pick it up,
500 the message ``the container is far too dangerous to pick up''
503 the player is wearing a Hazmat suit,
504 in which case the container can be picked up normally.
505 Also implement the Hazmat suit object.
506 (You can either just have the player pick up the Hazmat suit,
508 arrange that the Hazmat suit implement a custom ``wear'' verb.)
512 This page by
<a href=
"http://www.eskimo.com/~scs/">Steve Summit
</a>
513 //
<a href=
"copyright.html">Copyright
</a> 1995-
9
514 //
<a href=
"mailto:scs@eskimo.com">mail feedback
</a>