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