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