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