-
Notifications
You must be signed in to change notification settings - Fork 10
/
adv440.c
4557 lines (4351 loc) · 185 KB
/
adv440.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
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
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include <assert.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef Z_MACHINE
#ifdef SAVE_AND_RESTORE
extern int attempt_save(void);
extern int attempt_restore(void);
#endif /* SAVE_AND_RESTORE */
/* The Z-machine's "@random 42" instruction returns a value in the range 1..42. */
int ran(int range) =
"\t@random r0 -> r0;\n"
"\t@sub r0 0+1 -> r0;\n";
int abs(int x) { return x < 0 ? -x : x; }
#else
int ran(int range) { return rand() % range; }
#endif /* Z_MACHINE */
#define SOFT_NL "\n"
#define SOFT_HYPHEN "-\n"
#define EMDASH(x) "\xE2\x80\x94" /* U+2014 "EM DASH" */
bool pct(int percent) { return (ran(100) < percent); }
bool streq(const char *input, const char *pattern)
{
/* Pattern "NORTH-W" matches "NORTH", "NORTH-", and "NORTH-WIBBLE",
* but not "NORT" or "NORTHWEST".
* The order of vocabulary words in Pike means that "NORTH-" is
* a synonym for "NORTH-E" and "SOUTH-" is a synonym for "SOUTH-E".
*/
int n = strlen(input);
if (n < 5) n = 5;
if (n > 7) n = 7;
return !strncmp(input, pattern, n);
}
/*========== Forward declarations. ========================================
*/
void dwarves_upset(void);
void give_up(void);
void quit(void);
/*========== The vocabulary. ==============================================
* This section corresponds to sections 4--17 in Knuth.
*/
typedef enum {
WordClass_None, WordClass_Motion, WordClass_Object,
WordClass_Action, WordClass_Message
} WordClass;
struct HashEntry {
char text[8];
int meaning;
};
#define HASH_PRIME 1009
struct HashEntry hash_table[HASH_PRIME];
void new_word(const char *w, int m)
{
int h = 0;
assert(strlen(w) <= 7);
for (const char *p = w; *p != '\0' && p != w+5; ++p) {
h = ((h << 1) + *p) % HASH_PRIME;
}
while (hash_table[h].meaning != 0)
h = (h+1) % HASH_PRIME;
strcpy(hash_table[h].text, w);
hash_table[h].meaning = m;
}
#define new_motion_word new_word
#define new_object_word new_word
#define new_action_word new_word
#define new_message_word new_word
int lookup(const char *w)
{
int h = 0;
for (const char *p = w; *p != '\0' && p != w+5; ++p) {
h = ((h << 1) + *p) % HASH_PRIME;
}
while (hash_table[h].meaning != 0) {
if (streq(w, hash_table[h].text)) return hash_table[h].meaning;
h = (h+1) % HASH_PRIME;
}
return 0;
}
#undef HASH_PRIME
#define NOTHING 0
typedef enum {
MIN_MOTION=100,
ROAD=MIN_MOTION,ENTER,UPSTREAM,DOWNSTREAM,FOREST,FORWARD,
BACK,VALLEY,STAIRS,OUT,HOUSE,GULLY,STREAM,ROCK,
BED,CRAWL,COBBLES,IN,SURFACE,NOWHERE,DARK,PASSAGE,
LOW,CANYON,AWKWARD,PANTRY,VIEW,U,D,PIT,OUTDOORS,
CRACK,STEPS,DOME,LEFT,RIGHT,HALL,JUMP,BARREN,
OVER,ACROSS,E,W,N,S,NE,SE,SW,NW,HOLE,
WALL,BROKEN,Y2,CLIMB,LOOK,FLOOR,ROOM,SLIT,
SLAB,XYZZY,DEPRESSION,ENTRANCE,PLUGH,SECRET,
CAVE,CROSS,BEDQUILT,PLOVER,ORIENTAL,CAVERN,
SHELL,RESERVOIR,OFFICE,FORK,
REFLECT,TESTO,TOWER,BEDCHAMBER,GALLEY,
MAX_MOTION=GALLEY
} MotionWord;
typedef enum {
MIN_OBJ=200,
KEYS=MIN_OBJ, LAMP, GRATE, GRATE_, CAGE, ROD, ROD2, TREADS, TREADS_,
BIRD, RUSTY_DOOR, PILLOW, SNAKE, FISSURE, FISSURE_, TABLET, CLAM, OYSTER,
MAG, DWARF, KNIFE, FOOD, BOTTLE, WATER, OIL, MIRROR, MIRROR_, PLANT,
PLANT2, PLANT2_, STALACTITE, SHADOW, SHADOW_, AXE, DRAWINGS, PIRATE,
DRAGON, DRAGON_, CHASM, CHASM_, TROLL, TROLL_, NO_TROLL, NO_TROLL_,
BEAR, MESSAGE, GORGE, MACHINE, BATTERIES, MOSS,
OWL, WEB, SPIDER, DOCUMENTS, SPOON, HORN, RATS, GIANT, FLAGSTONE,
GOLD, DIAMONDS, SILVER, JEWELS, COINS, CHEST, EGGS,
TRIDENT, VASE, EMERALD, PYRAMID, PEARL, RUG, RUG_, SPICES, CHAIN,
CROWN, TUSK, CHALICE, RUBY, ORB, FAKE_ORB,
MAX_OBJ=FAKE_ORB
} ObjectWord;
typedef enum {
MIN_ACTION=300,
TAKE=MIN_ACTION, DROP, OPEN, CLOSE, ON, OFF, WAVE, CALM, GO,
RELAX, POUR, EAT, DRINK, RUB, TOSS, WAKE, FEED, FILL, BREAK, BLAST,
KILL, SAY, READ, FEEFIE, BRIEF, FIND,
HOOT, BLOW, FLY, RIDE, SLEEP, REST, SCREAM,
INVENTORY, SCORE,
#ifdef SAVE_AND_RESTORE
SAVE, RESTORE,
#endif /* SAVE_AND_RESTORE */
QUIT,
MAX_ACTION=QUIT
} ActionWord;
typedef enum {
MIN_MESSAGE=400,
ABRA=MIN_MESSAGE, HELP, TREES, DIG, LOST, MIST, FUCK, STOP, INFO, SWIM,
PUSH, EPNS, BONES, DEBRIS, DUNG, GRILL, CURTAIN, PORTCULLIS, STONE,
MAX_MESSAGE=STONE
} MessageWord;
WordClass word_class(int word)
{
if (word == NOTHING) {
return WordClass_None;
} else if (MIN_MOTION <= word && word <= MAX_MOTION) {
return WordClass_Motion;
} else if (MIN_OBJ <= word && word <= MAX_OBJ) {
return WordClass_Object;
} else if (MIN_ACTION <= word && word <= MAX_ACTION) {
return WordClass_Action;
} else if (MIN_MESSAGE <= word && word <= MAX_MESSAGE) {
return WordClass_Message;
} else {
assert(false);
return WordClass_None;
}
}
const char ok[] = "OK."; /* Woods' Fortran version didn't include the period, by the way. */
const char pitch_dark_msg[] = "It is now pitch dark. If you proceed you will most likely fall into a pit.";
void build_vocabulary(void)
{
new_motion_word("road", ROAD); new_motion_word("hill", ROAD);
new_motion_word("enter", ENTER);
new_motion_word("upstrea", UPSTREAM);
new_motion_word("downstr", DOWNSTREAM);
new_motion_word("forest", FOREST);
new_motion_word("forward", FORWARD); new_motion_word("continu", FORWARD);
new_motion_word("onward", FORWARD);
new_motion_word("back", BACK); new_motion_word("return", BACK);
new_motion_word("retreat", BACK);
new_motion_word("valley", VALLEY);
new_motion_word("stair", STAIRS); new_motion_word("stairs", STAIRS);
new_motion_word("stairca", STAIRS);
new_motion_word("out", OUT); new_motion_word("outside", OUT);
new_motion_word("exit", OUT); new_motion_word("leave", OUT);
new_motion_word("buildin", HOUSE); new_motion_word("house", HOUSE);
new_motion_word("gully", GULLY);
new_motion_word("stream", STREAM);
new_motion_word("rock", ROCK);
new_motion_word("bed", BED);
new_motion_word("crawl", CRAWL);
new_motion_word("cobble", COBBLES); new_motion_word("cobbles", COBBLES);
new_motion_word("inward", IN); new_motion_word("inwards", IN);
new_motion_word("inside", IN); new_motion_word("in", IN);
new_motion_word("surface", SURFACE);
new_motion_word("null", NOWHERE); new_motion_word("nowhere", NOWHERE);
new_motion_word("dark", DARK);
new_motion_word("passage", PASSAGE); new_motion_word("tunnel", PASSAGE);
new_motion_word("low", LOW);
new_motion_word("canyon", CANYON);
new_motion_word("awkward", AWKWARD);
new_motion_word("pantry", PANTRY);
new_motion_word("view", VIEW);
new_motion_word("upward", U); new_motion_word("upwards", U);
new_motion_word("up", U); new_motion_word("u", U);
new_motion_word("above", U); new_motion_word("ascend", U);
new_motion_word("d", D); new_motion_word("downwar", D);
new_motion_word("down", D); new_motion_word("descend", D);
new_motion_word("pit", PIT);
new_motion_word("outdoor", OUTDOORS);
new_motion_word("crack", CRACK);
new_motion_word("steps", STEPS);
new_motion_word("dome", DOME);
new_motion_word("left", LEFT);
new_motion_word("right", RIGHT);
new_motion_word("hall", HALL);
new_motion_word("jump", JUMP);
new_motion_word("barren", BARREN);
new_motion_word("over", OVER);
new_motion_word("across", ACROSS);
new_motion_word("east", E); new_motion_word("e", E);
new_motion_word("west", W); new_motion_word("w", W);
new_motion_word("north", N); new_motion_word("n", N);
new_motion_word("south", S); new_motion_word("s", S);
new_motion_word("ne", NE); new_motion_word("north-e", NE);
new_motion_word("se", SE); new_motion_word("south-e", SE);
new_motion_word("sw", SW); new_motion_word("south-w", SW);
new_motion_word("nw", NW); new_motion_word("north-w", NW);
new_motion_word("hole", HOLE);
new_motion_word("wall", WALL);
new_motion_word("broken", BROKEN);
new_motion_word("y2", Y2);
new_motion_word("climb", CLIMB);
new_motion_word("look", LOOK); new_motion_word("examine", LOOK);
new_motion_word("touch", LOOK); new_motion_word("describ", LOOK);
new_motion_word("floor", FLOOR);
new_motion_word("room", ROOM);
new_motion_word("slit", SLIT);
new_motion_word("slab", SLAB); new_motion_word("slabroo", SLAB);
new_motion_word("xyzzy", XYZZY);
new_motion_word("depress", DEPRESSION);
new_motion_word("entranc", ENTRANCE);
new_motion_word("plugh", PLUGH);
new_motion_word("secret", SECRET);
new_motion_word("cave", CAVE);
new_motion_word("cross", CROSS);
new_motion_word("bedquil", BEDQUILT);
new_motion_word("plover", PLOVER);
new_motion_word("orienta", ORIENTAL);
new_motion_word("cavern", CAVERN);
new_motion_word("shell", SHELL);
new_motion_word("reservo", RESERVOIR);
new_motion_word("main", OFFICE); new_motion_word("office", OFFICE);
new_motion_word("fork", FORK);
new_motion_word("reflect", REFLECT);
new_motion_word("testo", TESTO);
new_motion_word("tower", TOWER);
new_motion_word("bedch", BEDCHAMBER);
new_motion_word("galley", GALLEY);
new_object_word("key", KEYS); new_object_word("keys", KEYS);
new_object_word("lamp", LAMP); new_object_word("lantern", LAMP);
new_object_word("headlam", LAMP);
new_object_word("grate", GRATE);
new_object_word("cage", CAGE);
new_object_word("rod", ROD);
new_object_word("bird", BIRD);
new_object_word("door", RUSTY_DOOR);
new_object_word("pillow", PILLOW); new_object_word("velvet", PILLOW);
new_object_word("snake", SNAKE);
new_object_word("fissure", FISSURE);
new_object_word("tablet", TABLET);
new_object_word("clam", CLAM);
new_object_word("oyster", OYSTER);
new_object_word("magazin", MAG); new_object_word("issue", MAG);
new_object_word("spelunk", MAG); new_object_word("\"spelun", MAG);
new_object_word("dwarf", DWARF); new_object_word("dwarves", DWARF);
new_object_word("knife", KNIFE); new_object_word("knives", KNIFE);
new_object_word("food", FOOD); new_object_word("rations", FOOD);
new_object_word("bottle", BOTTLE); new_object_word("jar", BOTTLE);
new_object_word("water", WATER); new_object_word("h2o", WATER);
new_object_word("oil", OIL);
new_object_word("mirror", MIRROR);
new_object_word("plant", PLANT); new_object_word("beansta", PLANT);
new_object_word("stalact", STALACTITE);
new_object_word("shadow", SHADOW); new_object_word("figure", SHADOW);
new_object_word("axe", AXE);
new_object_word("drawing", DRAWINGS);
new_object_word("pirate", PIRATE);
new_object_word("dragon", DRAGON);
new_object_word("chasm", CHASM);
new_object_word("troll", TROLL);
new_object_word("bear", BEAR);
new_object_word("message", MESSAGE);
new_object_word("volcano", GORGE); new_object_word("geyser", GORGE);
new_object_word("vending", MACHINE); new_object_word("machine", MACHINE);
new_object_word("battery", BATTERIES); new_object_word("batteri", BATTERIES);
new_object_word("moss", MOSS); new_object_word("carpet", MOSS);
new_object_word("owl", OWL);
new_object_word("web", WEB);
new_object_word("spider", SPIDER);
new_object_word("documen", DOCUMENTS); new_object_word("legal", DOCUMENTS);
new_object_word("deed", DOCUMENTS); new_object_word("deeds", DOCUMENTS);
new_object_word("plan", DOCUMENTS); new_object_word("plans", DOCUMENTS);
new_object_word("papers", DOCUMENTS);
new_object_word("spoon", SPOON); new_object_word("inscrip", SPOON);
new_object_word("horn", HORN);
new_object_word("rat", RATS); new_object_word("rats", RATS);
new_object_word("giant", GIANT); new_object_word("man", GIANT);
new_object_word("flagsto", FLAGSTONE);
new_object_word("gold", GOLD); new_object_word("nugget", GOLD);
new_object_word("diamond", DIAMONDS);
new_object_word("silver", SILVER); new_object_word("bars", SILVER);
new_object_word("jewels", JEWELS);
new_object_word("jewelry", JEWELS);
new_object_word("coins", COINS);
new_object_word("chest", CHEST); new_object_word("box", CHEST);
new_object_word("treasur", CHEST);
new_object_word("eggs", EGGS); new_object_word("egg", EGGS);
new_object_word("nest", EGGS);
new_object_word("trident", TRIDENT);
new_object_word("ming", VASE); new_object_word("vase", VASE);
new_object_word("shards", VASE); new_object_word("pottery", VASE);
new_object_word("emerald", EMERALD);
new_object_word("platinu", PYRAMID); new_object_word("pyramid", PYRAMID);
new_object_word("pearl", PEARL);
new_object_word("persian", RUG); new_object_word("rug", RUG);
new_object_word("spices", SPICES);
new_object_word("chain", CHAIN);
new_object_word("crown", CROWN);
new_object_word("ivory", TUSK); new_object_word("tusk", TUSK);
new_object_word("inlaid", CHALICE); new_object_word("chalice", CHALICE);
new_object_word("unicorn", CHALICE);
new_object_word("ruby", RUBY);
/* The noun "GLOBE" works at the cellar view; the nouns "CRYSTAL" and "ORB"
* work only in the circular cellar itself. */
new_object_word("orb", ORB); new_object_word("crystal", ORB);
new_object_word("globe", FAKE_ORB);
new_action_word("take", TAKE); new_action_word("carry", TAKE);
new_action_word("keep", TAKE); new_action_word("catch", TAKE);
new_action_word("capture", TAKE); new_action_word("steal", TAKE);
new_action_word("get", TAKE); new_action_word("tote", TAKE);
new_action_word("lift", TAKE); new_action_word("raise", TAKE);
new_action_word("drop", DROP); new_action_word("release", DROP);
new_action_word("free", DROP); new_action_word("discard", DROP);
new_action_word("dump", DROP);
new_action_word("open", OPEN); new_action_word("unlock", OPEN);
new_action_word("close", CLOSE); new_action_word("lock", CLOSE);
new_action_word("shut", CLOSE);
new_action_word("light", ON); new_action_word("on", ON);
new_action_word("extingu", OFF); new_action_word("off", OFF);
new_action_word("wave", WAVE); new_action_word("shake", WAVE);
new_action_word("swing", WAVE);
new_action_word("calm", CALM); new_action_word("placate", CALM);
new_action_word("tame", CALM);
new_action_word("walk", GO); new_action_word("run", GO);
new_action_word("travel", GO); new_action_word("go", GO);
new_action_word("proceed", GO); new_action_word("explore", GO);
new_action_word("goto", GO); new_action_word("follow", GO);
new_action_word("turn", GO);
new_action_word("nothing", RELAX);
new_action_word("pour", POUR); new_action_word("empty", POUR);
new_action_word("eat", EAT); new_action_word("devour", EAT);
new_action_word("drink", DRINK);
new_action_word("rub", RUB); new_action_word("polish", RUB);
new_action_word("throw", TOSS); new_action_word("toss", TOSS);
new_action_word("wake", WAKE); new_action_word("disturb", WAKE);
new_action_word("feed", FEED);
new_action_word("fill", FILL);
new_action_word("break", BREAK); new_action_word("smash", BREAK);
new_action_word("shatter", BREAK);
new_action_word("blast", BLAST); new_action_word("detonat", BLAST);
new_action_word("ignite", BLAST); new_action_word("blowup", BLAST);
new_action_word("explode", BLAST);
new_action_word("attack", KILL); new_action_word("kill", KILL);
new_action_word("fight", KILL); new_action_word("hit", KILL);
new_action_word("strike", KILL);
new_action_word("say", SAY);
new_action_word("chant", SAY);
new_action_word("sing", SAY);
new_action_word("utter", SAY);
new_action_word("mumble", SAY);
new_action_word("read", READ); new_action_word("perus", READ);
new_action_word("fee", FEEFIE); new_action_word("fie", FEEFIE);
new_action_word("foe", FEEFIE); new_action_word("foo", FEEFIE);
new_action_word("fum", FEEFIE);
new_action_word("brief", BRIEF);
new_action_word("find", FIND); new_action_word("where", FIND);
new_action_word("seek", FIND);
new_action_word("read", READ); new_action_word("peruse", READ);
new_action_word("hoot", HOOT);
new_action_word("blow", BLOW); new_action_word("play", BLOW);
new_action_word("fly", FLY);
new_action_word("mount", RIDE); new_action_word("ride", RIDE);
new_action_word("sleep", SLEEP);
new_action_word("rest", REST);
new_action_word("scream", SCREAM);
new_action_word("invento", INVENTORY);
new_action_word("score", SCORE);
new_action_word("quit", QUIT); new_action_word("end", QUIT);
#ifdef SAVE_AND_RESTORE
new_action_word("save", SAVE);
new_action_word("resto", RESTORE);
#endif /* SAVE_AND_RESTORE */
/* Finally, our vocabulary is rounded out by words like HELP, which
* trigger the printing of fixed messages. */
new_message_word("abra", ABRA);
new_message_word("abracad", ABRA);
new_message_word("openses", ABRA);
new_message_word("sesamy", ABRA); /* Pike does not recognize SESAME (the correct spelling). */
new_message_word("shazah", ABRA); /* Pike does not recognize SHAZAM (the spelling Woods presumably intended). */
new_message_word("hocus", ABRA);
new_message_word("pocus", ABRA);
new_message_word("help", HELP);
new_message_word("?", HELP);
new_message_word("tree", TREES);
new_message_word("trees", TREES);
new_message_word("dig", DIG);
new_message_word("excavat", DIG);
new_message_word("lost", LOST);
new_message_word("mist", MIST);
new_message_word("fuck", FUCK);
new_message_word("stop", STOP);
new_message_word("info", INFO);
new_message_word("informa", INFO);
new_message_word("swim", SWIM);
new_message_word("push", PUSH); new_message_word("pull", PUSH);
new_message_word("epns", EPNS);
new_message_word("bone", BONES); new_message_word("bones", BONES);
new_message_word("debris", DEBRIS);
new_message_word("dung", DUNG); new_message_word("droppin", DUNG);
new_message_word("turd", DUNG); new_message_word("turds", DUNG);
new_message_word("shit", DUNG);
new_message_word("grill", GRILL);
new_message_word("curtain", CURTAIN);
new_message_word("portcul", PORTCULLIS);
new_message_word("stone", STONE);
new_message_word("pebbl", STONE); /* Pike recognizes PEBBLABC. */
/* Sanity checking. See the comments in streq(). */
assert(lookup("north") == N);
assert(lookup("south") == S);
assert(lookup("north-") == NE);
assert(lookup("south-") == SE);
}
/*========== Locations. ===================================================
* This section corresponds to sections 18--62 in Knuth.
*/
typedef enum {
R_INHAND = -1, R_LIMBO = 0,
R_ROAD, R_HILL, R_HOUSE, R_VALLEY, R_FOREST, R_FOREST2, R_SLIT, R_OUTSIDE,
R_INSIDE, MIN_IN_CAVE = R_INSIDE,
R_COBBLES, R_DEBRIS, R_AWK, R_BIRD, R_SPIT,
R_EMIST, MIN_LOWER_LOC = R_EMIST,
R_NUGGET, R_EFISS, R_WFISS, R_WMIST,
R_LIKE1, R_LIKE2, R_LIKE3, R_LIKE4, R_LIKE5, R_LIKE6, R_LIKE7,
R_LIKE8, R_LIKE9, R_LIKE10, R_LIKE11, R_LIKE12, R_LIKE13, R_LIKE14,
R_BRINK, R_ELONG, R_WLONG,
R_DIFF0, R_DIFF1, R_DIFF2, R_DIFF3, R_DIFF4, R_DIFF5,
R_DIFF6, R_DIFF7, R_DIFF8, R_DIFF9, R_DIFF10,
R_PONY, R_CROSS, R_HMK, R_WEST, R_SOUTH, R_NS, R_Y2, R_JUMBLE, R_WINDOE,
R_DIRTY, R_CLEAN, R_WET, R_DUSTY, R_COMPLEX,
R_SHELL, R_ARCHED, R_RAGGED, R_SAC, R_ANTE, R_WITT,
R_BEDQUILT, R_SWISS, R_SOFT,
R_E2PIT, R_W2PIT, R_EPIT, R_WPIT,
R_NARROW, R_GIANT, R_BLOCK, R_IMMENSE,
R_FALLS, R_INCLINE, R_ABOVEP, R_SJUNC,
R_TITE, R_LOW, R_CRAWL, R_WINDOW,
R_ORIENTAL, R_MISTY, R_ALCOVE, R_PLOVER, R_DARK,
R_SLAB, R_ABOVER, R_MIRROR, R_RES,
R_SCAN1, R_SCAN2, R_SCAN3, R_SECRET,
R_WIDE, R_TIGHT, R_TALL, R_BOULDERS,
R_SLOPING, R_SWSIDE,
R_DEAD0, R_DEAD1, R_PIRATES_NEST, R_DEAD3, R_DEAD4, R_DEAD5,
R_DEAD6, R_DEAD7, R_DEAD8, R_DEAD9, R_DEAD10, R_DEAD11,
R_NESIDE, R_CORR, R_FORK, R_WARM, R_VIEW, R_CHAMBER,
R_LIME, R_FBARR, R_BARR,
R_141,
R_142,
R_143,
R_144,
R_DUNGEON,
R_146,
R_147,
R_148,
R_149,
R_150,
R_151,
R_152,
R_153,
R_CELLAR,
R_155,
R_156,
R_157,
R_158,
R_159,
R_160,
R_161,
R_162,
R_163,
R_164,
R_165,
R_166,
R_168,
R_169,
R_170,
R_171,
R_172,
R_PANTRY,
R_174,
R_175,
R_176,
R_177,
R_178,
R_179,
R_180,
R_181,
R_182,
R_183,
R_184,
R_185,
R_186,
R_187,
R_188,
R_189,
R_190,
R_191,
R_192,
R_193,
R_194,
R_195,
R_197,
R_198,
R_NEEND, R_SWEND,
R_NECK, R_LOSE, R_CLIMB, R_CHECK,
R_THRU, R_DUCK, R_UPNOUT,
R_DIDIT,
R_167, R_196,
R_199, MAX_LOC = R_199,
R_PPASS, R_PDROP,
R_TROLL,
R_TPASS, R_GRAB,
FIRST_REMARK
} Location;
typedef struct {
MotionWord mot;
int cond;
Location dest;
} Instruction;
enum flagsBits {
F_CAVE_HINT = 0x008,
F_BIRD_HINT = 0x010,
F_SNAKE_HINT = 0x020,
F_TWIST_HINT = 0x040,
F_DARK_HINT = 0x080,
F_WITT_HINT = 0x100
};
Instruction travels[951];
Instruction *start[MAX_LOC+2];
struct Place {
bool has_long_desc;
bool has_short_desc;
unsigned int flags;
ObjectWord objects; /* first object at this location, or NOTHING */
int visits;
};
struct Place places[MAX_LOC+1];
bool now_in_darkness(Location loc);
void describe_object(ObjectWord t, Location loc);
bool is_forced(Location loc);
void make_loc(Instruction *q, Location x, bool has_long_desc, bool has_short_desc, unsigned int f)
{
static int last_x = 0;
assert(x == last_x + 1);
last_x = x;
assert(has_long_desc || is_forced(x));
places[x].has_long_desc = has_long_desc;
places[x].has_short_desc = has_short_desc;
places[x].flags = f;
places[x].objects = NOTHING;
places[x].visits = 0;
start[x] = q;
}
void make_inst(Instruction *q, MotionWord m, int c, Location d)
{
assert(&travels[0] <= q && q < &travels[951]);
assert(m==0 || (MIN_MOTION <= m && m <= MAX_MOTION));
q->mot = m;
q->cond = c;
q->dest = d;
}
#define make_ins(m, d) make_inst(q++, m, 0, d)
#define make_cond_ins(m, c, d) make_inst(q++, m, c, d)
#define ditto(m) make_inst(q, m, q[-1].cond, q[-1].dest); ++q;
#define only_if_toting(t) (100 + (t-MIN_OBJ))
#define only_if_here(t) (200 + (t-MIN_OBJ))
#define unless_prop(t, p) (300 + (t-MIN_OBJ) + 100*p)
#define remark(n) (FIRST_REMARK + n)
void build_travel_table(void)
{
Instruction *q = travels;
/* This is a huge kludge to make room descriptions optimizable on the Z-machine.
* We don't allow the description strings to show up outside of "sizeof" context
* in this routine. This does have the nasty fragile side effect that any short
* description of 4 or 8 characters will be ignored; but fortunately no such
* descriptions exist. */
#define make_loc(q,x,l,s,f) make_loc(q,x,sizeof(l)!=sizeof(NULL),sizeof(s)!=sizeof(NULL),f)
#define make_ins(m, d) make_inst(q++, m, 0, d)
#define make_cond_ins(m, c, d) make_inst(q++, m, c, d)
#define ditto(m) make_inst(q, m, q[-1].cond, q[-1].dest); ++q;
#include "locs.h"
#undef ditto
#undef make_cond_ins
#undef make_ins
#undef make_loc
/* The remaining "locations" R_PPASS, R_PDROP, R_TROLL, R_TPASS, and R_GRAB are special. */
start[R_PPASS] = q;
}
void print_long_room_description(Location loc)
{
if (!places[loc].has_long_desc) {
assert(is_forced(loc));
return;
}
switch (loc) {
#define make_loc(q,x,l,s,f) case x: puts(l); break;
#define make_ins(x,y)
#define make_cond_ins(x,y,z)
#define ditto(x)
#include "locs.h"
#undef ditto
#undef make_cond_ins
#undef make_ins
#undef make_loc
default:
assert(false);
}
}
void print_short_room_description(Location loc)
{
if (!places[loc].has_short_desc) {
assert(is_forced(loc));
return;
}
switch (loc) {
#define make_loc(q,x,l,s,f) case x: puts(s); break;
#define make_ins(x,y)
#define make_cond_ins(x,y,z)
#define ditto(x)
#include "locs.h"
#undef ditto
#undef make_cond_ins
#undef make_ins
#undef make_loc
default:
assert(false);
}
}
bool is_forced(Location loc)
{
switch (loc) {
case R_NECK:
case R_LOSE:
case R_CLIMB:
case R_CHECK:
case R_THRU:
case R_DUCK:
case R_UPNOUT:
case R_DIDIT:
case R_167:
case R_196:
case R_199:
return true;
default:
return false;
}
}
bool has_light(Location loc)
{
switch (loc) {
case R_ROAD: case R_HILL: case R_HOUSE: case R_VALLEY:
case R_FOREST: case R_FOREST2: case R_SLIT: case R_OUTSIDE:
case R_INSIDE: case R_COBBLES:
case R_PLOVER: case R_VIEW:
case R_NEEND: case R_SWEND:
case R_142: case R_143:
case R_155: case R_156:
case R_157: case R_158:
case R_159: case R_160:
case R_161: case R_162: case R_163:
case R_180:
return true;
default:
return false;
}
}
bool has_water(Location loc)
{
switch (loc) {
case R_ROAD: case R_HOUSE: case R_VALLEY: case R_SLIT:
case R_WET: case R_FALLS: case R_RES:
case R_PANTRY: case R_176: case R_178: case R_179:
case R_186: case R_187:
return true;
default:
return false;
}
}
bool has_oil(Location loc)
{
return (loc == R_EPIT);
}
bool has_sewage(Location loc)
{
switch (loc) {
case R_176: case R_178: case R_179:
case R_181: case R_182: case R_183:
case R_184: case R_185: case R_186:
return true;
default:
return false;
}
}
bool dwarves_wont_follow_into(Location loc)
{
switch (loc) {
case R_176: case R_177: case R_178:
case R_179: case R_180: case R_181:
case R_182: case R_183: case R_184:
case R_185: case R_186: case R_187:
case R_188:
return true;
default:
return false;
}
}
bool forbidden_to_owl(Location loc)
{
switch (loc) {
case R_ROAD: case R_HILL: case R_HOUSE: case R_VALLEY:
case R_FOREST: case R_FOREST2: case R_SLIT: case R_OUTSIDE:
case R_INSIDE:
case R_176: case R_177: case R_178:
case R_179: case R_180: case R_181:
case R_182: case R_183: case R_184:
case R_185: case R_186: case R_187:
return true;
default:
return false;
}
}
/*========== Data structures for objects. =================================
* This section corresponds to sections 63--70 in Knuth.
*/
struct ObjectData {
ObjectWord link; /* next object at this location, or NOTHING */
ObjectWord base; /* NOTHING for mobile objects */
int prop;
Location original_place;
Location place;
const char *name;
const char *desc[4]; /* .prop ranges from 0 to 3 */
} objs_[MAX_OBJ+1 - MIN_OBJ];
#define objs(t) objs_[(t)-MIN_OBJ]
int holding_count; /* how many objects have objs(t).place < 0? */
Location last_knife_loc = R_LIMBO;
int tally = 20; /* treasures awaiting you */
int lost_treasures; /* treasures that you won't find */
int thirst = -200; /* when it reaches 900, you're dead */
#define toting(t) (objs(t).place < 0)
#define is_immobile(t) (objs(t).base != NOTHING)
#define there(t, loc) (objs(t).place == (loc))
/* Return true if t is a treasure. Notice that RUG_ (the other half
* of the schizoid rug) does not need to be a treasure. You can
* never be carrying it; the pirate can't steal it; and its prop
* value is irrelevant (we use the prop value of the base object RUG).
* Also, FAKE_ORB *must* not be a treasure, or else the pirate might
* steal it.
*/
bool is_treasure(ObjectWord t)
{
switch (t) {
case GOLD: case DIAMONDS: case SILVER: case JEWELS:
case COINS: case CHEST: case EGGS: case TRIDENT:
case VASE: case EMERALD: case PYRAMID: case PEARL:
case RUG: case SPICES: case CHAIN:
case CROWN: case TUSK: case CHALICE: case RUBY: case ORB:
return true;
default:
return false;
}
}
ObjectWord bottle_contents(void)
{
switch (objs(BOTTLE).prop) {
case 0: return WATER;
case 2: return OIL;
/* other valid values: 1, -2 (post-closing) */
}
return NOTHING;
}
/* Return true if t is at loc, or being carried. */
bool here(ObjectWord t, Location loc)
{
return toting(t) || there(t, loc);
}
void drop(ObjectWord t, Location l)
{
assert(objs(t).place == R_INHAND || objs(t).place == R_LIMBO);
assert(objs(t).link == NOTHING);
if (toting(t)) --holding_count;
objs(t).place = l;
if (l == R_INHAND) {
++holding_count;
} else if (l != R_LIMBO) {
objs(t).link = places[l].objects;
places[l].objects = t;
}
}
#define move(t,l) do { carry(t); drop((t),l); } while (0)
#define juggle(t) do { Location l = objs(t).place; move(t, l); } while (0)
#define destroy(t) move((t), R_LIMBO)
void carry(ObjectWord t)
{
Location l = objs(t).place;
if (l != R_INHAND) {
if (l > R_LIMBO) {
/* Remove t from l's object-list */
ObjectWord *p = &places[l].objects;
while (*p != t) p = &objs(*p).link;
*p = objs(*p).link;
}
objs(t).place = R_INHAND;
objs(t).link = NOTHING;
++holding_count;
}
}
bool is_at_loc(ObjectWord t, Location loc)
{
if (objs(t).base == NOTHING)
return there(t, loc);
/* Check the "alternative" objects based on this one. */
for (ObjectWord tt = t; objs(tt).base == t; ++tt) {
if (there(tt, loc))
return true;
}
return false;
}
void mobilize(ObjectWord t) { objs(t).base = NOTHING; }
void immobilize(ObjectWord t) { objs(t).base = t; }
void new_obj(ObjectWord t, const char *n, ObjectWord b, Location l)
{
objs(t).name = n;
objs(t).prop = (is_treasure(t) ? -1 : 0);
objs(t).base = b;
objs(t).original_place = l;
objs(t).place = l;
objs(t).link = NOTHING;
if (l > R_LIMBO) {
/* Drop the object at the *end* of its list. Combined with the
* ordering of the item numbers, this ensures that the CHASM
* is described before the TROLL, the DRAGON before the RUG,
* and so on. */
ObjectWord *p = &places[l].objects;
while (*p != NOTHING)
p = &objs(*p).link;
*p = t;
}
}
void build_object_table(void)
{
new_obj(KEYS, "Set of keys", 0, R_HOUSE);
objs(KEYS).desc[0] = "There are some keys on the ground here.";
new_obj(LAMP, "Brass lantern", 0, R_HOUSE);
objs(LAMP).desc[0] = "There is a shiny brass lamp nearby.";
objs(LAMP).desc[1] = "There is a lamp shining nearby.";
new_obj(GRATE, 0, GRATE, R_OUTSIDE);
new_obj(GRATE_, 0, GRATE, R_INSIDE);
objs(GRATE).desc[0] = "The grate is locked.";
objs(GRATE).desc[1] = "The grate is open.";
new_obj(CAGE, "Wicker cage", 0, R_COBBLES);
objs(CAGE).desc[0] = "There is a small wicker cage discarded nearby.";
new_obj(ROD, "Black rod", 0, R_DEBRIS);
objs(ROD).desc[0] = "A three-foot black rod with a rusty star on an end lies nearby.";
new_obj(ROD2, "Black rod", 0, R_LIMBO);
objs(ROD2).desc[0] = "A three-foot black rod with a rusty mark on an end lies nearby.";
new_obj(TREADS, 0, TREADS, R_SPIT);
new_obj(TREADS_, 0, TREADS, R_EMIST);
objs(TREADS).desc[0] = "Rough stone steps lead down the pit.";
objs(TREADS).desc[1] = "Rough stone steps lead up the dome.";
new_obj(BIRD, "Little bird in cage", 0, R_BIRD);
objs(BIRD).desc[0] = "A cheerful little bird is sitting here singing.";
objs(BIRD).desc[1] = "There is a little bird in the cage.";
new_obj(RUSTY_DOOR, 0, RUSTY_DOOR, R_IMMENSE);
objs(RUSTY_DOOR).desc[0] = "The way north is barred by a massive, rusty, iron door.";
objs(RUSTY_DOOR).desc[1] = "The way north leads through a massive, rusty, iron door.";
new_obj(PILLOW, "Velvet pillow", 0, R_SOFT);
objs(PILLOW).desc[0] = "A small velvet pillow lies on the floor.";
new_obj(SNAKE, 0, SNAKE, R_HMK);
objs(SNAKE).desc[0] = "A huge green fierce snake bars the way!";
objs(SNAKE).desc[1] = NULL; /* chased away */
new_obj(FISSURE, 0, FISSURE, R_EFISS);
new_obj(FISSURE_, 0, FISSURE, R_WFISS);
objs(FISSURE).desc[0] = NULL;
objs(FISSURE).desc[1] ="A crystal bridge now spans the fissure.";
new_obj(TABLET, 0, TABLET, R_DARK);
objs(TABLET).desc[0] = /* Woods has "imbedded". */
"A massive stone tablet embedded in the wall reads:" SOFT_NL
"\"Congratulations on bringing light into the dark room!\"";
new_obj(CLAM, "Giant clam >GRUNT!<", 0, R_SHELL);
objs(CLAM).desc[0] = "There is an enormous clam here with its shell tightly closed.";
new_obj(OYSTER, "Giant oyster >GROAN!<", 0, R_LIMBO);
objs(OYSTER).desc[0] = "There is an enormous oyster here with its shell tightly closed.";
new_obj(MAG, "\"Spelunker Today\"", 0, R_ANTE);
objs(MAG).desc[0] = "There are a few recent issues of \"Spelunker Today\" magazine here.";
new_obj(DWARF, 0, DWARF, R_LIMBO);
new_obj(KNIFE, 0, 0, R_LIMBO);
new_obj(FOOD, "Tasty food", 0, R_PANTRY);
objs(FOOD).desc[0] = "There is food here.";
new_obj(BOTTLE, "Small bottle", 0, R_HOUSE);
objs(BOTTLE).desc[0] = "There is a bottle of water here.";
objs(BOTTLE).desc[1] = "There is an empty bottle here.";
objs(BOTTLE).desc[2] = "There is a bottle of oil here.";
/* WATER and OIL never appear on the ground; they are invariably
* either in R_LIMBO or R_INHAND. */
new_obj(WATER, "Water in the bottle", 0, R_LIMBO);
new_obj(OIL, "Oil in the bottle", 0, R_LIMBO);
new_obj(MIRROR, 0, MIRROR, R_MIRROR);
new_obj(MIRROR_, 0, MIRROR, R_LIMBO); /* joins up with MIRROR later */
objs(MIRROR).desc[0] = NULL;
new_obj(PLANT, 0, PLANT, R_WPIT);
objs(PLANT).desc[0] = "There is a tiny little plant in the pit, murmuring \"Water, water, ...\"";
objs(PLANT).desc[1] =
"There is a 12-foot-tall beanstalk stretching up out of the pit," SOFT_NL
"bellowing \"Water!! Water!!\"";
objs(PLANT).desc[2] = "There is a gigantic beanstalk stretching all the way up to the hole.";
new_obj(PLANT2, 0, PLANT2, R_W2PIT);
new_obj(PLANT2_, 0, PLANT2, R_E2PIT);
objs(PLANT2).desc[0] = NULL;
objs(PLANT2).desc[1] = "The top of a 12-foot-tall beanstalk is poking out of the west pit.";
objs(PLANT2).desc[2] = "There is a huge beanstalk growing out of the west pit up to the hole.";
new_obj(STALACTITE, 0, STALACTITE, R_TITE);
objs(STALACTITE).desc[0] = NULL;
new_obj(SHADOW, 0, SHADOW, R_WINDOE);
new_obj(SHADOW_, 0, SHADOW, R_WINDOW);
objs(SHADOW).desc[0] = "The shadowy figure seems to be trying to attract your attention.";
new_obj(AXE, "Dwarf's axe", 0, R_LIMBO);
objs(AXE).desc[0] = "There is a little axe here.";
objs(AXE).desc[1] = "There is a little axe lying beside the bear.";
new_obj(DRAWINGS, 0, DRAWINGS, R_ORIENTAL);
objs(DRAWINGS).desc[0] = NULL;
new_obj(PIRATE, 0, PIRATE, R_LIMBO);
new_obj(DRAGON, 0, DRAGON, R_SCAN1);
new_obj(DRAGON_, 0, DRAGON, R_SCAN3);
objs(DRAGON).desc[0] = "A huge green fierce dragon bars the way!";
objs(DRAGON).desc[1] = "The body of a huge green dead dragon is lying off to one side.";
new_obj(CHASM, 0, CHASM, R_SWSIDE);
new_obj(CHASM_, 0, CHASM, R_NESIDE);
objs(CHASM).desc[0] =