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