-
Notifications
You must be signed in to change notification settings - Fork 6
/
htpataic07.html
776 lines (764 loc) · 27.9 KB
/
htpataic07.html
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
<!DOCTYPE html>
<html>
<head>
<title>7. Distance</title>
<link rel="stylesheet" href="htpataic.css" type="text/css" />
</head>
<body>
<table class="contents"><tr><td>
<h4>Contents</h4>
<div><a href="htpataic01.html">1. Introduction</a></div>
<div><a href="htpataic02.html">2. The main loop</a></div>
<div><a href="htpataic03.html">3. Locations</a></div>
<div><a href="htpataic04.html">4. Objects</a></div>
<div><a href="htpataic05.html">5. Inventory</a></div>
<div><a href="htpataic06.html">6. Passages</a></div>
<div><b>7. Distance</b></div>
<div><a href="htpataic08.html">8. North, east, south, west</a></div>
<div><a href="htpataic09.html">9. Code generation</a></div>
<div><a href="htpataic10.html">10. More attributes</a></div>
<div><a href="htpataic11.html">11. Conditions</a></div>
<div><a href="htpataic12.html">12. Open and close</a></div>
<div><a href="htpataic13.html">13. The parser</a></div>
<div><a href="htpataic14.html">14. Multiple nouns</a></div>
<div><a href="htpataic15.html">15. Light and dark</a></div>
<div><a href="htpataic16.html">16. Savegame</a></div>
<div><a href="htpataic17.html">17. Test automation</a></div>
<div><a href="htpataic18.html">18. Abbreviations</a></div>
<div><a href="htpataic19.html">19. Conversations</a></div>
<div><a href="htpataic20.html">20. Combat</a></div>
<div><a href="htpataic21.html">21. Multi-player</a></div>
<div><a href="htpataic22.html">22. Client-server</a></div>
<div><a href="htpataic23.html">23. Database</a></div>
<div><a href="htpataic24.html">24. Speech</a></div>
<div><a href="htpataic25.html">25. JavaScript</a></div>
</td></tr></table>
<h1>How to program a text adventure in C</h1>
<p>
by Ruud Helderman
<<a href="mailto:[email protected]">[email protected]</a>>
</p>
<p>
Licensed under
<a href="https://github.com/helderman/htpataic/blob/master/LICENSE">MIT License</a>
</p>
<h2>7. Distance</h2>
<p class="intro">
A typical adventure contains many puzzles.
<a href="http://en.wikipedia.org/wiki/Infocom">Infocom</a>
adventures were notoriously difficult to complete; solving every puzzle
might require weeks, even months of trial and error.
Don’t be surprised if the majority of replies from the game
are ‘errors’:
you can’t do this; you can’t go there; you died.
I hate adventures that just return “You can’t”
in response to every attempt I make to solve one of the game’s puzzles.
Such a reply is dull and not very helpful.
It neglects an important aspect of any computer game;
in fact, an essential part of life itself:
the player has to learn from his mistakes.
It is OK for an adventure to be difficult, even frustratingly difficult.
But when the player has the feeling he is not making any progress at all,
or when the only way to make progress is a
<a href="http://en.wikipedia.org/wiki/Brute-force">brute-force</a>
attack on all verb-noun combinations,
then even the most hardened player will lose interest and eventually give up.
The least an adventure game should do,
is explain <i>why</i> the player’s command cannot be completed:
“You can’t do that, because...”
This helps to make the virtual world more convincing,
the story more credible, and the game more enjoyable.
</p>
<p>
I already put quite some effort in
having the game <b>explain why</b> certain commands are ineffective.
Just take a look at the many <i>printf</i> calls in
<i>noun.c</i>, <i>inventory.c</i>, <i>location.c</i>, <i>move.c</i>.
But as the game is getting increasingly complex,
this is becoming quite a burden.
I need a more structural way to detect and handle error conditions.
That is what we will be working on in this chapter.
</p>
<p>
Most commands operate on one or more <b>objects</b>, for example:
</p>
<ul>
<li>The player picks up an <b>item</b>, then gives it to another <b>actor</b>.
</li>
<li>The player follows a <b>passage</b> to another <b>location</b>.
</li>
</ul>
<p>
The first thing to check (after the obvious
<a href="http://en.wikipedia.org/wiki/Typographical_error">typos</a>
caught by the
<a href="http://en.wikipedia.org/wiki/Parsing">parser</a>)
is for the <b>presence</b> of these objects;
failure should result in something like
“There is no ... here” or
“You don’t see any ...”
In this chapter, we will build a generic function that can be used
by every command to find out if an object is within reach of the player.
</p>
<p>
You may think we only need to distinguish two cases:
either the object is here, or it is not.
But many commands require more gradients than just
‘here’ and ‘not here’.
Examples:
<ul>
<li>To <b>use</b> a weapon or a tool, the player must be holding it;
its mere presence at the scene is not enough.
</li>
<li>To <b>drop</b> an item, you must be holding it;
to <b>pick up</b> an item, you must <i>not</i> be holding it.
</li>
<li>Another actor holding an item, may keep the player from getting that item;
<b>get item</b> is typically easier when the item is just lying here.
</li>
</ul>
</p>
<p>
It all boils down to the fact that
there are different notions of ‘here’:
</p>
<table cellpadding="8"><tr>
<td class="snippet">distSelf</td>
<td class="comment">The object is the player</td>
<td class="snippet"><span class="red">object</span> == <span class="red">player</span></td>
</tr><tr>
<td class="snippet">distHeld</td>
<td class="comment">The player is holding the object</td>
<td class="snippet"><span class="red">object</span>->location == <span class="red">player</span></td>
</tr><tr>
<td class="snippet">distHeldContained</td>
<td class="comment">The player is holding another object (for example a bag)
containing the object</td>
<td class="snippet"><span class="red">object</span>->location != NULL &&
<span class="red">object</span>->location->location == <span class="red">player</span></td>
</tr><tr>
<td class="snippet">distLocation</td>
<td class="comment">The object is the player’s location</td>
<td class="snippet"><span class="red">object</span> == <span class="red">player</span>->location</td>
</tr><tr>
<td class="snippet">distHere</td>
<td class="comment">The object is present at the player’s location</td>
<td class="snippet"><span class="red">object</span>->location == <span class="red">player</span>->location</td>
</tr><tr>
<td class="snippet">distHereContained</td>
<td class="comment">Another object
(either an actor or a ‘container’),
present at the player’s location,
is holding (but <i>not</i> hiding) the object</td>
<td class="snippet"><span class="red">object</span>->location != NULL &&
<span class="red">object</span>->location->location == <span class="red">player</span>->location</td>
</tr><tr>
<td class="snippet">distOverthere</td>
<td class="comment">The object is a nearby location</td>
<td class="snippet">getPassage(<span class="red">player</span>->location, <span class="red">object</span>) != NULL</td>
</tr></table>
<p>
The first case (object is player) may seem trivial,
but it is important nonetheless.
For example, the command "examine yourself" should <i>not</i> return
"There is no yourself here."
</p>
<p>
I tried to follow a logical order:
nearby things are at the top, further down below they become more distant.
We can continue the list, to cover objects that are even further away:
</p>
<table cellpadding="8"><tr>
<td class="snippet">distNotHere</td>
<td class="comment">The object is (or appears to be) not here</td>
<td class="snippet"> </td>
</tr><tr>
<td class="snippet">distUnknownObject</td>
<td class="comment">The parser did not recognize the noun entered</td>
<td class="snippet"><span class="red">object</span> == NULL</td>
</tr></table>
<p>
Notice we have seven different cases of ‘here’,
but only one for ‘not here’.
This is because typically, the game only needs to provide information
about things that can be perceived by the player.
If it’s not here, then there’s nothing more to say.
</p>
<p>
In the leftmost column, I proposed a symbolic name for each case.
We will gather these names in an
<a href="http://en.wikipedia.org/wiki/Enumerated_type">enum</a>
named <b>DISTANCE</b>.
</p>
<table><tr>
<td class="snippet">typedef enum {
distSelf,
distHeld,
distHeldContained,
distLocation,
distHere,
distHereContained,
distOverthere,
distNotHere,
distUnknownObject
} DISTANCE;
</td>
</tr></table>
<p>
And in the rightmost column, I proposed a condition for each case to satisfy.
With a little reshuffling, we can easily turn this into a function
that calculates the ‘distance’ of an object
(as seen from the player’s point of view):
</p>
<table><tr>
<td class="snippet">DISTANCE getDistance(OBJECT *from, OBJECT *to)
{
return to == NULL ? distUnknownObject :
to == from ? distSelf :
to->location == from ? distHeld :
to == from->location ? distLocation :
to->location == from->location ? distHere :
getPassage(from->location, to) != NULL ? distOverthere :
to->location == NULL ? distNotHere :
to->location->location == from ? distHeldContained :
to->location->location == from->location ? distHereContained :
distNotHere;
}
</td>
</tr></table>
<p>
That’s all!
We can call this function and do a comparison on its return value.
For example, we had the following piece of code in <i>noun.c</i>:
</p>
<table><tr>
<td class="snippet">else if (!(obj == player ||
obj == player->location ||
obj->location == player ||
obj->location == player->location ||
getPassage(player->location, obj) != NULL ||
(obj->location != NULL &&
(obj->location->location == player ||
obj->location->location == player->location))))
</td>
</tr></table>
<p>
We can now replace each sub-condition by an appropriate distance check:
</p>
<table><tr>
<td class="snippet">else if (!(getDistance(player, obj) == distSelf ||
getDistance(player, obj) == distLocation ||
getDistance(player, obj) == distHeld ||
getDistance(player, obj) == distHere ||
getDistance(player, obj) == distOverthere ||
getDistance(player, obj) == distHeldContained ||
getDistance(player, obj) == distHereContained)
</td>
</tr></table>
<p>
This can be reduced to:
</p>
<table><tr>
<td class="snippet">else if (getDistance(player, obj) >= distNotHere)
</td>
</tr></table>
<p>
This was just an example to give you an idea of the concept;
the actual implementation of <i>noun.c</i> you will find below,
looks slightly different.
</p>
<p>
Time to put things into place.
The definitions of enum <i>DISTANCE</i> and function <i>getDistance</i>
are added to <i>misc.h</i> and <i>misc.c</i>,
since we will be using them in more than one module.
</p>
<table class="code"><tr>
<th>misc.h</th>
</tr><tr>
<td>
<ol>
<li class="new">typedef enum {</li>
<li class="new"> distSelf,</li>
<li class="new"> distHeld,</li>
<li class="new"> distHeldContained,</li>
<li class="new"> distLocation,</li>
<li class="new"> distHere,</li>
<li class="new"> distHereContained,</li>
<li class="new"> distOverthere,</li>
<li class="new"> distNotHere,</li>
<li class="new"> distUnknownObject</li>
<li class="new">} DISTANCE;</li>
<li class="new"></li>
<li class="new">extern bool isHolding(OBJECT *container, OBJECT *obj);</li>
<li class="old">extern OBJECT *getPassage(OBJECT *from, OBJECT *to);</li>
<li class="new">extern DISTANCE getDistance(OBJECT *from, OBJECT *to);</li>
<li class="old">extern OBJECT *actorHere(void);</li>
<li class="old">extern int listObjectsAtLocation(OBJECT *location);</li>
</ol>
</td>
</tr><tr>
<th>misc.c</th>
</tr><tr>
<td>
<ol>
<li class="new"><span class="old">#include </span><stdbool.h></li>
<li class="new">#include<span class="old"> <stdio.h></span></li>
<li class="old">#include "object.h"</li>
<li class="new">#include "misc.h"</li>
<li class="new"></li>
<li class="new">bool isHolding(OBJECT *container, OBJECT *obj)</li>
<li class="new">{</li>
<li class="new"> return obj != NULL && obj->location == container;</li>
<li class="new">}</li>
<li class="old"></li>
<li class="old">OBJECT *getPassage(OBJECT *from, OBJECT *to)</li>
<li class="old">{</li>
<li class="old"> if (from != NULL && to != NULL)</li>
<li class="old"> {</li>
<li class="old"> OBJECT *obj;</li>
<li class="old"> for (obj = objs; obj < endOfObjs; obj++)</li>
<li class="old"> {</li>
<li class="new"><span class="old"> if </span>(isHolding(from, obj)<span class="old"> && obj->destination == to)</span></li>
<li class="old"> {</li>
<li class="old"> return obj;</li>
<li class="old"> }</li>
<li class="old"> }</li>
<li class="old"> }</li>
<li class="old"> return NULL;</li>
<li class="old">}</li>
<li class="old"></li>
<li class="new">DISTANCE getDistance(OBJECT *from, OBJECT *to)</li>
<li class="new">{</li>
<li class="new"> return to == NULL ? distUnknownObject :</li>
<li class="new"> to == from ? distSelf :</li>
<li class="new"> isHolding(from, to) ? distHeld :</li>
<li class="new"> isHolding(to, from) ? distLocation :</li>
<li class="new"> isHolding(from->location, to) ? distHere :</li>
<li class="new"> isHolding(from, to->location) ? distHeldContained :</li>
<li class="new"> isHolding(from->location, to->location) ? distHereContained :</li>
<li class="new"> getPassage(from->location, to) != NULL ? distOverthere :</li>
<li class="new"> distNotHere;</li>
<li class="new">}</li>
<li class="old"></li>
<li class="old">OBJECT *actorHere(void)</li>
<li class="old">{</li>
<li class="old"> OBJECT *obj;</li>
<li class="old"> for (obj = objs; obj < endOfObjs; obj++)</li>
<li class="old"> {</li>
<li class="new"><span class="old"> if </span>(isHolding(player->location, obj)<span class="old"> && obj == guard)</span></li>
<li class="old"> {</li>
<li class="old"> return obj;</li>
<li class="old"> }</li>
<li class="old"> }</li>
<li class="old"> return NULL;</li>
<li class="old">}</li>
<li class="old"></li>
<li class="old">int listObjectsAtLocation(OBJECT *location)</li>
<li class="old">{</li>
<li class="old"> int count = 0;</li>
<li class="old"> OBJECT *obj;</li>
<li class="old"> for (obj = objs; obj < endOfObjs; obj++)</li>
<li class="old"> {</li>
<li class="new"><span class="old"> if (obj != player && </span>isHolding(location, obj))</li>
<li class="old"> {</li>
<li class="old"> if (count++ == 0)</li>
<li class="old"> {</li>
<li class="old"> printf("You see:\n");</li>
<li class="old"> }</li>
<li class="old"> printf("%s\n", obj->description);</li>
<li class="old"> }</li>
<li class="old"> }</li>
<li class="old"> return count;</li>
<li class="old">}</li>
</ol>
</td>
</tr></table>
<div class="explanation">
<p>
Explanation:
</p>
<ul>
<li>Line 6-9:
a little helper function <i>isHolding</i>
which we will be using in various places.
</li>
</ul>
</div>
<p>
In function <i>executeGo</i>,
we can replace most of the <i>if</i> conditions by a check on distance.
</p>
<table class="demo">
<tr><th>Sample output</th></tr>
<tr><td>
Welcome to Little Cave Adventure.<br />
You are in an open field.<br />
You see:<br />
a silver coin<br />
a burly guard<br />
a cave entrance<br />
<br />
--> go guard<br />
You can't get much closer than this.<br />
<br />
--> give silver<br />
You are not holding any silver.<br />
<br />
--> ask silver<br />
There appears to be no silver you can get from a burly guard.<br />
<br />
--> get silver<br />
You pick up a silver coin.<br />
<br />
--> get gold<br />
You don't see any gold here.<br />
<br />
--> give silver<br />
You give a silver coin to a burly guard.<br />
<br />
--> go cave<br />
OK.<br />
You are in a little cave.<br />
You see:<br />
a gold coin<br />
an exit<br />
<br />
--> get gold<br />
You pick up a gold coin.<br />
<br />
--> give gold<br />
There is nobody here to give that to.<br />
<br />
--> quit<br />
<br />
Bye!<br />
</td></tr>
</table>
<table class="code"><tr>
<th>location.h</th>
</tr><tr>
<td>
<ol>
<li class="old">extern void executeLook(const char *noun);</li>
<li class="old">extern void executeGo(const char *noun);</li>
</ol>
</td>
</tr><tr>
<th>location.c</th>
</tr><tr>
<td>
<ol>
<li class="new">#include <stdbool.h></li>
<li class="old">#include <stdio.h></li>
<li class="old">#include <string.h></li>
<li class="old">#include "object.h"</li>
<li class="old">#include "misc.h"</li>
<li class="old">#include "noun.h"</li>
<li class="old"></li>
<li class="old">void executeLook(const char *noun)</li>
<li class="old">{</li>
<li class="old"> if (noun != NULL && strcmp(noun, "around") == 0)</li>
<li class="old"> {</li>
<li class="old"> printf("You are in %s.\n", player->location->description);</li>
<li class="old"> listObjectsAtLocation(player->location);</li>
<li class="old"> }</li>
<li class="old"> else</li>
<li class="old"> {</li>
<li class="old"> printf("I don't understand what you want to see.\n");</li>
<li class="old"> }</li>
<li class="old">}</li>
<li class="old"></li>
<li class="old">void executeGo(const char *noun)</li>
<li class="old">{</li>
<li class="old"> OBJECT *obj = getVisible("where you want to go", noun);</li>
<li class="new"> switch (getDistance(player, obj))</li>
<li class="old"> {</li>
<li class="new"> case distOverthere:</li>
<li class="old"> printf("OK.\n");</li>
<li class="old"> player->location = obj;</li>
<li class="old"> executeLook("around");</li>
<li class="new"> break;</li>
<li class="new"> case distNotHere:</li>
<li class="old"> printf("You don't see any %s here.\n", noun);</li>
<li class="new"> break;</li>
<li class="new"> case distUnknownObject:</li>
<li class="new"> // already handled by getVisible</li>
<li class="new"> break;</li>
<li class="new"> default:</li>
<li class="new"> if (obj->destination != NULL)</li>
<li class="new"> {</li>
<li class="new"> printf("OK.\n");</li>
<li class="new"> player->location = obj->destination;</li>
<li class="new"> executeLook("around");</li>
<li class="new"> }</li>
<li class="new"> else</li>
<li class="new"> {</li>
<li class="new"> printf("You can't get much closer than this.\n");</li>
<li class="new"> }</li>
<li class="old"> }</li>
<li class="old">}</li>
</ol>
</td>
</tr></table>
<p>
The same goes for function <i>executeGet</i>.
</p>
<table class="code"><tr>
<th>inventory.h</th>
</tr><tr>
<td>
<ol>
<li class="old">extern void executeGet(const char *noun);</li>
<li class="old">extern void executeDrop(const char *noun);</li>
<li class="old">extern void executeAsk(const char *noun);</li>
<li class="old">extern void executeGive(const char *noun);</li>
<li class="old">extern void executeInventory(void);</li>
</ol>
</td>
</tr><tr>
<th>inventory.c</th>
</tr><tr>
<td>
<ol>
<li class="new"><span class="old">#include </span><stdbool.h></li>
<li class="new">#include<span class="old"> <stdio.h></span></li>
<li class="old">#include "object.h"</li>
<li class="old">#include "misc.h"</li>
<li class="old">#include "noun.h"</li>
<li class="old">#include "move.h"</li>
<li class="old"></li>
<li class="old">void executeGet(const char *noun)</li>
<li class="old">{</li>
<li class="old"> OBJECT *obj = getVisible("what you want to get", noun);</li>
<li class="new"><span class="old"> </span>switch (getDistance(player, obj))</li>
<li class="old"> {</li>
<li class="new"><span class="old"> </span>case distSelf:</li>
<li class="old"> printf("You should not be doing that to yourself.\n");</li>
<li class="new"><span class="old"> </span>break;</li>
<li class="new"> case distHeld:</li>
<li class="old"> printf("You already have %s.\n", obj->description);</li>
<li class="new"><span class="old"> </span>break;</li>
<li class="new"> case distOverthere:</li>
<li class="new"> printf("Too far away, move closer please.\n");</li>
<li class="new"> break;</li>
<li class="new"> case distUnknownObject:</li>
<li class="new"> // already handled by getVisible</li>
<li class="new"> break;</li>
<li class="new"> default:</li>
<li class="old"> if (obj->location == guard)</li>
<li class="old"> {</li>
<li class="old"> printf("You should ask %s nicely.\n", obj->location->description);</li>
<li class="old"> }</li>
<li class="old"> else</li>
<li class="old"> {</li>
<li class="old"> moveObject(obj, player);</li>
<li class="new"><span class="old"> </span>}</li>
<li class="old"> }</li>
<li class="old">}</li>
<li class="old"></li>
<li class="old">void executeDrop(const char *noun)</li>
<li class="old">{</li>
<li class="old"> moveObject(getPossession(player, "drop", noun), player->location);</li>
<li class="old">}</li>
<li class="old"></li>
<li class="old">void executeAsk(const char *noun)</li>
<li class="old">{</li>
<li class="old"> moveObject(getPossession(actorHere(), "ask", noun), player);</li>
<li class="old">}</li>
<li class="old"></li>
<li class="old">void executeGive(const char *noun)</li>
<li class="old">{</li>
<li class="old"> moveObject(getPossession(player, "give", noun), actorHere());</li>
<li class="old">}</li>
<li class="old"></li>
<li class="old">void executeInventory(void)</li>
<li class="old">{</li>
<li class="old"> if (listObjectsAtLocation(player) == 0)</li>
<li class="old"> {</li>
<li class="old"> printf("You are empty-handed.\n");</li>
<li class="old"> }</li>
<li class="old">}</li>
</ol>
</td>
</tr></table>
<p>
And finally, we will adjust the constraints in <i>noun.c</i>.
I am adding two parameters to function <i>getObject</i>
that make it possible to find a match for a specific noun,
while at the same time ignore any objects that are considered absent.
This will really pay off in the next chapter,
where we will introduce different objects having the same tag.
</p>
<table class="code"><tr>
<th>noun.h</th>
</tr><tr>
<td>
<ol>
<li class="old">extern OBJECT *getVisible(const char *intention, const char *noun);</li>
<li class="old">extern OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun);</li>
</ol>
</td>
</tr><tr>
<th>noun.c</th>
</tr><tr>
<td>
<ol>
<li class="old">#include <stdbool.h></li>
<li class="old">#include <stdio.h></li>
<li class="old">#include <string.h></li>
<li class="old">#include "object.h"</li>
<li class="old">#include "misc.h"</li>
<li class="old"></li>
<li class="old">static bool objectHasTag(OBJECT *obj, const char *noun)</li>
<li class="old">{</li>
<li class="old"> return noun != NULL && *noun != '\0' && strcmp(noun, obj->tag) == 0;</li>
<li class="old">}</li>
<li class="old"></li>
<li class="new"><span class="old">static OBJECT *getObject(const char </span>*noun, OBJECT *from, DISTANCE maxDistance)</li>
<li class="old">{</li>
<li class="old"> OBJECT *obj, *res = NULL;</li>
<li class="old"> for (obj = objs; obj < endOfObjs; obj++)</li>
<li class="old"> {</li>
<li class="new"><span class="old"> if (objectHasTag(obj, </span>noun) && getDistance(from, obj) <= maxDistance)</li>
<li class="old"> {</li>
<li class="old"> res = obj;</li>
<li class="old"> }</li>
<li class="old"> }</li>
<li class="old"> return res;</li>
<li class="old">}</li>
<li class="old"></li>
<li class="old">OBJECT *getVisible(const char *intention, const char *noun)</li>
<li class="old">{</li>
<li class="new"><span class="old"> OBJECT *obj = </span>getObject(noun, player, distOverthere);</li>
<li class="old"> if (obj == NULL)</li>
<li class="old"> {</li>
<li class="new"><span class="old"> </span>if (getObject(noun, player, distNotHere) == NULL)</li>
<li class="new"> {</li>
<li class="old"> printf("I don't understand %s.\n", intention);</li>
<li class="old"> }</li>
<li class="old"> else</li>
<li class="old"> {</li>
<li class="old"> printf("You don't see any %s here.\n", noun);</li>
<li class="new"><span class="old"> </span>}</li>
<li class="old"> }</li>
<li class="old"> return obj;</li>
<li class="old">}</li>
<li class="old"></li>
<li class="old">OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun)</li>
<li class="old">{</li>
<li class="old"> OBJECT *obj = NULL;</li>
<li class="old"> if (from == NULL)</li>
<li class="old"> {</li>
<li class="old"> printf("I don't understand who you want to %s.\n", verb);</li>
<li class="old"> }</li>
<li class="new"><span class="old"> else if ((obj = </span>getObject(noun, from, distHeldContained)) == NULL)</li>
<li class="new"> {</li>
<li class="new"> if (getObject(noun, player, distNotHere)<span class="old"> == NULL)</span></li>
<li class="old"> {</li>
<li class="old"> printf("I don't understand what you want to %s.\n", verb);</li>
<li class="old"> }</li>
<li class="old"> else if (from == player)</li>
<li class="old"> {</li>
<li class="old"> printf("You are not holding any %s.\n", noun);</li>
<li class="old"> }</li>
<li class="old"> else</li>
<li class="old"> {</li>
<li class="old"> printf("There appears to be no %s you can get from %s.\n",</li>
<li class="old"> noun, from->description);</li>
<li class="old"> }</li>
<li class="new"><span class="old"> </span>}</li>
<li class="new"> else if (obj == from)</li>
<li class="new"> {</li>
<li class="new"> printf("You should not be doing that to %s.\n", obj->description);</li>
<li class="old"> obj = NULL;</li>
<li class="old"> }</li>
<li class="old"> return obj;</li>
<li class="old">}</li>
</ol>
</td>
</tr></table>
<div class="explanation">
<p>
Explanation:
</p>
<ul>
<li>Line 17:
only consider objects with a distance from object ‘from’
that is less than or equal to <i>maxDistance</i>;
any objects farther away are ignored.
Because of the way enum <i>DISTANCE</i> is numbered
(increasing values for increasing distance),
the ‘less than’ operator (<tt><</tt>)
automatically means ‘nearer than’.
</li>
<li>Line 27:
<i>getObject</i> is called with distance <i>distOverthere</i>,
meaning everything outside the player’s visible range is ignored.
</li>
<li>Line 49:
<i>getObject</i> is called with distance <i>distHeldContained</i>,
which limits the scope to any objects held by the player
(and the player himself).
</li>
<li>Lines 30 and 51:
if the first call to <i>getObject</i> yields nothing,
then we always make a second call with a bigger distance,
to decide between a noun that was ignored (based on its distance),
and a noun that just could not be found to match <i>any</i> object.
</li>
</ul>
</div>
<p>
The other modules
(<i><i>main.c</i>, parsexec.c</i>, <i>move.c</i>, <i>object.c</i>)
remain unchanged, you can see them in previous chapters.
</p>
<p>
In this chapter, the concept of <i>distance</i> was mainly used
to choose between different responses the game can give to the user.
But the benefits of distance are not reserved to the <b>output</b> side;
it can be used equally well to make improvements on the <b>input</b> side.
In the next chapter,
we will use distance to improve the recognition of nouns entered by the user.
</p>
<hr />
<table class="download"><tr><td>
<a class="button" href="code07/src.zip">⭳ Download source code</a>
<a class="button" href="https://repl.it/github/helderman/htpataic">🌀 Run on Repl.it</a>
</td></tr></table>
<p>
Next chapter: <a href="htpataic08.html">8. North, east, south, west</a>
</p>
</body>
</html>