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