object_descriptions[] is now objects[].
[open-adventure.git] / main.c
1 /*
2  * There used to be a note that said this:
3  *
4  * The author - Don Woods - apologises for the style of the code; it
5  * is a result of running the original Fortran IV source through a
6  * home-brew Fortran-to-C converter.
7  *
8  * Now that the code has been restructured into something much closer
9  * to idiomatic C, the following is more appropriate:
10  *
11  * ESR apologizes for the remaing gotos (now confined to one function
12  * in this file - there used to be over 350 of them, *everywhere*),
13  * and for the offensive globals.  Applying the Structured Program
14  * Theorem can be hard.
15  */
16 #define DEFINE_GLOBALS_FROM_INCLUDES
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <stdbool.h>
20 #include <getopt.h>
21 #include <signal.h>
22 #include <time.h>
23 #include "advent.h"
24 #include "database.h"
25 #include "linenoise/linenoise.h"
26 #include "newdb.h"
27
28 #define DIM(a) (sizeof(a)/sizeof(a[0]))
29
30 /* Abstract out the encoding of words in the travel array.  Gives us
31  * some hope of getting to a less cryptic representation than we
32  * inherited from FORTRAN, someday. To understand these, read the
33  * encoding description for TRAVEL.
34  */
35 #define T_DESTINATION(entry)    MOD(labs(entry) / 1000, 1000)
36 #define T_NODWARVES(entry)      labs(entry) / 1000000 == 100
37 #define T_MOTION(entry)         MOD(labs(entry), 1000)
38 #define L_SPEAK(loc)            ((loc) - 500)
39 #define T_TERMINATE(entry)      (T_MOTION(entry) == 1)
40
41 struct game_t game;
42
43 long LNLENG, LNPOSN;
44 char rawbuf[LINESIZE], INLINE[LINESIZE + 1];
45
46 long AMBER, AXE, BACK, BATTERY, BEAR, BIRD, BLOOD,
47      BOTTLE, CAGE, CAVE, CAVITY, CHAIN, CHASM, CHEST,
48      CLAM, COINS, DOOR, DPRSSN, DRAGON, DWARF, EGGS,
49      EMERALD, ENTER, ENTRNC, FIND, FISSURE, FOOD,
50      GRATE, HINT, INVENT, JADE, KEYS,
51      KNIFE, LAMP, LOCK, LOOK, MAGAZINE,
52      MESSAG, MIRROR, NUGGET, NUL, OGRE, OIL, OYSTER,
53      PEARL, PILLOW, PLANT, PLANT2, PYRAMID, RESER, ROD, ROD2,
54      RUBY, RUG, SAPPH, SAY, SIGN, SNAKE,
55      STEPS, STREAM, THROW, TRIDENT, TROLL, TROLL2,
56      URN, VASE, VEND, VOLCANO, WATER;
57
58 FILE  *logfp = NULL, *rfp = NULL;
59 bool oldstyle = false;
60 bool editline = true;
61 bool prompt = true;
62
63 static void sig_handler(int signo)
64 {
65     if (signo == SIGINT) {
66         if (logfp != NULL)
67             fflush(logfp);
68     }
69     exit(0);
70 }
71
72 /*
73  * MAIN PROGRAM
74  *
75  *  Adventure (rev 2: 20 treasures)
76  *
77  *  History: Original idea & 5-treasure version (adventures) by Willie Crowther
78  *           15-treasure version (adventure) by Don Woods, April-June 1977
79  *           20-treasure version (rev 2) by Don Woods, August 1978
80  *              Errata fixed: 78/12/25
81  *           Revived 2017 as Open Adventure.
82  */
83
84 static bool do_command(FILE *);
85
86 int main(int argc, char *argv[])
87 {
88     int ch;
89
90     /*  Options. */
91
92 #ifndef ADVENT_NOSAVE
93     const char* opts = "l:or:s";
94     const char* usage = "Usage: %s [-l logfilename] [-o] [-r restorefilename] [-s] \n";
95 #else
96     const char* opts = "l:os";
97     const char* usage = "Usage: %s [-l logfilename] [-o] [-s] \n";
98 #endif
99     while ((ch = getopt(argc, argv, opts)) != EOF) {
100         switch (ch) {
101         case 'l':
102             logfp = fopen(optarg, "w");
103             if (logfp == NULL)
104                 fprintf(stderr,
105                         "advent: can't open logfile %s for write\n",
106                         optarg);
107             signal(SIGINT, sig_handler);
108             break;
109         case 'o':
110             oldstyle = true;
111             editline = prompt = false;
112             break;
113 #ifndef ADVENT_NOSAVE
114         case 'r':
115             rfp = fopen(optarg, "r");
116             if (rfp == NULL)
117                 fprintf(stderr,
118                         "advent: can't open save file %s for read\n",
119                         optarg);
120             signal(SIGINT, sig_handler);
121             break;
122 #endif
123         case 's':
124             editline = false;
125             break;
126         default:
127             fprintf(stderr,
128                     usage, argv[0]);
129             fprintf(stderr,
130                     "        -l create a log file of your game named as specified'\n");
131             fprintf(stderr,
132                     "        -o 'oldstyle' (no prompt, no command editing, displays 'Initialising...')\n");
133 #ifndef ADVENT_NOSAVE
134             fprintf(stderr,
135                     "        -r restore from specified saved game file\n");
136 #endif
137             fprintf(stderr,
138                     "        -s suppress command editing\n");
139             exit(-1);
140             break;
141         }
142     }
143
144     linenoiseHistorySetMaxLen(350);
145
146     /* Initialize our LCG PRNG with parameters tested against
147      * Knuth vol. 2. by the original authors */
148     game.lcg_a = 1093;
149     game.lcg_c = 221587;
150     game.lcg_m = 1048576;
151     srand(time(NULL));
152     long seedval = (long)rand();
153     set_seed(seedval);
154
155     /*  Initialize game variables */
156     initialise();
157
158     /*  Start-up, dwarf stuff */
159     game.zzword = rndvoc(3, 0);
160     game.newloc = LOC_START;
161     game.loc = LOC_START;
162     game.limit = GAMELIMIT;
163     if (!rfp) {
164         game.novice = yes(arbitrary_messages[WELCOME_YOU], arbitrary_messages[CAVE_NEARBY], arbitrary_messages[NO_MESSAGE]);
165         if (game.novice)
166             game.limit = NOVICELIMIT;
167     } else {
168         restore(rfp);
169     }
170
171     if (logfp)
172         fprintf(logfp, "seed %ld\n", seedval);
173
174     /* interpret commands until EOF or interrupt */
175     for (;;) {
176         if (!do_command(stdin))
177             break;
178     }
179     /* show score and exit */
180     terminate(quitgame);
181 }
182
183 static bool fallback_handler(char *buf)
184 /* fallback handler for commands not handled by FORTRANish parser */
185 {
186     long sv;
187     if (sscanf(buf, "seed %ld", &sv) == 1) {
188         set_seed(sv);
189         printf("Seed set to %ld\n", sv);
190         // autogenerated, so don't charge user time for it.
191         --game.turns;
192         // here we reconfigure any global game state that uses random numbers
193         game.zzword = rndvoc(3, 0);
194         return true;
195     }
196     return false;
197 }
198
199 /*  Check if this loc is eligible for any hints.  If been here long
200  *  enough, display.  Ignore "HINTS" < 4 (special stuff, see database
201  *  notes). */
202 static void checkhints(void)
203 {
204     if (conditions[game.loc] >= game.conds) {
205         for (int hint = 0; hint < NHINTS; hint++) {
206             if (game.hinted[hint])
207                 continue;
208             if (!CNDBIT(game.loc, hint + 1 + COND_HBASE))
209                 game.hintlc[hint] = -1;
210             ++game.hintlc[hint];
211             /*  Come here if he's been long enough at required loc(s) for some
212              *  unused hint. */
213             if (game.hintlc[hint] >= hints[hint].turns) {
214                 int i;
215
216                 switch (hint) {
217                 case 0:
218                     /* cave */
219                     if (game.prop[GRATE] == GRATE_CLOSED && !HERE(KEYS))
220                         break;
221                     game.hintlc[hint] = 0;
222                     return;
223                 case 1: /* bird */
224                     if (game.place[BIRD] == game.loc && TOTING(ROD) && game.oldobj == BIRD)
225                         break;
226                     return;
227                 case 2: /* snake */
228                     if (HERE(SNAKE) && !HERE(BIRD))
229                         break;
230                     game.hintlc[hint] = 0;
231                     return;
232                 case 3: /* maze */
233                     if (game.atloc[game.loc] == 0 &&
234                         game.atloc[game.oldloc] == 0 &&
235                         game.atloc[game.oldlc2] == 0 &&
236                         game.holdng > 1)
237                         break;
238                     game.hintlc[hint] = 0;
239                     return;
240                 case 4: /* dark */
241                     if (game.prop[EMERALD] != -1 && game.prop[PYRAMID] == -1)
242                         break;
243                     game.hintlc[hint] = 0;
244                     return;
245                 case 5: /* witt */
246                     break;
247                 case 6: /* urn */
248                     if (game.dflag == 0)
249                         break;
250                     game.hintlc[hint] = 0;
251                     return;
252                 case 7: /* woods */
253                     if (game.atloc[game.loc] == 0 &&
254                         game.atloc[game.oldloc] == 0 &&
255                         game.atloc[game.oldlc2] == 0)
256                         break;
257                     return;
258                 case 8: /* ogre */
259                     i = atdwrf(game.loc);
260                     if (i < 0) {
261                         game.hintlc[hint] = 0;
262                         return;
263                     }
264                     if (HERE(OGRE) && i == 0)
265                         break;
266                     return;
267                 case 9: /* jade */
268                     if (game.tally == 1 && game.prop[JADE] < 0)
269                         break;
270                     game.hintlc[hint] = 0;
271                     return;
272                 default:
273                     BUG(HINT_NUMBER_EXCEEDS_GOTO_LIST);
274                     break;
275                 }
276
277                 /* Fall through to hint display */
278                 game.hintlc[hint] = 0;
279                 if (!yes(hints[hint].question, arbitrary_messages[NO_MESSAGE], arbitrary_messages[OK_MAN]))
280                     return;
281                 rspeak(HINT_COST, hints[hint].penalty, hints[hint].penalty);
282                 game.hinted[hint] = yes(arbitrary_messages[WANT_HINT], hints[hint].hint, arbitrary_messages[OK_MAN]);
283                 if (game.hinted[hint] && game.limit > WARNTIME)
284                     game.limit += WARNTIME * hints[hint].penalty;
285             }
286         }
287     }
288 }
289
290 static bool spotted_by_pirate(int i)
291 {
292     if (i != PIRATE)
293         return false;
294
295     /*  The pirate's spotted him.  He leaves him alone once we've
296      *  found chest.  K counts if a treasure is here.  If not, and
297      *  tally=1 for an unseen chest, let the pirate be spotted.  Note
298      *  that game.place[CHEST] = LOC_NOWHERE might mean that he's thrown
299      *  it to the troll, but in that case he's seen the chest
300      *  (game.prop=0). */
301     if (game.loc == game.chloc || game.prop[CHEST] >= 0)
302         return true;
303     int snarfed = 0;
304     bool movechest = false, robplayer = false;
305     for (int treasure = 1; treasure <= NOBJECTS; treasure++) {
306         if (!objects[treasure].is_treasure)
307             continue;
308         /*  Pirate won't take pyramid from plover room or dark
309          *  room (too easy!). */
310         if (treasure == PYRAMID && (game.loc == objects[PYRAMID].plac || game.loc == objects[EMERALD].plac)) {
311             continue;
312         }
313         if (TOTING(treasure) || HERE(treasure))
314             ++snarfed;
315         if (TOTING(treasure)) {
316             movechest = true;
317             robplayer = true;
318         }
319     }
320     /* Force chest placement before player finds last treasure */
321     if (game.tally == 1 && snarfed == 0 && game.place[CHEST] == LOC_NOWHERE && HERE(LAMP) && game.prop[LAMP] == LAMP_BRIGHT) {
322         rspeak(PIRATE_SPOTTED);
323         movechest = true;
324     }
325     /* Do things in this order (chest move before robbery) so chest is listed
326      * last at the maze location. */
327     if (movechest) {
328         move(CHEST, game.chloc);
329         move(MESSAG, game.chloc2);
330         game.dloc[PIRATE] = game.chloc;
331         game.odloc[PIRATE] = game.chloc;
332         game.dseen[PIRATE] = false;
333     } else {
334         /* You might get a hint of the pirate's presence even if the
335          * chest doesn't move... */
336         if (game.odloc[PIRATE] != game.dloc[PIRATE] && PCT(20))
337             rspeak(PIRATE_RUSTLES);
338     }
339     if (robplayer) {
340         rspeak(PIRATE_POUNCES);
341         for (int treasure = 1; treasure <= NOBJECTS; treasure++) {
342             if (!objects[treasure].is_treasure)
343                 continue;
344             if (!(treasure == PYRAMID && (game.loc == objects[PYRAMID].plac || game.loc == objects[EMERALD].plac))) {
345                 if (AT(treasure) && game.fixed[treasure] == 0)
346                     carry(treasure, game.loc);
347                 if (TOTING(treasure))
348                     drop(treasure, game.chloc);
349             }
350         }
351     }
352
353     return true;
354 }
355
356 static bool dwarfmove(void)
357 /* Dwarves move.  Return true if player survives, false if he dies. */
358 {
359     int kk, stick, attack;
360     long tk[21];
361
362     /*  Dwarf stuff.  See earlier comments for description of
363      *  variables.  Remember sixth dwarf is pirate and is thus
364      *  very different except for motion rules. */
365
366     /*  First off, don't let the dwarves follow him into a pit or
367      *  a wall.  Activate the whole mess the first time he gets as
368      *  far as the hall of mists (loc 15).  If game.newloc is
369      *  forbidden to pirate (in particular, if it's beyond the
370      *  troll bridge), bypass dwarf stuff.  That way pirate can't
371      *  steal return toll, and dwarves can't meet the bear.  Also
372      *  means dwarves won't follow him into dead end in maze, but
373      *  c'est la vie.  They'll wait for him outside the dead
374      *  end. */
375     if (game.loc == 0 || FORCED(game.loc) || CNDBIT(game.newloc, COND_NOARRR))
376         return true;
377
378     /* Dwarf activity level ratchets up */
379     if (game.dflag == 0) {
380         if (INDEEP(game.loc))
381             game.dflag = 1;
382         return true;
383     }
384
385     /*  When we encounter the first dwarf, we kill 0, 1, or 2 of
386      *  the 5 dwarves.  If any of the survivors is at loc,
387      *  replace him with the alternate. */
388     if (game.dflag == 1) {
389         if (!INDEEP(game.loc) || (PCT(95) && (!CNDBIT(game.loc, COND_NOBACK) || PCT(85))))
390             return true;
391         game.dflag = 2;
392         for (int i = 1; i <= 2; i++) {
393             int j = 1 + randrange(NDWARVES - 1);
394             if (PCT(50))
395                 game.dloc[j] = 0;
396         }
397         for (int i = 1; i <= NDWARVES - 1; i++) {
398             if (game.dloc[i] == game.loc)
399                 game.dloc[i] = DALTLC;
400             game.odloc[i] = game.dloc[i];
401         }
402         rspeak(DWARF_RAN);
403         drop(AXE, game.loc);
404         return true;
405     }
406
407     /*  Things are in full swing.  Move each dwarf at random,
408      *  except if he's seen us he sticks with us.  Dwarves stay
409      *  deep inside.  If wandering at random, they don't back up
410      *  unless there's no alternative.  If they don't have to
411      *  move, they attack.  And, of course, dead dwarves don't do
412      *  much of anything. */
413     game.dtotal = 0;
414     attack = 0;
415     stick = 0;
416     for (int i = 1; i <= NDWARVES; i++) {
417         if (game.dloc[i] == 0)
418             continue;
419         /*  Fill tk array with all the places this dwarf might go. */
420         unsigned int j = 1;
421         kk = TKEY[game.dloc[i]];
422         if (kk != 0)
423             do {
424                 game.newloc = T_DESTINATION(TRAVEL[kk]);
425                 /* Have we avoided a dwarf encounter? */
426                 bool avoided = (SPECIAL(game.newloc) ||
427                                 !INDEEP(game.newloc) ||
428                                 game.newloc == game.odloc[i] ||
429                                 (j > 1 && game.newloc == tk[j - 1]) ||
430                                 j >= DIM(tk) - 1 ||
431                                 game.newloc == game.dloc[i] ||
432                                 FORCED(game.newloc) ||
433                                 (i == PIRATE && CNDBIT(game.newloc, COND_NOARRR)) ||
434                                 T_NODWARVES(TRAVEL[kk]));
435                 if (!avoided) {
436                     tk[j++] = game.newloc;
437                 }
438                 ++kk;
439             } while
440             (TRAVEL[kk - 1] >= 0);
441         tk[j] = game.odloc[i];
442         if (j >= 2)
443             --j;
444         j = 1 + randrange(j);
445         game.odloc[i] = game.dloc[i];
446         game.dloc[i] = tk[j];
447         game.dseen[i] = (game.dseen[i] && INDEEP(game.loc)) || (game.dloc[i] == game.loc || game.odloc[i] == game.loc);
448         if (!game.dseen[i]) continue;
449         game.dloc[i] = game.loc;
450         if (spotted_by_pirate(i))
451             continue;
452         /* This threatening little dwarf is in the room with him! */
453         ++game.dtotal;
454         if (game.odloc[i] == game.dloc[i]) {
455             ++attack;
456             if (game.knfloc >= 0)
457                 game.knfloc = game.loc;
458             if (randrange(1000) < 95 * (game.dflag - 2))
459                 ++stick;
460         }
461     }
462
463     /*  Now we know what's happening.  Let's tell the poor sucker about it.
464      *  Note that various of the "knife" messages must have specific relative
465      *  positions in the rspeak database. */
466     if (game.dtotal == 0)
467         return true;
468     rspeak(game.dtotal == 1 ? DWARF_SINGLE : DWARF_PACK, game.dtotal);
469     if (attack == 0)
470         return true;
471     if (game.dflag == 2)game.dflag = 3;
472     if (attack > 1) {
473         rspeak(THROWN_KNIVES, attack);
474         rspeak(stick > 1 ? MULTIPLE_HITS : (stick == 1 ? ONE_HIT : NONE_HIT), stick);
475     } else {
476         rspeak(KNIFE_THROWN);
477         rspeak(MISSES_YOU);
478     }
479     if (stick == 0)
480         return true;
481     game.oldlc2 = game.loc;
482     return false;
483 }
484
485 /*  "You're dead, Jim."
486  *
487  *  If the current loc is zero, it means the clown got himself killed.
488  *  We'll allow this maxdie times.  NDEATHS is automatically set based
489  *  on the number of snide messages available.  Each death results in
490  *  a message (81, 83, etc.)  which offers reincarnation; if accepted,
491  *  this results in message 82, 84, etc.  The last time, if he wants
492  *  another chance, he gets a snide remark as we exit.  When
493  *  reincarnated, all objects being carried get dropped at game.oldlc2
494  *  (presumably the last place prior to being killed) without change
495  *  of props.  the loop runs backwards to assure that the bird is
496  *  dropped before the cage.  (this kluge could be changed once we're
497  *  sure all references to bird and cage are done by keywords.)  The
498  *  lamp is a special case (it wouldn't do to leave it in the cave).
499  *  It is turned off and left outside the building (only if he was
500  *  carrying it, of course).  He himself is left inside the building
501  *  (and heaven help him if he tries to xyzzy back into the cave
502  *  without the lamp!).  game.oldloc is zapped so he can't just
503  *  "retreat". */
504
505 static void croak(void)
506 /*  Okay, he's dead.  Let's get on with it. */
507 {
508     const char* query = obituaries[game.numdie].query;
509     const char* yes_response = obituaries[game.numdie].yes_response;
510     ++game.numdie;
511     if (game.closng) {
512         /*  He died during closing time.  No resurrection.  Tally up a
513          *  death and exit. */
514         rspeak(DEATH_CLOSING);
515         terminate(endgame);
516     } else if (game.numdie == NDEATHS || !yes(query, yes_response, arbitrary_messages[OK_MAN]))
517         terminate(endgame);
518     else {
519         game.place[WATER] = game.place[OIL] = LOC_NOWHERE;
520         if (TOTING(LAMP))
521             game.prop[LAMP] = LAMP_DARK;
522         for (int j = 1; j <= NOBJECTS; j++) {
523             int i = NOBJECTS + 1 - j;
524             if (TOTING(i)) {
525                 /* Always leave lamp where it's accessible aboveground */
526                 drop(i, (i == LAMP) ? LOC_START : game.oldlc2);
527             }
528         }
529         game.loc = LOC_BUILDING;
530         game.oldloc = game.loc;
531     }
532 }
533
534 /*  Given the current location in "game.loc", and a motion verb number in
535  *  "motion", put the new location in "game.newloc".  The current loc is saved
536  *  in "game.oldloc" in case he wants to retreat.  The current
537  *  game.oldloc is saved in game.oldlc2, in case he dies.  (if he
538  *  does, game.newloc will be limbo, and game.oldloc will be what killed
539  *  him, so we need game.oldlc2, which is the last place he was
540  *  safe.) */
541
542 static bool playermove(token_t verb, int motion)
543 {
544     int scratchloc, k2, kk = TKEY[game.loc];
545     game.newloc = game.loc;
546     if (kk == 0)
547         BUG(LOCATION_HAS_NO_TRAVEL_ENTRIES);
548     if (motion == NUL)
549         return true;
550     else if (motion == BACK) {
551         /*  Handle "go back".  Look for verb which goes from game.loc to
552          *  game.oldloc, or to game.oldlc2 If game.oldloc has forced-motion.
553          *  k2 saves entry -> forced loc -> previous loc. */
554         motion = game.oldloc;
555         if (FORCED(motion))
556             motion = game.oldlc2;
557         game.oldlc2 = game.oldloc;
558         game.oldloc = game.loc;
559         k2 = 0;
560         if (motion == game.loc)k2 = FORGOT_PATH;
561         if (CNDBIT(game.loc, COND_NOBACK))k2 = TWIST_TURN;
562         if (k2 == 0) {
563             for (;;) {
564                 scratchloc = T_DESTINATION(TRAVEL[kk]);
565                 if (scratchloc != motion) {
566                     if (!SPECIAL(scratchloc)) {
567                         if (FORCED(scratchloc) && T_DESTINATION(TRAVEL[TKEY[scratchloc]]) == motion)
568                             k2 = kk;
569                     }
570                     if (TRAVEL[kk] >= 0) {
571                         ++kk;   /* go to next travel entry for this location */
572                         continue;
573                     }
574                     /* we've reached the end of travel entries for game.loc */
575                     kk = k2;
576                     if (kk == 0) {
577                         rspeak(NOT_CONNECTED);
578                         return true;
579                     }
580                 }
581
582                 motion = T_MOTION(TRAVEL[kk]);
583                 kk = TKEY[game.loc];
584                 break; /* fall through to ordinary travel */
585             }
586         } else {
587             rspeak(k2);
588             return true;
589         }
590     } else if (motion == LOOK) {
591         /*  Look.  Can't give more detail.  Pretend it wasn't dark
592          *  (though it may now be dark) so he won't fall into a
593          *  pit while staring into the gloom. */
594         if (game.detail < 3)
595             rspeak(NO_MORE_DETAIL);
596         ++game.detail;
597         game.wzdark = false;
598         game.abbrev[game.loc] = 0;
599         return true;
600     } else if (motion == CAVE) {
601         /*  Cave.  Different messages depending on whether above ground. */
602         rspeak((OUTSID(game.loc) && game.loc != LOC_GRATE) ? FOLLOW_STREAM : NEED_DETAIL);
603         return true;
604     } else {
605         /* none of the specials */
606         game.oldlc2 = game.oldloc;
607         game.oldloc = game.loc;
608     }
609
610     /* Look for a way to fulfil the motion - kk indexes the beginning
611      * of the motion entries for here (game.loc). */
612     for (;;) {
613         if (T_TERMINATE(TRAVEL[kk]) || T_MOTION(TRAVEL[kk]) == motion)
614             break;
615         if (TRAVEL[kk] < 0) {
616             /* FIXME: Magic numbers! */
617             /*  Couldn't find an entry matching the motion word passed
618              *  in.  Various messages depending on word given. */
619             int spk = CANT_APPLY;
620             if (motion >= 43 && motion <= 50)spk = BAD_DIRECTION;
621             if (motion == 29 || motion == 30)spk = BAD_DIRECTION;
622             if (motion == 7 || motion == 36 || motion == 37)spk = UNSURE_FACING;
623             if (motion == 11 || motion == 19)spk = NO_INOUT_HERE;
624             if (verb == FIND || verb == INVENT)spk = NEARBY;
625             if (motion == 62 || motion == 65)spk = NOTHING_HAPPENS;
626             if (motion == 17)spk = WHICH_WAY;
627             rspeak(spk);
628             return true;
629         }
630         ++kk;
631     }
632     scratchloc = labs(TRAVEL[kk]) / 1000;
633
634     do {
635         /*
636          * (ESR) This special-travel loop may have to be repeated if it includes
637          * the plover passage.  Same deal for any future cases where we need to
638          * block travel and then redo it once the blocking condition has been
639          * removed.
640          */
641         for (;;) { /* L12 loop */
642             for (;;) {
643                 game.newloc = scratchloc / 1000;
644                 motion = MOD(game.newloc, 100);
645                 if (!SPECIAL(game.newloc)) {
646                     if (game.newloc <= 100) {
647                         if (game.newloc == 0 || PCT(game.newloc))
648                             break;
649                         /* else fall through */
650                     }
651                     if (TOTING(motion) || (game.newloc > 200 && AT(motion)))
652                         break;
653                     /* else fall through */
654                 } else if (game.prop[motion] != game.newloc / 100 - 3)
655                     break;
656                 do {
657                     if (TRAVEL[kk] < 0)
658                         BUG(CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION);
659                     ++kk;
660                     game.newloc = labs(TRAVEL[kk]) / 1000;
661                 } while
662                 (game.newloc == scratchloc);
663                 scratchloc = game.newloc;
664             }
665
666             game.newloc = MOD(scratchloc, 1000);
667             if (!SPECIAL(game.newloc))
668                 return true;
669             if (game.newloc <= 500) {
670                 game.newloc -= SPECIALBASE;
671                 switch (game.newloc) {
672                 case 1:
673                     /*  Travel 301.  Plover-alcove passage.  Can carry only
674                      *  emerald.  Note: travel table must include "useless"
675                      *  entries going through passage, which can never be used for
676                      *  actual motion, but can be spotted by "go back". */
677                     /* FIXME: Arithmetic on location numbers */
678                     game.newloc = 99 + 100 - game.loc;
679                     if (game.holdng > 1 || (game.holdng == 1 && !TOTING(EMERALD))) {
680                         game.newloc = game.loc;
681                         rspeak(MUST_DROP);
682                     }
683                     return true;
684                 case 2:
685                     /*  Travel 302.  Plover transport.  Drop the emerald (only use
686                      *  special travel if toting it), so he's forced to use the
687                      *  plover-passage to get it out.  Having dropped it, go back and
688                      *  pretend he wasn't carrying it after all. */
689                     drop(EMERALD, game.loc);
690                     do {
691                         if (TRAVEL[kk] < 0)
692                             BUG(CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION);
693                         ++kk;
694                         game.newloc = labs(TRAVEL[kk]) / 1000;
695                     } while
696                     (game.newloc == scratchloc);
697                     scratchloc = game.newloc;
698                     continue; /* goto L12 */
699                 case 3:
700                     /*  Travel 303.  Troll bridge.  Must be done only as special
701                      *  motion so that dwarves won't wander across and encounter
702                      *  the bear.  (They won't follow the player there because
703                      *  that region is forbidden to the pirate.)  If
704                      *  game.prop(TROLL)=1, he's crossed since paying, so step out
705                      *  and block him.  (standard travel entries check for
706                      *  game.prop(TROLL)=0.)  Special stuff for bear. */
707                     if (game.prop[TROLL] == 1) {
708                         pspeak(TROLL,look, 1);
709                         game.prop[TROLL] = 0;
710                         move(TROLL2, 0);
711                         move(TROLL2 + NOBJECTS, 0);
712                         move(TROLL, objects[TROLL].plac);
713                         move(TROLL + NOBJECTS, objects[TROLL].fixd);
714                         juggle(CHASM);
715                         game.newloc = game.loc;
716                         return true;
717                     } else {
718                         game.newloc = objects[TROLL].plac + objects[TROLL].fixd - game.loc;
719                         if (game.prop[TROLL] == 0)game.prop[TROLL] = 1;
720                         if (!TOTING(BEAR)) return true;
721                         rspeak(BRIDGE_COLLAPSE);
722                         game.prop[CHASM] = 1;
723                         game.prop[TROLL] = 2;
724                         drop(BEAR, game.newloc);
725                         game.fixed[BEAR] = -1;
726                         game.prop[BEAR] = 3;
727                         game.oldlc2 = game.newloc;
728                         croak();
729                         return true;
730                     }
731                 }
732                 BUG(SPECIAL_TRAVEL_500_GT_L_GT_300_EXCEEDS_GOTO_LIST);
733             }
734             break; /* Leave L12 loop */
735         }
736     } while
737     (false);
738     
739     /* Execute a speak rule */
740     rspeak(L_SPEAK(game.newloc));
741     game.newloc = game.loc;
742     return true;
743 }
744
745 static bool closecheck(void)
746 /*  Handle the closing of the cave.  The cave closes "clock1" turns
747  *  after the last treasure has been located (including the pirate's
748  *  chest, which may of course never show up).  Note that the
749  *  treasures need not have been taken yet, just located.  Hence
750  *  clock1 must be large enough to get out of the cave (it only ticks
751  *  while inside the cave).  When it hits zero, we branch to 10000 to
752  *  start closing the cave, and then sit back and wait for him to try
753  *  to get out.  If he doesn't within clock2 turns, we close the cave;
754  *  if he does try, we assume he panics, and give him a few additional
755  *  turns to get frantic before we close.  When clock2 hits zero, we
756  *  branch to 11000 to transport him into the final puzzle.  Note that
757  *  the puzzle depends upon all sorts of random things.  For instance,
758  *  there must be no water or oil, since there are beanstalks which we
759  *  don't want to be able to water, since the code can't handle it.
760  *  Also, we can have no keys, since there is a grate (having moved
761  *  the fixed object!) there separating him from all the treasures.
762  *  Most of these problems arise from the use of negative prop numbers
763  *  to suppress the object descriptions until he's actually moved the
764  *  objects. */
765 {
766     if (game.tally == 0 && INDEEP(game.loc) && game.loc != LOC_Y2)
767         --game.clock1;
768
769     /*  When the first warning comes, we lock the grate, destroy
770      *  the bridge, kill all the dwarves (and the pirate), remove
771      *  the troll and bear (unless dead), and set "closng" to
772      *  true.  Leave the dragon; too much trouble to move it.
773      *  from now until clock2 runs out, he cannot unlock the
774      *  grate, move to any location outside the cave, or create
775      *  the bridge.  Nor can he be resurrected if he dies.  Note
776      *  that the snake is already gone, since he got to the
777      *  treasure accessible only via the hall of the mountain
778      *  king. Also, he's been in giant room (to get eggs), so we
779      *  can refer to it.  Also also, he's gotten the pearl, so we
780      *  know the bivalve is an oyster.  *And*, the dwarves must
781      *  have been activated, since we've found chest. */
782     if (game.clock1 == 0) {
783         game.prop[GRATE] = GRATE_CLOSED;
784         game.prop[FISSURE] = 0;
785         for (int i = 1; i <= NDWARVES; i++) {
786             game.dseen[i] = false;
787             game.dloc[i] = 0;
788         }
789         move(TROLL, 0);
790         move(TROLL + NOBJECTS, 0);
791         move(TROLL2, objects[TROLL].plac);
792         move(TROLL2 + NOBJECTS, objects[TROLL].fixd);
793         juggle(CHASM);
794         if (game.prop[BEAR] != 3)DESTROY(BEAR);
795         game.prop[CHAIN] = 0;
796         game.fixed[CHAIN] = 0;
797         game.prop[AXE] = 0;
798         game.fixed[AXE] = 0;
799         rspeak(CAVE_CLOSING);
800         game.clock1 = -1;
801         game.closng = true;
802         return true;
803     } else if (game.clock1 < 0)
804         --game.clock2;
805     if (game.clock2 == 0) {
806         /*  Once he's panicked, and clock2 has run out, we come here
807          *  to set up the storage room.  The room has two locs,
808          *  hardwired as LOC_NE and LOC_SW.  At the ne end, we
809          *  place empty bottles, a nursery of plants, a bed of
810          *  oysters, a pile of lamps, rods with stars, sleeping
811          *  dwarves, and him.  At the sw end we place grate over
812          *  treasures, snake pit, covey of caged birds, more rods, and
813          *  pillows.  A mirror stretches across one wall.  Many of the
814          *  objects come from known locations and/or states (e.g. the
815          *  snake is known to have been destroyed and needn't be
816          *  carried away from its old "place"), making the various
817          *  objects be handled differently.  We also drop all other
818          *  objects he might be carrying (lest he have some which
819          *  could cause trouble, such as the keys).  We describe the
820          *  flash of light and trundle back. */
821         game.prop[BOTTLE] = put(BOTTLE, LOC_NE, EMPTY_BOTTLE);
822         game.prop[PLANT] = put(PLANT, LOC_NE, 0);
823         game.prop[OYSTER] = put(OYSTER, LOC_NE, 0);
824         game.prop[LAMP] = put(LAMP, LOC_NE, 0);
825         game.prop[ROD] = put(ROD, LOC_NE, 0);
826         game.prop[DWARF] = put(DWARF, LOC_NE, 0);
827         game.loc = LOC_NE;
828         game.oldloc = LOC_NE;
829         game.newloc = LOC_NE;
830         /*  Leave the grate with normal (non-negative) property.
831          *  Reuse sign. */
832         put(GRATE, LOC_SW, 0);
833         put(SIGN, LOC_SW, 0);
834         game.prop[SIGN] = ENDGAME_SIGN;
835         game.prop[SNAKE] = put(SNAKE, LOC_SW, 1);
836         game.prop[BIRD] = put(BIRD, LOC_SW, 1);
837         game.prop[CAGE] = put(CAGE, LOC_SW, 0);
838         game.prop[ROD2] = put(ROD2, LOC_SW, 0);
839         game.prop[PILLOW] = put(PILLOW, LOC_SW, 0);
840
841         game.prop[MIRROR] = put(MIRROR, LOC_NE, 0);
842         game.fixed[MIRROR] = LOC_SW;
843
844         for (int i = 1; i <= NOBJECTS; i++) {
845             if (TOTING(i))
846                 DESTROY(i);
847         }
848
849         rspeak(CAVE_CLOSED);
850         game.closed = true;
851         return true;
852     }
853
854     return false;
855 }
856
857 static void lampcheck(void)
858 /* Check game limit and lamp timers */
859 {
860     if (game.prop[LAMP] == LAMP_BRIGHT)
861         --game.limit;
862
863     /*  Another way we can force an end to things is by having the
864      *  lamp give out.  When it gets close, we come here to warn him.
865      *  First following arm checks if the lamp and fresh batteries are
866      *  here, in which case we replace the batteries and continue.
867      *  Second is for other cases of lamp dying.  Eve after it goes
868      *  out, he can explore outside for a while if desired. */
869     if (game.limit <= WARNTIME && HERE(BATTERY) && game.prop[BATTERY] == FRESH_BATTERIES && HERE(LAMP)) {
870         rspeak(REPLACE_BATTERIES);
871         game.prop[BATTERY] = DEAD_BATTERIES;
872         if (TOTING(BATTERY))
873             drop(BATTERY, game.loc);
874         game.limit += BATTERYLIFE;
875         game.lmwarn = false;
876     } else if (game.limit == 0) {
877         game.limit = -1;
878         game.prop[LAMP] = LAMP_DARK;
879         if (HERE(LAMP))
880             rspeak(LAMP_OUT);
881     } else if (game.limit <= WARNTIME) {
882         if (!game.lmwarn && HERE(LAMP)) {
883             game.lmwarn = true;
884             int spk = GET_BATTERIES;
885             if (game.place[BATTERY] == LOC_NOWHERE)spk = LAMP_DIM;
886             if (game.prop[BATTERY] == DEAD_BATTERIES)
887                 spk = MISSING_BATTERIES;
888             rspeak(spk);
889         }
890     }
891 }
892
893 static void listobjects(void)
894 /*  Print out descriptions of objects at this location.  If
895  *  not closing and property value is negative, tally off
896  *  another treasure.  Rug is special case; once seen, its
897  *  game.prop is 1 (dragon on it) till dragon is killed.
898  *  Similarly for chain; game.prop is initially 1 (locked to
899  *  bear).  These hacks are because game.prop=0 is needed to
900  *  get full score. */
901 {
902     if (!DARK(game.loc)) {
903         ++game.abbrev[game.loc];
904         for (int i = game.atloc[game.loc]; i != 0; i = game.link[i]) {
905             long obj = i;
906             if (obj > NOBJECTS)obj = obj - NOBJECTS;
907             if (obj == STEPS && TOTING(NUGGET))
908                 continue;
909             if (game.prop[obj] < 0) {
910                 if (game.closed)
911                     continue;
912                 game.prop[obj] = 0;
913                 if (obj == RUG || obj == CHAIN)
914                     game.prop[obj] = 1;
915                 --game.tally;
916                 /*  Note: There used to be a test here to see whether the
917                  *  player had blown it so badly that he could never ever see
918                  *  the remaining treasures, and if so the lamp was zapped to
919                  *  35 turns.  But the tests were too simple-minded; things
920                  *  like killing the bird before the snake was gone (can never
921                  *  see jewelry), and doing it "right" was hopeless.  E.G.,
922                  *  could cross troll bridge several times, using up all
923                  *  available treasures, breaking vase, using coins to buy
924                  *  batteries, etc., and eventually never be able to get
925                  *  across again.  If bottle were left on far side, could then
926                  *  never get eggs or trident, and the effects propagate.  So
927                  *  the whole thing was flushed.  anyone who makes such a
928                  *  gross blunder isn't likely to find everything else anyway
929                  *  (so goes the rationalisation). */
930             }
931             int kk = game.prop[obj];
932             if (obj == STEPS && game.loc == game.fixed[STEPS])
933                 kk = 1;
934             pspeak(obj, look, kk);
935         }
936     }
937 }
938
939 static bool do_command(FILE *cmdin)
940 /* Get and execute a command */
941 {
942     long V1, V2;
943     long kmod, defn;
944     static long igo = 0;
945     static struct command_t command;
946     command.verb = 0;
947
948     /*  Can't leave cave once it's closing (except by main office). */
949     if (OUTSID(game.newloc) && game.newloc != 0 && game.closng) {
950         rspeak(EXIT_CLOSED);
951         game.newloc = game.loc;
952         if (!game.panic)game.clock2 = PANICTIME;
953         game.panic = true;
954     }
955
956     /*  See if a dwarf has seen him and has come from where he
957      *  wants to go.  If so, the dwarf's blocking his way.  If
958      *  coming from place forbidden to pirate (dwarves rooted in
959      *  place) let him get out (and attacked). */
960     if (game.newloc != game.loc && !FORCED(game.loc) && !CNDBIT(game.loc, COND_NOARRR)) {
961         for (size_t i = 1; i <= NDWARVES - 1; i++) {
962             if (game.odloc[i] == game.newloc && game.dseen[i]) {
963                 game.newloc = game.loc;
964                 rspeak(DWARF_BLOCK);
965                 break;
966             }
967         }
968     }
969     game.loc = game.newloc;
970
971     if (!dwarfmove())
972         croak();
973
974     /*  Describe the current location and (maybe) get next command. */
975
976     for (;;) {
977         if (game.loc == 0)
978             croak();
979         const char* msg = locations[game.loc].description.small;
980         if (MOD(game.abbrev[game.loc], game.abbnum) == 0 || msg == 0)
981             msg = locations[game.loc].description.big;
982         if (!FORCED(game.loc) && DARK(game.loc)) {
983             /*  The easiest way to get killed is to fall into a pit in
984              *  pitch darkness. */
985             if (game.wzdark && PCT(35)) {
986                 rspeak(PIT_FALL);
987                 game.oldlc2 = game.loc;
988                 croak();
989                 continue;       /* back to top of main interpreter loop */
990             }
991             msg = arbitrary_messages[PITCH_DARK];
992         }
993         if (TOTING(BEAR))rspeak(TAME_BEAR);
994         speak(msg);
995         if (FORCED(game.loc)) {
996             if (playermove(command.verb, 1))
997                 return true;
998             else
999                 continue;       /* back to top of main interpreter loop */
1000         }
1001         if (game.loc == LOC_Y2 && PCT(25) && !game.closng)
1002             rspeak(SAYS_PLUGH);
1003
1004         listobjects();
1005
1006 L2012:
1007         command.verb = 0;
1008         game.oldobj = command.obj;
1009         command.obj = 0;
1010
1011 L2600:
1012         checkhints();
1013
1014         /*  If closing time, check for any objects being toted with
1015          *  game.prop < 0 and set the prop to -1-game.prop.  This way
1016          *  objects won't be described until they've been picked up
1017          *  and put down separate from their respective piles.  Don't
1018          *  tick game.clock1 unless well into cave (and not at Y2). */
1019         if (game.closed) {
1020             if (game.prop[OYSTER] < 0 && TOTING(OYSTER))
1021                 pspeak(OYSTER, look, 1);
1022             for (size_t i = 1; i <= NOBJECTS; i++) {
1023                 if (TOTING(i) && game.prop[i] < 0)
1024                     game.prop[i] = -1 - game.prop[i];
1025             }
1026         }
1027         game.wzdark = DARK(game.loc);
1028         if (game.knfloc > 0 && game.knfloc != game.loc)
1029             game.knfloc = 0;
1030
1031         /* This is where we get a new command from the user */
1032         if (!GETIN(cmdin, &command.wd1, &command.wd1x, &command.wd2, &command.wd2x))
1033             return false;
1034
1035         /*  Every input, check "game.foobar" flag.  If zero, nothing's
1036          *  going on.  If pos, make neg.  If neg, he skipped a word,
1037          *  so make it zero. */
1038 L2607:
1039         game.foobar = (game.foobar > 0 ? -game.foobar : 0);
1040         ++game.turns;
1041
1042         /* If a turn threshold has been met, apply penalties and tell
1043          * the player about it. */
1044         for (int i = 0; i < NTHRESHOLDS; ++i)
1045           {
1046             if (game.turns == turn_thresholds[i].threshold + 1)
1047               {
1048                 game.trnluz += turn_thresholds[i].point_loss;
1049                 speak(turn_thresholds[i].message);
1050               }
1051           }
1052
1053         if (command.verb == SAY && command.wd2 > 0)
1054             command.verb = 0;
1055         if (command.verb == SAY) {
1056             command.part = transitive;
1057             goto Laction;
1058         }
1059         if (closecheck()) {
1060             if (game.closed)
1061                 return true;
1062         } else
1063             lampcheck();
1064
1065         V1 = vocab(command.wd1, -1);
1066         V2 = vocab(command.wd2, -1);
1067         if (V1 == ENTER && (V2 == STREAM || V2 == 1000 + WATER)) {
1068             if (LIQLOC(game.loc) == WATER) {
1069                 rspeak(FEET_WET);
1070             } else {
1071                 rspeak(WHERE_QUERY);
1072             }
1073             goto L2012;
1074         }
1075         if (V1 == ENTER && command.wd2 > 0) {
1076             command.wd1 = command.wd2;
1077             command.wd1x = command.wd2x;
1078             wordclear(&command.wd2);
1079         } else {
1080             /* FIXME: Magic numbers */
1081             if (!((V1 != 1000 + WATER && V1 != 1000 + OIL) ||
1082                   (V2 != 1000 + PLANT && V2 != 1000 + DOOR))) {
1083                 if (AT(V2 - 1000))
1084                     command.wd2 = MAKEWD(WORD_POUR);
1085             }
1086             if (V1 == 1000 + CAGE && V2 == 1000 + BIRD && HERE(CAGE) && HERE(BIRD))
1087                 command.wd1 = MAKEWD(WORD_CATCH);
1088         }
1089 L2620:
1090         if (wordeq(command.wd1, MAKEWD(WORD_WEST))) {
1091             ++game.iwest;
1092             if (game.iwest == 10)
1093                 rspeak(W_IS_WEST);
1094         }
1095         if (wordeq(command.wd1, MAKEWD(WORD_GO)) && !wordempty(command.wd2)) {
1096             if (++igo == 10)
1097                 rspeak(GO_UNNEEDED);
1098         }
1099 Lookup:
1100         defn = vocab(command.wd1, -1);
1101         if (defn == -1) {
1102             /* Gee, I don't understand. */
1103             if (fallback_handler(rawbuf))
1104                 continue;
1105             rspeak(DONT_KNOW, command.wd1, command.wd1x);
1106             goto L2600;
1107         }
1108         kmod = MOD(defn, 1000);
1109         switch (defn / 1000) {
1110         case 0:
1111             if (playermove(command.verb, kmod))
1112                 return true;
1113             else
1114                 continue;       /* back to top of main interpreter loop */
1115         case 1:
1116             command.part = unknown;
1117             command.obj = kmod;
1118             break;
1119         case 2:
1120             command.part = intransitive;
1121             command.verb = kmod;
1122             break;
1123         case 3:
1124             rspeak(kmod);
1125             goto L2012;
1126         default:
1127             BUG(VOCABULARY_TYPE_N_OVER_1000_NOT_BETWEEN_0_AND_3);
1128         }
1129
1130 Laction:
1131         switch (action(cmdin, &command)) {
1132         case GO_TERMINATE:
1133             return true;
1134         case GO_MOVE:
1135             playermove(command.verb, NUL);
1136             return true;
1137         case GO_TOP:
1138             continue;   /* back to top of main interpreter loop */
1139         case GO_CLEAROBJ:
1140             goto L2012;
1141         case GO_CHECKHINT:
1142             goto L2600;
1143         case GO_CHECKFOO:
1144             goto L2607;
1145         case GO_LOOKUP:
1146             goto Lookup;
1147         case GO_WORD2:
1148             /* Get second word for analysis. */
1149             command.wd1 = command.wd2;
1150             command.wd1x = command.wd2x;
1151             wordclear(&command.wd2);
1152             goto L2620;
1153         case GO_UNKNOWN:
1154             /*  Random intransitive verbs come here.  Clear obj just in case
1155              *  (see attack()). */
1156             rspeak(DO_WHAT, command.wd1, command.wd1x);
1157             command.obj = 0;
1158             goto L2600;
1159         case GO_DWARFWAKE:
1160             /*  Oh dear, he's disturbed the dwarves. */
1161             rspeak(DWARVES_AWAKEN);
1162             terminate(endgame);
1163         default:
1164             BUG(ACTION_RETURNED_PHASE_CODE_BEYOND_END_OF_SWITCH);
1165         }
1166     }
1167 }
1168
1169 /* end */