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