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