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