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