Replace SETPRM/[PR]SPEAK with variadic [pr]speak
[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 (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                 rspeak(HINT_COST, hints[hint-1].penalty, hints[hint-1].penalty);
269                 game.hinted[hint] = YES(arbitrary_messages[WANT_HINT], hints[hint-1].hint, arbitrary_messages[OK_MAN]);
270                 if (game.hinted[hint] && game.limit > WARNTIME)
271                     game.limit += WARNTIME * hints[hint-1].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] == 1) {
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, 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, 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, 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] = 0;
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, 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] = 0;
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, 1);
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] == 1)
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] == 0 && HERE(LAMP)) {
852         rspeak(REPLACE_BATTERIES);
853         game.prop[BATTERY] = 1;
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] = 0;
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] == 1)spk = MISSING_BATTERYIES;
869             rspeak(spk);
870         }
871     }
872 }
873
874 static void listobjects(void)
875 /*  Print out descriptions of objects at this location.  If
876  *  not closing and property value is negative, tally off
877  *  another treasure.  Rug is special case; once seen, its
878  *  game.prop is 1 (dragon on it) till dragon is killed.
879  *  Similarly for chain; game.prop is initially 1 (locked to
880  *  bear).  These hacks are because game.prop=0 is needed to
881  *  get full score. */
882 {
883     if (!DARK(game.loc)) {
884         ++game.abbrev[game.loc];
885         for (int i = game.atloc[game.loc]; i != 0; i = game.link[i]) {
886             long obj = i;
887             if (obj > NOBJECTS)obj = obj - NOBJECTS;
888             if (obj == STEPS && TOTING(NUGGET))
889                 continue;
890             if (game.prop[obj] < 0) {
891                 if (game.closed)
892                     continue;
893                 game.prop[obj] = 0;
894                 if (obj == RUG || obj == CHAIN)
895                     game.prop[obj] = 1;
896                 --game.tally;
897                 /*  Note: There used to be a test here to see whether the
898                  *  player had blown it so badly that he could never ever see
899                  *  the remaining treasures, and if so the lamp was zapped to
900                  *  35 turns.  But the tests were too simple-minded; things
901                  *  like killing the bird before the snake was gone (can never
902                  *  see jewelry), and doing it "right" was hopeless.  E.G.,
903                  *  could cross troll bridge several times, using up all
904                  *  available treasures, breaking vase, using coins to buy
905                  *  batteries, etc., and eventually never be able to get
906                  *  across again.  If bottle were left on far side, could then
907                  *  never get eggs or trident, and the effects propagate.  So
908                  *  the whole thing was flushed.  anyone who makes such a
909                  *  gross blunder isn't likely to find everything else anyway
910                  *  (so goes the rationalisation). */
911             }
912             int kk = game.prop[obj];
913             if (obj == STEPS && game.loc == game.fixed[STEPS])
914                 kk = 1;
915             pspeak(obj, kk);
916         }
917     }
918 }
919
920 static bool do_command(FILE *cmdin)
921 /* Get and execute a command */
922 {
923     long V1, V2;
924     long kmod, defn;
925     static long igo = 0;
926     static struct command_t command;
927     command.verb = 0;
928
929     /*  Can't leave cave once it's closing (except by main office). */
930     if (OUTSID(game.newloc) && game.newloc != 0 && game.closng) {
931         rspeak(EXIT_CLOSED);
932         game.newloc = game.loc;
933         if (!game.panic)game.clock2 = PANICTIME;
934         game.panic = true;
935     }
936
937     /*  See if a dwarf has seen him and has come from where he
938      *  wants to go.  If so, the dwarf's blocking his way.  If
939      *  coming from place forbidden to pirate (dwarves rooted in
940      *  place) let him get out (and attacked). */
941     if (game.newloc != game.loc && !FORCED(game.loc) && !CNDBIT(game.loc, NOARRR)) {
942         for (size_t i = 1; i <= NDWARVES - 1; i++) {
943             if (game.odloc[i] == game.newloc && game.dseen[i]) {
944                 game.newloc = game.loc;
945                 rspeak(DWARF_BLOCK);
946                 break;
947             }
948         }
949     }
950     game.loc = game.newloc;
951
952     if (!dwarfmove())
953         croak();
954
955     /*  Describe the current location and (maybe) get next command. */
956
957     for (;;) {
958         if (game.loc == 0)
959             croak();
960         const char* msg = locations[game.loc].description.small;
961         if (MOD(game.abbrev[game.loc], game.abbnum) == 0 || msg == 0)
962             msg = locations[game.loc].description.big;
963         if (!FORCED(game.loc) && DARK(game.loc)) {
964             /*  The easiest way to get killed is to fall into a pit in
965              *  pitch darkness. */
966             if (game.wzdark && PCT(35)) {
967                 rspeak(PIT_FALL);
968                 game.oldlc2 = game.loc;
969                 croak();
970                 continue;       /* back to top of main interpreter loop */
971             }
972             msg = arbitrary_messages[PITCH_DARK];
973         }
974         if (TOTING(BEAR))rspeak(TAME_BEAR);
975         speak(msg);
976         if (FORCED(game.loc)) {
977             if (playermove(command.verb, 1))
978                 return true;
979             else
980                 continue;       /* back to top of main interpreter loop */
981         }
982         if (game.loc == LOC_Y2 && PCT(25) && !game.closng)
983             rspeak(SAYS_PLUGH);
984
985         listobjects();
986
987 L2012:
988         command.verb = 0;
989         game.oldobj = command.obj;
990         command.obj = 0;
991
992 L2600:
993         checkhints();
994
995         /*  If closing time, check for any objects being toted with
996          *  game.prop < 0 and set the prop to -1-game.prop.  This way
997          *  objects won't be described until they've been picked up
998          *  and put down separate from their respective piles.  Don't
999          *  tick game.clock1 unless well into cave (and not at Y2). */
1000         if (game.closed) {
1001             if (game.prop[OYSTER] < 0 && TOTING(OYSTER))
1002                 pspeak(OYSTER, 1);
1003             for (size_t i = 1; i <= NOBJECTS; i++) {
1004                 if (TOTING(i) && game.prop[i] < 0)
1005                     game.prop[i] = -1 - game.prop[i];
1006             }
1007         }
1008         game.wzdark = DARK(game.loc);
1009         if (game.knfloc > 0 && game.knfloc != game.loc)
1010             game.knfloc = 0;
1011
1012         /* This is where we get a new command from the user */
1013         if (!GETIN(cmdin, &command.wd1, &command.wd1x, &command.wd2, &command.wd2x))
1014             return false;
1015
1016         /*  Every input, check "game.foobar" flag.  If zero, nothing's
1017          *  going on.  If pos, make neg.  If neg, he skipped a word,
1018          *  so make it zero. */
1019 L2607:
1020         game.foobar = (game.foobar > 0 ? -game.foobar : 0);
1021         ++game.turns;
1022
1023         /* If a turn threshold has been met, apply penalties and tell
1024          * the player about it. */
1025         for (int i = 0; i < turn_threshold_count; ++i)
1026           {
1027             if (game.turns == turn_thresholds[i].threshold + 1)
1028               {
1029                 game.trnluz += turn_thresholds[i].point_loss;
1030                 speak(turn_thresholds[i].message);
1031               }
1032           }
1033
1034         if (command.verb == SAY && command.wd2 > 0)
1035             command.verb = 0;
1036         if (command.verb == SAY) {
1037             command.part = transitive;
1038             goto Laction;
1039         }
1040         if (closecheck()) {
1041             if (game.closed)
1042                 return true;
1043         } else
1044             lampcheck();
1045
1046         V1 = VOCAB(command.wd1, -1);
1047         V2 = VOCAB(command.wd2, -1);
1048         if (V1 == ENTER && (V2 == STREAM || V2 == 1000 + WATER)) {
1049             if (LIQLOC(game.loc) == WATER) {
1050                 rspeak(FEET_WET);
1051             } else {
1052                 rspeak(WHERE_QUERY);
1053             }
1054             goto L2012;
1055         }
1056         if (V1 == ENTER && command.wd2 > 0) {
1057             command.wd1 = command.wd2;
1058             command.wd1x = command.wd2x;
1059             wordclear(&command.wd2);
1060         } else {
1061             /* FIXME: Magic numbers */
1062             if (!((V1 != 1000 + WATER && V1 != 1000 + OIL) ||
1063                   (V2 != 1000 + PLANT && V2 != 1000 + DOOR))) {
1064                 if (AT(V2 - 1000))
1065                     command.wd2 = MAKEWD(WORD_POUR);
1066             }
1067             if (V1 == 1000 + CAGE && V2 == 1000 + BIRD && HERE(CAGE) && HERE(BIRD))
1068                 command.wd1 = MAKEWD(WORD_CATCH);
1069         }
1070 L2620:
1071         if (wordeq(command.wd1, MAKEWD(WORD_WEST))) {
1072             ++game.iwest;
1073             if (game.iwest == 10)
1074                 rspeak(W_IS_WEST);
1075         }
1076         if (wordeq(command.wd1, MAKEWD(WORD_GO)) && !wordempty(command.wd2)) {
1077             if (++igo == 10)
1078                 rspeak(GO_UNNEEDED);
1079         }
1080 Lookup:
1081         defn = VOCAB(command.wd1, -1);
1082         if (defn == -1) {
1083             /* Gee, I don't understand. */
1084             if (fallback_handler(rawbuf))
1085                 continue;
1086             rspeak(DONT_KNOW, command.wd1, command.wd1x);
1087             goto L2600;
1088         }
1089         kmod = MOD(defn, 1000);
1090         switch (defn / 1000) {
1091         case 0:
1092             if (playermove(command.verb, kmod))
1093                 return true;
1094             else
1095                 continue;       /* back to top of main interpreter loop */
1096         case 1:
1097             command.part = unknown;
1098             command.obj = kmod;
1099             break;
1100         case 2:
1101             command.part = intransitive;
1102             command.verb = kmod;
1103             break;
1104         case 3:
1105             rspeak(kmod);
1106             goto L2012;
1107         default:
1108             BUG(VOCABULARY_TYPE_N_OVER_1000_NOT_BETWEEN_0_AND_3);
1109         }
1110
1111 Laction:
1112         switch (action(cmdin, &command)) {
1113         case GO_TERMINATE:
1114             return true;
1115         case GO_MOVE:
1116             playermove(command.verb, NUL);
1117             return true;
1118         case GO_TOP:
1119             continue;   /* back to top of main interpreter loop */
1120         case GO_CLEAROBJ:
1121             goto L2012;
1122         case GO_CHECKHINT:
1123             goto L2600;
1124         case GO_CHECKFOO:
1125             goto L2607;
1126         case GO_LOOKUP:
1127             goto Lookup;
1128         case GO_WORD2:
1129             /* Get second word for analysis. */
1130             command.wd1 = command.wd2;
1131             command.wd1x = command.wd2x;
1132             wordclear(&command.wd2);
1133             goto L2620;
1134         case GO_UNKNOWN:
1135             /*  Random intransitive verbs come here.  Clear obj just in case
1136              *  (see attack()). */
1137             rspeak(DO_WHAT, command.wd1, command.wd1x);
1138             command.obj = 0;
1139             goto L2600;
1140         case GO_DWARFWAKE:
1141             /*  Oh dear, he's disturbed the dwarves. */
1142             rspeak(DWARVES_AWAKEN);
1143             terminate(endgame);
1144         default:
1145             BUG(ACTION_RETURNED_PHASE_CODE_BEYOND_END_OF_SWITCH);
1146         }
1147     }
1148 }
1149
1150 /* end */